From a4ef32abc34819bcf3f323caa1a059df00756610 Mon Sep 17 00:00:00 2001 From: Serhii Vecherenko Date: Mon, 14 Sep 2026 14:14:15 -0700 Subject: [PATCH] refactor: keep the GPUI forks as patches, not as vendored trees `[patch.crates-io]` pointed at five in-tree copies of published packages -- 207 files and roughly 153,000 lines, of which +1,644/-397 were ours. The copies were a derived artifact: pinned published source plus the patches this repository already generates and verifies. Keep only the smaller half. `.shots/gpui_patches.py --materialize` now writes the pinned published packages into the gitignored `.vendor/` tree and applies each patch exactly, and `[patch.crates-io]` names those paths. `--check` no longer compares an in-tree fork against the registry; it proves each patch applies to the published source with no fuzz and reproduces `.vendor/` byte for byte, so the patch stays the complete statement of the deviation. Cargo resolves `[patch]` paths at manifest load, before any build script, so there is no hook that can run earlier: a fresh clone must materialize before its first cargo command, rust-analyzer included, or cargo aborts with `failed to load source for dependency`. Every CI job that touches cargo runs the step first, through `.github/actions/rust-env` or its own copy. A warm run is a hash comparison that finishes in under a second. Two things this had to keep working, both verified rather than assumed. The `gpui-pre-web` fork's `default = []` is what keeps `wasm_thread` out of the wasm32 graph and the wasm job on stable; it now reaches the build as a hunk in `docs/upstream/patches/gpui-pre-web-0.3.3.patch`, and `cargo tree -i wasm_thread` still reports no such package. And the patches are build inputs on all three hosts now, so `*.patch text eol=lf` pins them -- `* text=auto` would have handed the Windows runner CRLF copies that `patch` would reject. No git dependency is introduced anywhere, so `cargo publish` is unaffected. Co-Authored-By: Claude Opus 5 (1M context) --- .gitattributes | 5 + .github/actions/rust-env/action.yml | 27 + .github/workflows/ci.yml | 55 +- .github/workflows/release.yml | 22 + .gitignore | 9 +- .shots/fence_audit.py | 22 +- .shots/gpui_patches.py | 392 +- .shots/lint.ps1 | 7 +- .shots/package_audit.py | 57 +- .shots/test_interaction_inventory.py | 2 +- AGENTS.md | 60 +- Cargo.toml | 63 +- README.md | 9 + RELEASING.md | 39 +- crates/gpui_pre/Cargo.lock | 5112 ---------- crates/gpui_pre/Cargo.toml | 670 -- crates/gpui_pre/LICENSE-APACHE | 222 - crates/gpui_pre/README.md | 98 - crates/gpui_pre/build.rs | 23 - crates/gpui_pre/docs/contexts.md | 33 - crates/gpui_pre/docs/key_dispatch.md | 100 - crates/gpui_pre/examples/README.md | 72 - crates/gpui_pre/examples/a11y.rs | 272 - crates/gpui_pre/examples/active_state_bug.rs | 47 - crates/gpui_pre/examples/anchor.rs | 207 - crates/gpui_pre/examples/animation.rs | 373 - crates/gpui_pre/examples/data_table.rs | 494 - crates/gpui_pre/examples/drag_drop.rs | 158 - .../examples/example_support/fonts.rs | 44 - crates/gpui_pre/examples/focus_visible.rs | 235 - crates/gpui_pre/examples/gif_viewer.rs | 62 - crates/gpui_pre/examples/gradient.rs | 278 - crates/gpui_pre/examples/grid_layout.rs | 95 - crates/gpui_pre/examples/hello_world.rs | 127 - crates/gpui_pre/examples/image/app-icon.png | Bin 168173 -> 0 bytes .../gpui_pre/examples/image/arrow_circle.svg | 6 - .../examples/image/black-cat-typing.gif | Bin 4471092 -> 0 bytes crates/gpui_pre/examples/image/color.svg | 13 - .../image/exif-orientation-rotate-180.jpg | Bin 378 -> 0 bytes crates/gpui_pre/examples/image/image.rs | 223 - crates/gpui_pre/examples/image_gallery.rs | 313 - crates/gpui_pre/examples/image_loading.rs | 232 - crates/gpui_pre/examples/input.rs | 784 -- crates/gpui_pre/examples/layer_shell.rs | 101 - crates/gpui_pre/examples/list_example.rs | 176 - crates/gpui_pre/examples/mouse_pressure.rs | 87 - .../examples/move_entity_between_windows.rs | 160 - .../gpui_pre/examples/on_window_close_quit.rs | 103 - crates/gpui_pre/examples/opacity.rs | 195 - crates/gpui_pre/examples/ownership_post.rs | 56 - crates/gpui_pre/examples/painting.rs | 478 - crates/gpui_pre/examples/paths_bench.rs | 113 - crates/gpui_pre/examples/pattern.rs | 136 - crates/gpui_pre/examples/popover.rs | 197 - crates/gpui_pre/examples/scrollable.rs | 78 - crates/gpui_pre/examples/set_menus.rs | 134 - crates/gpui_pre/examples/shadow.rs | 622 -- crates/gpui_pre/examples/svg/dragon.svg | 240 - crates/gpui_pre/examples/svg/svg.rs | 108 - .../gpui_pre/examples/system_notifications.rs | 155 - crates/gpui_pre/examples/tab_stop.rs | 220 - crates/gpui_pre/examples/testing.rs | 559 -- crates/gpui_pre/examples/text.rs | 415 - crates/gpui_pre/examples/text_layout.rs | 117 - crates/gpui_pre/examples/text_wrapper.rs | 144 - crates/gpui_pre/examples/tree.rs | 64 - crates/gpui_pre/examples/uniform_list.rs | 71 - .../examples/view_example/example_editor.rs | 549 -- .../examples/view_example/example_input.rs | 121 - .../examples/view_example/example_tests.rs | 131 - .../view_example/example_text_area.rs | 118 - .../view_example/view_example_main.rs | 179 - crates/gpui_pre/examples/window.rs | 355 - crates/gpui_pre/examples/window_movable.rs | 131 - .../gpui_pre/examples/window_positioning.rs | 240 - crates/gpui_pre/examples/window_shadow.rs | 252 - .../resources/windows/gpui.manifest.xml | 33 - crates/gpui_pre/resources/windows/gpui.rc | 2 - crates/gpui_pre/src/_accessibility.rs | 295 - .../gpui_pre/src/_ownership_and_data_flow.rs | 140 - crates/gpui_pre/src/action.rs | 459 - crates/gpui_pre/src/app.rs | 3192 ------- crates/gpui_pre/src/app/async_context.rs | 537 -- crates/gpui_pre/src/app/bench_context.rs | 1283 --- crates/gpui_pre/src/app/context.rs | 883 -- crates/gpui_pre/src/app/entity_map.rs | 1278 --- .../gpui_pre/src/app/headless_app_context.rs | 284 - crates/gpui_pre/src/app/test_app.rs | 607 -- crates/gpui_pre/src/app/test_context.rs | 1337 --- .../gpui_pre/src/app/visual_test_context.rs | 484 - crates/gpui_pre/src/arena.rs | 396 - crates/gpui_pre/src/asset_cache.rs | 82 - crates/gpui_pre/src/assets.rs | 133 - crates/gpui_pre/src/bounds_tree.rs | 472 - crates/gpui_pre/src/clip.rs | 188 - crates/gpui_pre/src/color.rs | 1070 --- crates/gpui_pre/src/colors.rs | 122 - crates/gpui_pre/src/debug_overlay.rs | 429 - crates/gpui_pre/src/element.rs | 792 -- crates/gpui_pre/src/elements/anchored.rs | 398 - crates/gpui_pre/src/elements/animation.rs | 1028 -- crates/gpui_pre/src/elements/canvas.rs | 95 - .../gpui_pre/src/elements/container_query.rs | 126 - crates/gpui_pre/src/elements/deferred.rs | 206 - crates/gpui_pre/src/elements/div.rs | 5450 ----------- crates/gpui_pre/src/elements/image_cache.rs | 353 - crates/gpui_pre/src/elements/img.rs | 983 -- crates/gpui_pre/src/elements/list.rs | 2985 ------ crates/gpui_pre/src/elements/mod.rs | 27 - crates/gpui_pre/src/elements/surface.rs | 121 - crates/gpui_pre/src/elements/svg.rs | 303 - crates/gpui_pre/src/elements/text.rs | 1313 --- crates/gpui_pre/src/elements/uniform_list.rs | 868 -- crates/gpui_pre/src/executor.rs | 588 -- crates/gpui_pre/src/geometry.rs | 4008 -------- crates/gpui_pre/src/gestures.rs | 2211 ----- crates/gpui_pre/src/global.rs | 75 - crates/gpui_pre/src/gpui.rs | 354 - crates/gpui_pre/src/input.rs | 485 - crates/gpui_pre/src/inspector.rs | 254 - crates/gpui_pre/src/interactive.rs | 971 -- crates/gpui_pre/src/key_dispatch.rs | 1691 ---- crates/gpui_pre/src/keymap.rs | 927 -- crates/gpui_pre/src/keymap/binding.rs | 143 - crates/gpui_pre/src/keymap/context.rs | 891 -- crates/gpui_pre/src/path_builder.rs | 347 - crates/gpui_pre/src/platform.rs | 3113 ------ crates/gpui_pre/src/platform/app_menu.rs | 426 - crates/gpui_pre/src/platform/keyboard.rs | 41 - crates/gpui_pre/src/platform/keystroke.rs | 776 -- crates/gpui_pre/src/platform/layer_shell.rs | 83 - crates/gpui_pre/src/platform/popup.rs | 134 - .../src/platform/scap_screen_capture.rs | 325 - crates/gpui_pre/src/platform/test.rs | 12 - .../gpui_pre/src/platform/test/dispatcher.rs | 148 - crates/gpui_pre/src/platform/test/display.rs | 33 - crates/gpui_pre/src/platform/test/platform.rs | 676 -- crates/gpui_pre/src/platform/test/window.rs | 550 -- .../src/platform/threaded_dispatcher.rs | 720 -- crates/gpui_pre/src/platform/visual_test.rs | 264 - crates/gpui_pre/src/platform_scheduler.rs | 457 - crates/gpui_pre/src/prelude.rs | 9 - crates/gpui_pre/src/profiler.rs | 1582 ---- crates/gpui_pre/src/profiler/actions.rs | 213 - crates/gpui_pre/src/profiler/hang.rs | 1297 --- crates/gpui_pre/src/profiler/journal.rs | 2766 ------ crates/gpui_pre/src/queue.rs | 435 - crates/gpui_pre/src/scene.rs | 1022 -- crates/gpui_pre/src/shared_uri.rs | 25 - crates/gpui_pre/src/spring.rs | 830 -- crates/gpui_pre/src/style.rs | 1583 ---- crates/gpui_pre/src/styled.rs | 904 -- crates/gpui_pre/src/subscription.rs | 351 - crates/gpui_pre/src/svg_renderer.rs | 527 -- crates/gpui_pre/src/tab_stop.rs | 615 -- crates/gpui_pre/src/taffy.rs | 776 -- crates/gpui_pre/src/test.rs | 220 - crates/gpui_pre/src/text_system.rs | 1222 --- .../src/text_system/font_fallbacks.rs | 21 - .../gpui_pre/src/text_system/font_features.rs | 154 - crates/gpui_pre/src/text_system/line.rs | 1025 -- .../gpui_pre/src/text_system/line_layout.rs | 1140 --- .../gpui_pre/src/text_system/line_wrapper.rs | 1587 ---- crates/gpui_pre/src/util.rs | 252 - crates/gpui_pre/src/view.rs | 507 - crates/gpui_pre/src/window.rs | 8383 ----------------- crates/gpui_pre/src/window/a11y.rs | 888 -- crates/gpui_pre/src/window/a11y/debug.rs | 330 - crates/gpui_pre/src/window/prompts.rs | 232 - crates/gpui_pre/tests/action_macros.rs | 55 - crates/gpui_pre_apple/Cargo.lock | 4811 ---------- crates/gpui_pre_apple/Cargo.toml | 152 - crates/gpui_pre_apple/LICENSE-APACHE | 222 - crates/gpui_pre_apple/build.rs | 190 - crates/gpui_pre_apple/src/gpui_apple.rs | 8 - crates/gpui_pre_apple/src/metal_atlas.rs | 379 - crates/gpui_pre_apple/src/metal_renderer.rs | 1649 ---- crates/gpui_pre_apple/src/shaders.metal | 1384 --- crates/gpui_pre_apple/vendor/gpui/src/clip.rs | 188 - .../gpui_pre_apple/vendor/gpui/src/color.rs | 1070 --- .../vendor/gpui/src/geometry.rs | 4008 -------- .../vendor/gpui/src/platform.rs | 3113 ------ .../gpui_pre_apple/vendor/gpui/src/scene.rs | 1022 -- .../gpui_pre_apple/vendor/gpui/src/window.rs | 8373 ---------------- crates/gpui_pre_wgpu/Cargo.lock | 4715 --------- crates/gpui_pre_wgpu/Cargo.toml | 150 - crates/gpui_pre_wgpu/LICENSE-APACHE | 222 - crates/gpui_pre_wgpu/benches/layout_line.rs | 104 - .../gpui_pre_wgpu/src/cosmic_text_system.rs | 1454 --- crates/gpui_pre_wgpu/src/gpui_wgpu.rs | 11 - crates/gpui_pre_wgpu/src/shaders.rs | 54 - crates/gpui_pre_wgpu/src/shaders.wgsl | 1515 --- crates/gpui_pre_wgpu/src/shaders_storage.wgsl | 48 - .../gpui_pre_wgpu/src/shaders_subpixel.wgsl | 69 - crates/gpui_pre_wgpu/src/shaders_webgl.wgsl | 265 - crates/gpui_pre_wgpu/src/wgpu_atlas.rs | 527 -- crates/gpui_pre_wgpu/src/wgpu_context.rs | 606 -- crates/gpui_pre_wgpu/src/wgpu_renderer.rs | 2261 ----- crates/gpui_pre_windows/Cargo.lock | 4881 ---------- crates/gpui_pre_windows/Cargo.toml | 238 - crates/gpui_pre_windows/LICENSE-APACHE | 222 - crates/gpui_pre_windows/build.rs | 242 - .../src/alpha_correction.hlsl | 49 - crates/gpui_pre_windows/src/clipboard.rs | 388 - .../src/color_text_raster.hlsl | 44 - .../gpui_pre_windows/src/destination_list.rs | 207 - .../src/direct_manipulation.rs | 359 - crates/gpui_pre_windows/src/direct_write.rs | 2161 ----- crates/gpui_pre_windows/src/directx_atlas.rs | 420 - .../gpui_pre_windows/src/directx_devices.rs | 194 - .../gpui_pre_windows/src/directx_renderer.rs | 2102 ----- crates/gpui_pre_windows/src/dispatcher.rs | 191 - crates/gpui_pre_windows/src/display.rs | 210 - crates/gpui_pre_windows/src/events.rs | 1773 ---- crates/gpui_pre_windows/src/gpui_windows.rs | 42 - crates/gpui_pre_windows/src/keyboard.rs | 371 - crates/gpui_pre_windows/src/platform.rs | 1605 ---- crates/gpui_pre_windows/src/shaders.hlsl | 1379 --- .../src/system_notifications.rs | 198 - .../gpui_pre_windows/src/system_settings.rs | 87 - crates/gpui_pre_windows/src/util.rs | 191 - crates/gpui_pre_windows/src/vsync.rs | 81 - crates/gpui_pre_windows/src/window.rs | 1687 ---- crates/gpui_pre_windows/src/wrapper.rs | 53 - crates/gpui_web/Cargo.toml | 203 - crates/gpui_web/LICENSE-APACHE | 222 - crates/gpui_web/src/dispatcher.rs | 435 - crates/gpui_web/src/display.rs | 101 - crates/gpui_web/src/events.rs | 1494 --- crates/gpui_web/src/gpui_web.rs | 25 - crates/gpui_web/src/http_client.rs | 309 - crates/gpui_web/src/ime_mirror.rs | 605 -- crates/gpui_web/src/keyboard.rs | 13 - crates/gpui_web/src/logging.rs | 45 - crates/gpui_web/src/platform.rs | 812 -- crates/gpui_web/src/window.rs | 878 -- .../tests/rounded_clip_shaders.rs | 11 +- deny.toml | 3 +- docs/agents/workflow.md | 12 +- docs/parity/coverage-report.json | 4 +- docs/parity/coverage-report.md | 2 +- docs/parity/interaction-inventory.json | 213 +- docs/parity/ui-design-plan.md | 11 +- docs/upstream/gpui-rounded-content-mask.md | 82 +- docs/upstream/gpui-web-scroll-and-ime.md | 47 +- .../upstream/patches/gpui-pre-web-0.3.3.patch | 328 + web/DEPLOYMENT.md | 8 +- 247 files changed, 1099 insertions(+), 158984 deletions(-) delete mode 100644 crates/gpui_pre/Cargo.lock delete mode 100644 crates/gpui_pre/Cargo.toml delete mode 100644 crates/gpui_pre/LICENSE-APACHE delete mode 100644 crates/gpui_pre/README.md delete mode 100644 crates/gpui_pre/build.rs delete mode 100644 crates/gpui_pre/docs/contexts.md delete mode 100644 crates/gpui_pre/docs/key_dispatch.md delete mode 100644 crates/gpui_pre/examples/README.md delete mode 100644 crates/gpui_pre/examples/a11y.rs delete mode 100644 crates/gpui_pre/examples/active_state_bug.rs delete mode 100644 crates/gpui_pre/examples/anchor.rs delete mode 100644 crates/gpui_pre/examples/animation.rs delete mode 100644 crates/gpui_pre/examples/data_table.rs delete mode 100644 crates/gpui_pre/examples/drag_drop.rs delete mode 100644 crates/gpui_pre/examples/example_support/fonts.rs delete mode 100644 crates/gpui_pre/examples/focus_visible.rs delete mode 100644 crates/gpui_pre/examples/gif_viewer.rs delete mode 100644 crates/gpui_pre/examples/gradient.rs delete mode 100644 crates/gpui_pre/examples/grid_layout.rs delete mode 100644 crates/gpui_pre/examples/hello_world.rs delete mode 100644 crates/gpui_pre/examples/image/app-icon.png delete mode 100644 crates/gpui_pre/examples/image/arrow_circle.svg delete mode 100644 crates/gpui_pre/examples/image/black-cat-typing.gif delete mode 100644 crates/gpui_pre/examples/image/color.svg delete mode 100644 crates/gpui_pre/examples/image/exif-orientation-rotate-180.jpg delete mode 100644 crates/gpui_pre/examples/image/image.rs delete mode 100644 crates/gpui_pre/examples/image_gallery.rs delete mode 100644 crates/gpui_pre/examples/image_loading.rs delete mode 100644 crates/gpui_pre/examples/input.rs delete mode 100644 crates/gpui_pre/examples/layer_shell.rs delete mode 100644 crates/gpui_pre/examples/list_example.rs delete mode 100644 crates/gpui_pre/examples/mouse_pressure.rs delete mode 100644 crates/gpui_pre/examples/move_entity_between_windows.rs delete mode 100644 crates/gpui_pre/examples/on_window_close_quit.rs delete mode 100644 crates/gpui_pre/examples/opacity.rs delete mode 100644 crates/gpui_pre/examples/ownership_post.rs delete mode 100644 crates/gpui_pre/examples/painting.rs delete mode 100644 crates/gpui_pre/examples/paths_bench.rs delete mode 100644 crates/gpui_pre/examples/pattern.rs delete mode 100644 crates/gpui_pre/examples/popover.rs delete mode 100644 crates/gpui_pre/examples/scrollable.rs delete mode 100644 crates/gpui_pre/examples/set_menus.rs delete mode 100644 crates/gpui_pre/examples/shadow.rs delete mode 100644 crates/gpui_pre/examples/svg/dragon.svg delete mode 100644 crates/gpui_pre/examples/svg/svg.rs delete mode 100644 crates/gpui_pre/examples/system_notifications.rs delete mode 100644 crates/gpui_pre/examples/tab_stop.rs delete mode 100644 crates/gpui_pre/examples/testing.rs delete mode 100644 crates/gpui_pre/examples/text.rs delete mode 100644 crates/gpui_pre/examples/text_layout.rs delete mode 100644 crates/gpui_pre/examples/text_wrapper.rs delete mode 100644 crates/gpui_pre/examples/tree.rs delete mode 100644 crates/gpui_pre/examples/uniform_list.rs delete mode 100644 crates/gpui_pre/examples/view_example/example_editor.rs delete mode 100644 crates/gpui_pre/examples/view_example/example_input.rs delete mode 100644 crates/gpui_pre/examples/view_example/example_tests.rs delete mode 100644 crates/gpui_pre/examples/view_example/example_text_area.rs delete mode 100644 crates/gpui_pre/examples/view_example/view_example_main.rs delete mode 100644 crates/gpui_pre/examples/window.rs delete mode 100644 crates/gpui_pre/examples/window_movable.rs delete mode 100644 crates/gpui_pre/examples/window_positioning.rs delete mode 100644 crates/gpui_pre/examples/window_shadow.rs delete mode 100644 crates/gpui_pre/resources/windows/gpui.manifest.xml delete mode 100644 crates/gpui_pre/resources/windows/gpui.rc delete mode 100644 crates/gpui_pre/src/_accessibility.rs delete mode 100644 crates/gpui_pre/src/_ownership_and_data_flow.rs delete mode 100644 crates/gpui_pre/src/action.rs delete mode 100644 crates/gpui_pre/src/app.rs delete mode 100644 crates/gpui_pre/src/app/async_context.rs delete mode 100644 crates/gpui_pre/src/app/bench_context.rs delete mode 100644 crates/gpui_pre/src/app/context.rs delete mode 100644 crates/gpui_pre/src/app/entity_map.rs delete mode 100644 crates/gpui_pre/src/app/headless_app_context.rs delete mode 100644 crates/gpui_pre/src/app/test_app.rs delete mode 100644 crates/gpui_pre/src/app/test_context.rs delete mode 100644 crates/gpui_pre/src/app/visual_test_context.rs delete mode 100644 crates/gpui_pre/src/arena.rs delete mode 100644 crates/gpui_pre/src/asset_cache.rs delete mode 100644 crates/gpui_pre/src/assets.rs delete mode 100644 crates/gpui_pre/src/bounds_tree.rs delete mode 100644 crates/gpui_pre/src/clip.rs delete mode 100644 crates/gpui_pre/src/color.rs delete mode 100644 crates/gpui_pre/src/colors.rs delete mode 100644 crates/gpui_pre/src/debug_overlay.rs delete mode 100644 crates/gpui_pre/src/element.rs delete mode 100644 crates/gpui_pre/src/elements/anchored.rs delete mode 100644 crates/gpui_pre/src/elements/animation.rs delete mode 100644 crates/gpui_pre/src/elements/canvas.rs delete mode 100644 crates/gpui_pre/src/elements/container_query.rs delete mode 100644 crates/gpui_pre/src/elements/deferred.rs delete mode 100644 crates/gpui_pre/src/elements/div.rs delete mode 100644 crates/gpui_pre/src/elements/image_cache.rs delete mode 100644 crates/gpui_pre/src/elements/img.rs delete mode 100644 crates/gpui_pre/src/elements/list.rs delete mode 100644 crates/gpui_pre/src/elements/mod.rs delete mode 100644 crates/gpui_pre/src/elements/surface.rs delete mode 100644 crates/gpui_pre/src/elements/svg.rs delete mode 100644 crates/gpui_pre/src/elements/text.rs delete mode 100644 crates/gpui_pre/src/elements/uniform_list.rs delete mode 100644 crates/gpui_pre/src/executor.rs delete mode 100644 crates/gpui_pre/src/geometry.rs delete mode 100644 crates/gpui_pre/src/gestures.rs delete mode 100644 crates/gpui_pre/src/global.rs delete mode 100644 crates/gpui_pre/src/gpui.rs delete mode 100644 crates/gpui_pre/src/input.rs delete mode 100644 crates/gpui_pre/src/inspector.rs delete mode 100644 crates/gpui_pre/src/interactive.rs delete mode 100644 crates/gpui_pre/src/key_dispatch.rs delete mode 100644 crates/gpui_pre/src/keymap.rs delete mode 100644 crates/gpui_pre/src/keymap/binding.rs delete mode 100644 crates/gpui_pre/src/keymap/context.rs delete mode 100644 crates/gpui_pre/src/path_builder.rs delete mode 100644 crates/gpui_pre/src/platform.rs delete mode 100644 crates/gpui_pre/src/platform/app_menu.rs delete mode 100644 crates/gpui_pre/src/platform/keyboard.rs delete mode 100644 crates/gpui_pre/src/platform/keystroke.rs delete mode 100644 crates/gpui_pre/src/platform/layer_shell.rs delete mode 100644 crates/gpui_pre/src/platform/popup.rs delete mode 100644 crates/gpui_pre/src/platform/scap_screen_capture.rs delete mode 100644 crates/gpui_pre/src/platform/test.rs delete mode 100644 crates/gpui_pre/src/platform/test/dispatcher.rs delete mode 100644 crates/gpui_pre/src/platform/test/display.rs delete mode 100644 crates/gpui_pre/src/platform/test/platform.rs delete mode 100644 crates/gpui_pre/src/platform/test/window.rs delete mode 100644 crates/gpui_pre/src/platform/threaded_dispatcher.rs delete mode 100644 crates/gpui_pre/src/platform/visual_test.rs delete mode 100644 crates/gpui_pre/src/platform_scheduler.rs delete mode 100644 crates/gpui_pre/src/prelude.rs delete mode 100644 crates/gpui_pre/src/profiler.rs delete mode 100644 crates/gpui_pre/src/profiler/actions.rs delete mode 100644 crates/gpui_pre/src/profiler/hang.rs delete mode 100644 crates/gpui_pre/src/profiler/journal.rs delete mode 100644 crates/gpui_pre/src/queue.rs delete mode 100644 crates/gpui_pre/src/scene.rs delete mode 100644 crates/gpui_pre/src/shared_uri.rs delete mode 100644 crates/gpui_pre/src/spring.rs delete mode 100644 crates/gpui_pre/src/style.rs delete mode 100644 crates/gpui_pre/src/styled.rs delete mode 100644 crates/gpui_pre/src/subscription.rs delete mode 100644 crates/gpui_pre/src/svg_renderer.rs delete mode 100644 crates/gpui_pre/src/tab_stop.rs delete mode 100644 crates/gpui_pre/src/taffy.rs delete mode 100644 crates/gpui_pre/src/test.rs delete mode 100644 crates/gpui_pre/src/text_system.rs delete mode 100644 crates/gpui_pre/src/text_system/font_fallbacks.rs delete mode 100644 crates/gpui_pre/src/text_system/font_features.rs delete mode 100644 crates/gpui_pre/src/text_system/line.rs delete mode 100644 crates/gpui_pre/src/text_system/line_layout.rs delete mode 100644 crates/gpui_pre/src/text_system/line_wrapper.rs delete mode 100644 crates/gpui_pre/src/util.rs delete mode 100644 crates/gpui_pre/src/view.rs delete mode 100644 crates/gpui_pre/src/window.rs delete mode 100644 crates/gpui_pre/src/window/a11y.rs delete mode 100644 crates/gpui_pre/src/window/a11y/debug.rs delete mode 100644 crates/gpui_pre/src/window/prompts.rs delete mode 100644 crates/gpui_pre/tests/action_macros.rs delete mode 100644 crates/gpui_pre_apple/Cargo.lock delete mode 100644 crates/gpui_pre_apple/Cargo.toml delete mode 100644 crates/gpui_pre_apple/LICENSE-APACHE delete mode 100644 crates/gpui_pre_apple/build.rs delete mode 100644 crates/gpui_pre_apple/src/gpui_apple.rs delete mode 100644 crates/gpui_pre_apple/src/metal_atlas.rs delete mode 100644 crates/gpui_pre_apple/src/metal_renderer.rs delete mode 100644 crates/gpui_pre_apple/src/shaders.metal delete mode 100644 crates/gpui_pre_apple/vendor/gpui/src/clip.rs delete mode 100644 crates/gpui_pre_apple/vendor/gpui/src/color.rs delete mode 100644 crates/gpui_pre_apple/vendor/gpui/src/geometry.rs delete mode 100644 crates/gpui_pre_apple/vendor/gpui/src/platform.rs delete mode 100644 crates/gpui_pre_apple/vendor/gpui/src/scene.rs delete mode 100644 crates/gpui_pre_apple/vendor/gpui/src/window.rs delete mode 100644 crates/gpui_pre_wgpu/Cargo.lock delete mode 100644 crates/gpui_pre_wgpu/Cargo.toml delete mode 100644 crates/gpui_pre_wgpu/LICENSE-APACHE delete mode 100644 crates/gpui_pre_wgpu/benches/layout_line.rs delete mode 100644 crates/gpui_pre_wgpu/src/cosmic_text_system.rs delete mode 100644 crates/gpui_pre_wgpu/src/gpui_wgpu.rs delete mode 100644 crates/gpui_pre_wgpu/src/shaders.rs delete mode 100644 crates/gpui_pre_wgpu/src/shaders.wgsl delete mode 100644 crates/gpui_pre_wgpu/src/shaders_storage.wgsl delete mode 100644 crates/gpui_pre_wgpu/src/shaders_subpixel.wgsl delete mode 100644 crates/gpui_pre_wgpu/src/shaders_webgl.wgsl delete mode 100644 crates/gpui_pre_wgpu/src/wgpu_atlas.rs delete mode 100644 crates/gpui_pre_wgpu/src/wgpu_context.rs delete mode 100644 crates/gpui_pre_wgpu/src/wgpu_renderer.rs delete mode 100644 crates/gpui_pre_windows/Cargo.lock delete mode 100644 crates/gpui_pre_windows/Cargo.toml delete mode 100644 crates/gpui_pre_windows/LICENSE-APACHE delete mode 100644 crates/gpui_pre_windows/build.rs delete mode 100644 crates/gpui_pre_windows/src/alpha_correction.hlsl delete mode 100644 crates/gpui_pre_windows/src/clipboard.rs delete mode 100644 crates/gpui_pre_windows/src/color_text_raster.hlsl delete mode 100644 crates/gpui_pre_windows/src/destination_list.rs delete mode 100644 crates/gpui_pre_windows/src/direct_manipulation.rs delete mode 100644 crates/gpui_pre_windows/src/direct_write.rs delete mode 100644 crates/gpui_pre_windows/src/directx_atlas.rs delete mode 100644 crates/gpui_pre_windows/src/directx_devices.rs delete mode 100644 crates/gpui_pre_windows/src/directx_renderer.rs delete mode 100644 crates/gpui_pre_windows/src/dispatcher.rs delete mode 100644 crates/gpui_pre_windows/src/display.rs delete mode 100644 crates/gpui_pre_windows/src/events.rs delete mode 100644 crates/gpui_pre_windows/src/gpui_windows.rs delete mode 100644 crates/gpui_pre_windows/src/keyboard.rs delete mode 100644 crates/gpui_pre_windows/src/platform.rs delete mode 100644 crates/gpui_pre_windows/src/shaders.hlsl delete mode 100644 crates/gpui_pre_windows/src/system_notifications.rs delete mode 100644 crates/gpui_pre_windows/src/system_settings.rs delete mode 100644 crates/gpui_pre_windows/src/util.rs delete mode 100644 crates/gpui_pre_windows/src/vsync.rs delete mode 100644 crates/gpui_pre_windows/src/window.rs delete mode 100644 crates/gpui_pre_windows/src/wrapper.rs delete mode 100644 crates/gpui_web/Cargo.toml delete mode 100644 crates/gpui_web/LICENSE-APACHE delete mode 100644 crates/gpui_web/src/dispatcher.rs delete mode 100644 crates/gpui_web/src/display.rs delete mode 100644 crates/gpui_web/src/events.rs delete mode 100644 crates/gpui_web/src/gpui_web.rs delete mode 100644 crates/gpui_web/src/http_client.rs delete mode 100644 crates/gpui_web/src/ime_mirror.rs delete mode 100644 crates/gpui_web/src/keyboard.rs delete mode 100644 crates/gpui_web/src/logging.rs delete mode 100644 crates/gpui_web/src/platform.rs delete mode 100644 crates/gpui_web/src/window.rs create mode 100644 docs/upstream/patches/gpui-pre-web-0.3.3.patch diff --git a/.gitattributes b/.gitattributes index f6cc4645..f9bac119 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,6 +8,11 @@ *.py text eol=lf *.sh text eol=lf *.md text eol=lf +# The GPUI fork patches are a build input now, not just an audit artifact: +# `.shots/gpui_patches.py --materialize` applies them before every cargo +# command on all three CI hosts. `* text=auto` above would hand the Windows +# runner CRLF copies, so pin them the way every other source file is pinned. +*.patch text eol=lf # Oxfmt checks LF endings, including on Windows checkouts. web/**/*.ts text eol=lf web/**/*.tsx text eol=lf diff --git a/.github/actions/rust-env/action.yml b/.github/actions/rust-env/action.yml index 9409e1ef..ed216849 100644 --- a/.github/actions/rust-env/action.yml +++ b/.github/actions/rust-env/action.yml @@ -19,6 +19,33 @@ inputs: runs: using: composite steps: + # The patched GPUI sources are not in the repository; five + # `[patch.crates-io]` entries point into the gitignored `.vendor/` tree that + # `.shots/gpui_patches.py --materialize` builds from the pinned published + # packages plus `docs/upstream/patches/*.patch`. Cargo fails at manifest + # load when a patch path is missing ("failed to load source for + # dependency"), so this has to happen before the first cargo command in the + # job -- which is why it lives in the shared action rather than in each + # job, where one omission would break a whole leg. + # + # It also has to happen before `Swatinem/rust-cache` below: that action runs + # `cargo metadata` of its own to key the cache, and that call is a cargo + # command like any other. The cost is that a cold job downloads the five + # `.crate` files before the registry cache is restored; they are small next + # to the dependency graph the cache is actually for, and a warm `.vendor/` + # check is a hash comparison that finishes in under a tenth of a second. + # + # `setup-python` rather than the runner's default interpreter: `python3` is + # not on PATH on the Windows runners, and pinning the version keeps the + # three OS legs on one interpreter. + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Materialize the patched GPUI sources + shell: bash + run: python .shots/gpui_patches.py --materialize + - name: Disable Windows Defender and configure exclusions # Windows-only. GitHub Actions Windows runners run Defender by default. # Scanning every .rlib, .rmeta, .exe, and cache tar extraction causes diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0b4a00a..6ba871db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,6 +48,19 @@ jobs: working-directory: web run: pnpm install --frozen-lockfile + # `cargo fmt --all` is a cargo command, so it needs the patched GPUI + # sources materialized like every other one -- and this job needs it for a + # second reason besides `[patch.crates-io]`: + # `crates/herogpui-components/tests/rounded_clip_shaders.rs` includes the + # renderer's own `shaders.rs` by `#[path]`, so rustfmt fails on a missing + # module before cargo ever resolves a dependency. + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Materialize the patched GPUI sources + run: python .shots/gpui_patches.py --materialize + - name: Format check run: cargo fmt --all -- --check @@ -146,15 +159,23 @@ jobs: with: tool: cargo-machete + # The patches are the only record of the fork, so this is the gate that + # keeps them honest: each one has to apply to the pristine published + # package with no fuzz and no offset, and reproduce the `.vendor/` tree + # the rest of this job just built against, byte for byte. A hand-edit + # under `.vendor/` that never made it back into a patch fails here. - name: Verify GPUI renderer patches - run: python .shots/gpui_patches.py --check + run: | + python .shots/gpui_patches.py --self-test + python .shots/gpui_patches.py --check # Unused-dependency gate. cargo-machete reads every manifest in the # tree and greps the crate sources for each dependency's name -- no # build, not even a toolchain -- so it answers in seconds and sits - # before the clippy step below, which pays for a full compile. The - # vendored `crates/gpui_web` fork is clean under it too (verified by - # running machete on that directory directly). + # before the clippy step below, which pays for a full compile. It walks + # the working directory rather than the dependency graph, and the + # materialized GPUI sources under `.vendor/` are hidden from it by the + # leading dot, so it sees only HeroGPUI's own manifests. # # There is no ignore list today, deliberately: the tree is clean with # none (verified against cargo-machete 0.9.2, what install-action @@ -164,7 +185,7 @@ jobs: # that crate's manifest, or the same table under # `[workspace.metadata.cargo-machete]` in the root. Every entry must # carry its reason in a comment beside it, the way - # `[package.metadata.cargo-shear]` does in crates/gpui_web; an entry + # `[package.metadata.cargo-shear]` does upstream; an entry # without a reason is a hole in the gate. - name: Unused dependencies (cargo-machete) run: cargo machete @@ -338,10 +359,12 @@ jobs: # channel # --> .../wasm_thread-0.3.3/src/lib.rs:1:1 # - # `multithreaded` is now off in the vendored fork's own manifest - # (`crates/gpui_web/Cargo.toml`, which `[patch.crates-io]` substitutes -- - # the only place it can be switched, because `gpui_platform`'s wasm32 - # edge enables default features and cargo unions feature sets). With it + # `multithreaded` is now off in the forked manifest itself -- a + # `default = []` hunk in `docs/upstream/patches/gpui-pre-web-0.3.3.patch`, + # materialized into `.vendor/gpui-pre-web-0.3.3/Cargo.toml`, which + # `[patch.crates-io]` substitutes. That is the only place it can be + # switched, because `gpui_platform`'s wasm32 edge enables default features + # and cargo unions feature sets. With it # off, `wasm_thread` is not in the graph and stable builds this target, # profile included. Nothing was using it: `crates/herogpui-web` starts the # app with `single_threaded_web()`, and the multi-threaded platform needs @@ -350,6 +373,20 @@ jobs: - name: Add the wasm32 target to the pinned toolchain run: rustup target add wasm32-unknown-unknown + # This job builds its own environment instead of using + # `./.github/actions/rust-env`, so it repeats that action's + # materialization step. `gpui-pre-web` is the package that matters most + # here: its `default = []` hunk is what keeps `wasm_thread` -- and with it + # a nightly toolchain -- out of the wasm32 graph, and it now lives only in + # `docs/upstream/patches/gpui-pre-web-0.3.3.patch`. Before + # `Swatinem/rust-cache`, which runs a `cargo metadata` of its own. + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Materialize the patched GPUI sources + run: python .shots/gpui_patches.py --materialize + - uses: Swatinem/rust-cache@v2 with: key: wasm32 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a98c989f..b908f205 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,6 +50,17 @@ jobs: libxcb-xfixes0-dev \ libfontconfig-dev \ libfreetype-dev + # The patched GPUI sources are not in the repository -- the five + # `[patch.crates-io]` paths under `.vendor/` are built from + # `docs/upstream/patches/*.patch` by this command, and cargo aborts at + # manifest load without them. Before `Swatinem/rust-cache`, which runs a + # `cargo metadata` of its own. + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Materialize the patched GPUI sources + shell: bash + run: python .shots/gpui_patches.py --materialize - uses: Swatinem/rust-cache@v2 - run: cargo test --workspace --locked @@ -93,6 +104,17 @@ jobs: libxcb-xfixes0-dev \ libfontconfig-dev \ libfreetype-dev + # The patched GPUI sources are not in the repository -- the five + # `[patch.crates-io]` paths under `.vendor/` are built from + # `docs/upstream/patches/*.patch` by this command, and cargo aborts at + # manifest load without them. Before `Swatinem/rust-cache`, which runs a + # `cargo metadata` of its own. + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Materialize the patched GPUI sources + shell: bash + run: python .shots/gpui_patches.py --materialize - uses: Swatinem/rust-cache@v2 - run: cargo build --locked --release -p herogpui-gallery - name: Stage release binary diff --git a/.gitignore b/.gitignore index 696db128..e18c2f6e 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,9 @@ web/.verify/ .vercel .env* -# Vendored gpui-pre forks are standalone workspaces and may be built in -# place; their target output is not source. -crates/gpui_pre*/target/ +# The patched GPUI sources. The repository records the fork as patches under +# docs/upstream/patches/; `.shots/gpui_patches.py --materialize` rebuilds this +# tree from the pinned published packages plus those patches, and every cargo +# command (and rust-analyzer) needs it to exist first. ~153,000 lines of +# dependency source that is not ours and is never committed. +/.vendor/ diff --git a/.shots/fence_audit.py b/.shots/fence_audit.py index c1e624ac..69eb0192 100644 --- a/.shots/fence_audit.py +++ b/.shots/fence_audit.py @@ -53,17 +53,6 @@ SCAN_ROOT = 'crates' -# These are exact-version gpui-pre source forks selected through the workspace -# `[patch.crates-io]` block. Their rustdoc belongs to the external dependency; -# the application fence contract is audited in HeroGPUI-owned crates below. -EXTERNAL_GPUI_DIRS = { - 'gpui_pre', - 'gpui_pre_apple', - 'gpui_pre_wgpu', - 'gpui_pre_windows', - 'gpui_web', -} - # (path relative to the repository root, why running it is not an option). # Every entry is asserted to still match a `no_run` fence in that file, so an # example that stops needing the exemption fails this audit until its entry @@ -128,12 +117,11 @@ def rust_files(root): """Every `.rs` file under `root`, sorted, skipping build output.""" found = [] for base, dirs, names in os.walk(root): - dirs[:] = [ - d for d in dirs - if d != 'target' - and not d.startswith('.') - and d not in EXTERNAL_GPUI_DIRS - ] + # The patched gpui-pre forks used to sit under `crates/` and had to be + # named here to keep an external dependency's rustdoc out of this + # contract. They are materialized into `.vendor/` now, which the + # dot-prefix rule already excludes -- and `SCAN_ROOT` does not reach it. + dirs[:] = [d for d in dirs if d != 'target' and not d.startswith('.')] for name in names: if name.endswith('.rs'): found.append(os.path.join(base, name).replace(os.sep, '/')) diff --git a/.shots/gpui_patches.py b/.shots/gpui_patches.py index 85835919..a70c87f8 100644 --- a/.shots/gpui_patches.py +++ b/.shots/gpui_patches.py @@ -1,8 +1,29 @@ #!/usr/bin/env python3 -"""Record and verify renderer forks against their exact published packages.""" +"""Materialize, record and verify the renderer forks of the published GPUI packages. + +The repository carries the *patches*, never the patched sources. Five published +`gpui-pre*` packages are forked (`docs/upstream/gpui-rounded-content-mask.md` +and `docs/upstream/gpui-web-scroll-and-ime.md` say why), and each fork is stored +as a single unified patch under `docs/upstream/patches/`. The build materializes +the patched tree into the gitignored `.vendor/` root, which the workspace's +`[patch.crates-io]` table points at. + + python3 .shots/gpui_patches.py --materialize # bootstrap: required before *any* cargo command + python3 .shots/gpui_patches.py --check # patch still reproduces `.vendor/` exactly + python3 .shots/gpui_patches.py --write # re-record a patch after editing `.vendor/` + python3 .shots/gpui_patches.py --self-test # the recorder's own regressions + +A missing `.vendor/` tree is not a soft failure: cargo aborts at manifest load +with "failed to load source for dependency" because a `[patch.crates-io]` path +does not exist. That is why `--materialize` runs first in every CI job that +touches cargo, and why a fresh clone must run it before `cargo`, `rustup` +component tooling, or rust-analyzer. +""" import argparse import difflib +import hashlib +import json import os from pathlib import Path import shutil @@ -11,11 +32,26 @@ import tomllib ROOT = Path(__file__).resolve().parents[1] -PACKAGES = ("gpui-pre", "gpui-pre-apple", "gpui-pre-wgpu", "gpui-pre-windows") +PATCH_DIR = ROOT / "docs/upstream/patches" +PACKAGES = ( + "gpui-pre", + "gpui-pre-apple", + "gpui-pre-web", + "gpui-pre-wgpu", + "gpui-pre-windows", +) +# Written into each materialized tree so a warm run can decide in milliseconds +# whether the tree is already the patch applied to the pinned published source. +STAMP = ".herogpui-materialized.json" # Cargo uses the workspace lockfile. These registry/cache files are not fork -# code: `Cargo.toml.orig` is the packager's pre-normalization backup and is -# gitignored, so it exists only in an unpacked registry copy, never in the fork. -IGNORED = {".cargo-checksum.json", ".cargo-ok", "Cargo.lock", "Cargo.toml.orig"} +# code: `Cargo.toml.orig` is the packager's pre-normalization backup and +# `.cargo-ok`/`.cargo-checksum.json` are cargo's own unpack bookkeeping, so +# none of them belongs in a materialized tree or in a recorded patch. +IGNORED = {".cargo-checksum.json", ".cargo-ok", "Cargo.lock", "Cargo.toml.orig", STAMP} + + +def digest(payload): + return hashlib.sha256(payload).hexdigest() def files(root): @@ -28,6 +64,15 @@ def files(root): } +def tree_digest(root): + return digest( + json.dumps( + {name: digest(content) for name, content in files(root).items()}, + sort_keys=True, + ).encode() + ) + + def patch_text(base, fork, package): original, changed = files(base), files(fork) output = [] @@ -46,6 +91,146 @@ def patch_text(base, fork, package): return "".join(output) +def parse_patch(body, package): + """Split a `patch_text` unified diff into `(path, deleted, created, hunks)` per file. + + Only the dialect `patch_text` emits is accepted -- no index lines, no + renames, no binary hunks, one `---`/`+++` pair per file. Anything else is a + hand-edited patch, which is exactly what this refuses to guess at. + """ + lines = body.splitlines(keepends=True) + files_, index = [], 0 + while index < len(lines): + line = lines[index] + if not line.startswith("--- ") or index + 1 >= len(lines): + raise ValueError(f"unexpected line {index + 1} in the patch for {package}: {line!r}") + source, target = line[4:].rstrip("\n"), lines[index + 1][4:].rstrip("\n") + if not lines[index + 1].startswith("+++ "): + raise ValueError(f"patch for {package} has a `---` line with no `+++` line") + index += 2 + deleted, created = target == "/dev/null", source == "/dev/null" + named = source if deleted else target + prefix = "a/" if deleted else "b/" + if not named.startswith(f"{prefix}{package}/") or deleted and created: + raise ValueError(f"patch for {package} names a foreign file: {named!r}") + path, hunks = named[len(prefix) + len(package) + 1:], [] + while index < len(lines) and lines[index].startswith("@@ "): + header = lines[index].split(" ") + old_start, old_count = hunk_range(header[1]) + _, new_count = hunk_range(header[2]) + index, body_lines = index + 1, [] + # The two counts say exactly how many body lines this hunk has, so + # the scan never has to guess where it ends -- a `-` line that opens + # the next file's `--- a/...` header cannot be mistaken for a removal. + seen_old = seen_new = 0 + while seen_old < old_count or seen_new < new_count: + if index >= len(lines): + raise ValueError(f"patch for {package} ends inside a hunk for {path}") + entry = lines[index] + index += 1 + if entry.startswith("\\"): + body_lines[-1] = strip_final_newline(body_lines[-1]) + continue + if entry == "\n": + entry = " \n" # an empty context line, if an editor ate the space + tag, text = entry[0], entry[1:] + seen_old += tag in " -" + seen_new += tag in " +" + if tag not in " -+": + raise ValueError(f"patch for {package} has an unreadable hunk line: {entry!r}") + body_lines.append((tag, text)) + # A `\ No newline at end of file` marker belongs to the line above + # it, whose recorded text carries a newline the file does not have. + if index < len(lines) and lines[index].startswith("\\"): + body_lines[-1] = strip_final_newline(body_lines[-1]) + index += 1 + hunks.append((old_start, old_count, body_lines)) + if not hunks: + raise ValueError(f"patch for {package} has no hunk for {path}") + files_.append((path, deleted, created, hunks)) + return files_ + + +def hunk_range(field): + """`-12,3` or `+12` into `(start, count)`; a bare number means one line.""" + start, _, count = field[1:].partition(",") + return int(start), int(count) if count else 1 + + +def strip_final_newline(entry): + tag, text = entry + return tag, text[:-1] if text.endswith("\n") else text + + +def apply_hunks(original, hunks, path): + """Replay `hunks` against `original`, requiring an exact positional match. + + There is no search and no fallback, so there is nothing for a fuzz factor or + a line offset to hide in: every context and removed line has to be the line + the patch says it is, or the whole application fails. + """ + result, cursor = [], 0 + for start, count, body in hunks: + # difflib writes a zero-length range as the line *before* the insertion + # point, so the 0-based index is `start` there and `start - 1` otherwise. + index = start if count == 0 else start - 1 + if index < cursor or index > len(original): + raise ValueError(f"hunk at line {start} of {path} is out of order or past the file end") + result.extend(original[cursor:index]) + cursor = index + for tag, text in body: + if tag == "+": + result.append(text) + continue + if cursor >= len(original) or original[cursor] != text: + found = original[cursor] if cursor < len(original) else "" + raise ValueError( + f"{path}: line {cursor + 1} is {found!r}, but the patch expects {text!r}" + ) + if tag == " ": + result.append(text) + cursor += 1 + result.extend(original[cursor:]) + return result + + +def apply_patch(tree, patch, package): + """Apply `patch` inside `tree`, rejecting anything short of an exact application. + + Deliberately not a `patch(1)` subprocess. This runs on all three CI hosts + now that materialization precedes every cargo command, and the GNU/BSD + implementations disagree about deletions, whitespace and exit codes -- BSD + leaves a zero-length file where GNU unlinks it, and "applied with fuzz" is + reported on stdout rather than in the exit status, so strictness depended on + sniffing English prose. An in-process applier is the same behaviour on every + host, and `--check` proves it round-trips against the recorded diff. + """ + body = patch.read_text() + if not body: + return + for path, deleted, created, hunks in parse_patch(body, package): + target = tree / path + # A header says whether the published package has this file, so a + # rebase that adds or removes one upstream fails here rather than + # quietly producing a tree the patch no longer describes. + if target.is_file() == created: + state = "already exists" if created else "is absent" + raise ValueError(f"patch does not apply exactly: {patch}\n{path} {state} in the published source") + if deleted: + target.unlink() + continue + # Bytes, not `read_text`/`write_text`: those translate newlines, and a + # published source file with CRLF endings has to survive the round trip + # byte for byte or the recorded patch stops matching it. + original = [] if created else target.read_bytes().decode("utf-8").splitlines(keepends=True) + try: + updated = apply_hunks(original, hunks, path) + except ValueError as error: + raise ValueError(f"patch does not apply exactly: {patch}\n{error}") from error + target.parent.mkdir(parents=True, exist_ok=True) + target.write_bytes("".join(updated).encode("utf-8")) + + def check_patch(base, fork, patch, package): expected = patch_text(base, fork, package) if not patch.is_file(): @@ -54,26 +239,37 @@ def check_patch(base, fork, patch, package): raise ValueError(f"stale patch: {patch}; regenerate with --write") with tempfile.TemporaryDirectory(prefix="herogpui-patch-") as temporary: replay = Path(temporary) / "source" - shutil.copytree(base, replay) - if expected: - result = subprocess.run( - ["patch", "--batch", "--forward", "-p2", "-i", str(patch.resolve())], - cwd=replay, capture_output=True, text=True, - ) - if result.returncode or "fuzz" in result.stdout or "offset" in result.stdout: - raise ValueError(f"patch does not apply exactly: {patch}\n{result.stdout}{result.stderr}") - # BSD patch leaves a zero-length file for a /dev/null deletion. Remove - # only explicitly deleted files; -E would also erase intentional empties. - original, changed = files(base), files(fork) - for name in original.keys() - changed.keys(): - deleted = replay / name - if deleted.is_file() and deleted.stat().st_size == 0: - deleted.unlink() - if files(replay) != changed: - raise ValueError(f"patch replay differs from local fork: {package}") + copy_pristine(base, replay) + apply_patch(replay, patch, package) + if files(replay) != files(fork): + raise ValueError(f"patch replay differs from the materialized tree: {package}") + + +def copy_pristine(base, destination): + """Copy the published source into a writable tree, minus cargo's bookkeeping. + + Registry unpacks are read-only, so the bytes are rewritten rather than + `copytree`d; `patch` has to be able to edit what lands here. Every file in + the published packages is mode 644 (verified against all five), so no + executable bit is carried across. + """ + destination.mkdir(parents=True) + for name, content in files(base).items(): + path = destination / name + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(content) + path.chmod(0o644) def registry_package(name, version): + """The pristine published source for the exact pin, from cargo's own cache. + + Nothing is downloaded by this script, so there is no unverified download to + trust: the tree returned here is one cargo unpacked itself after checking + the `.crate` tarball against the registry index checksum. On a cold cache + `cargo info` is what fetches it, outside this workspace so the patched + `[patch.crates-io]` entries cannot redirect the fetch to the fork. + """ cargo_home = Path(os.environ.get("CARGO_HOME", Path.home() / ".cargo")) candidates = sorted((cargo_home / "registry/src").glob(f"*/{name}-{version}")) candidates = [path for path in candidates if (path / "Cargo.toml").is_file()] @@ -97,6 +293,92 @@ def registry_package(name, version): return source +def write_stamp(tree, name, version, patch_sha256, upstream_sha256): + (tree / STAMP).write_text(json.dumps({ + "note": "Generated by .shots/gpui_patches.py --materialize. Not source; do not edit.", + "package": name, + "version": version, + "patch_sha256": patch_sha256, + "upstream_sha256": upstream_sha256, + "tree_sha256": tree_digest(tree), + }, indent=2) + "\n") + + +def materialize(name, version, destination, patch, package, resolve_base=registry_package): + """Make `destination` the pinned published source with `patch` applied. + + Idempotent and self-healing: a tree whose stamp still matches both the patch + and its own contents is left alone, and anything else is rebuilt from + scratch under a `.partial` name and swapped in, so an interrupted run never + leaves a half-patched tree behind for cargo to compile. + """ + if not patch.is_file(): + raise ValueError(f"missing patch: {patch}") + fingerprint = digest(patch.read_bytes()) + stamp = destination / STAMP + if stamp.is_file(): + try: + record = json.loads(stamp.read_text()) + except (json.JSONDecodeError, UnicodeDecodeError): + record = {} + if (record.get("package") == name + and record.get("version") == version + and record.get("patch_sha256") == fingerprint + and record.get("tree_sha256") == tree_digest(destination)): + return "already materialized" + base = resolve_base(name, version) + destination.parent.mkdir(parents=True, exist_ok=True) + staging = destination.parent / f".{destination.name}.partial" + shutil.rmtree(staging, ignore_errors=True) + try: + copy_pristine(base, staging) + apply_patch(staging, patch, package) + write_stamp(staging, name, version, fingerprint, tree_digest(base)) + shutil.rmtree(destination, ignore_errors=True) + staging.replace(destination) + finally: + shutil.rmtree(staging, ignore_errors=True) + return "materialized" + + +def workspace_pin(): + manifest = tomllib.loads((ROOT / "Cargo.toml").read_text()) + version = manifest["workspace"]["dependencies"]["gpui"]["version"] + if not version.startswith("="): + raise ValueError("the workspace GPUI dependency must have an exact version pin") + return manifest, version[1:] + + +def patched_path(manifest, name, version): + """Where `[patch.crates-io]` says this package lives, asserted to the convention. + + The path, the patch filename and the pin are one fact written three times, + so this refuses any `[patch.crates-io]` entry that does not spell the + materialization root the way `--materialize` writes it. + """ + configured = manifest["patch"]["crates-io"][name]["path"] + expected = f".vendor/{name}-{version}" + if configured != expected: + raise ValueError( + f"[patch.crates-io].{name} points at {configured!r}, not {expected!r}" + ) + return ROOT / configured + + +def require_materialized(destination, name, version): + if not (destination / "Cargo.toml").is_file(): + raise ValueError( + f"{destination.relative_to(ROOT).as_posix()} is not materialized; " + "run `python3 .shots/gpui_patches.py --materialize` first" + ) + package = tomllib.loads((destination / "Cargo.toml").read_text())["package"] + if package["name"] != name or package["version"] != version: + raise ValueError( + f"{destination.relative_to(ROOT).as_posix()}: materialized tree is " + f"{package['name']} {package['version']}, not the pinned {name} {version}" + ) + + def self_test(): with tempfile.TemporaryDirectory(prefix="herogpui-patch-test-") as temporary: root = Path(temporary) @@ -110,6 +392,30 @@ def self_test(): (fork / "added.rs").write_text("no trailing newline") patch.write_text(patch_text(base, fork, "example-0.3.3")) check_patch(base, fork, patch, "example-0.3.3") + + # Materialization reproduces the fork from the patch alone, and says so + # cheaply on a second run instead of rebuilding the tree. + vendor = root / "vendor" / "example-0.3.3" + resolve = lambda *_: base # noqa: E731 - one-line stub for the registry lookup + assert materialize("example", "0.3.3", vendor, patch, "example-0.3.3", resolve) == "materialized" + assert files(vendor) == files(fork), "materialized tree differs from the fork" + assert json.loads((vendor / STAMP).read_text())["package"] == "example" + assert materialize("example", "0.3.3", vendor, patch, "example-0.3.3", resolve) == "already materialized" + + # A hand-edited or half-written tree is rebuilt, not trusted. + (vendor / "changed.rs").write_text("tampered\n") + assert materialize("example", "0.3.3", vendor, patch, "example-0.3.3", resolve) == "materialized" + assert files(vendor) == files(fork), "tampered tree was not rebuilt" + + # ... and `--check` reports that tampering rather than repairing it. + (vendor / "changed.rs").write_text("tampered\n") + try: + check_patch(base, vendor, patch, "example-0.3.3") + except ValueError as error: + assert "stale patch" in str(error) + else: + raise AssertionError("a tampered materialized tree was accepted") + patch.write_text("") try: check_patch(base, fork, patch, "example-0.3.3") @@ -124,37 +430,45 @@ def self_test(): assert "missing patch" in str(error) else: raise AssertionError("a missing patch was accepted") - print("GPUI patch self-test: replay passed; stale and missing patches rejected") + try: + materialize("example", "0.3.3", vendor, patch, "example-0.3.3", resolve) + except ValueError as error: + assert "missing patch" in str(error) + else: + raise AssertionError("materialization accepted a missing patch") + print("GPUI patch self-test: materialize, replay, tamper, stale and missing patches all handled") def main(): parser = argparse.ArgumentParser(description=__doc__) mode = parser.add_mutually_exclusive_group() - mode.add_argument("--write", action="store_true", help="regenerate versioned patches") - mode.add_argument("--check", action="store_true", help="verify freshness and exact replay (default)") + mode.add_argument("--materialize", action="store_true", + help="build .vendor/ from the published sources plus the recorded patches") + mode.add_argument("--write", action="store_true", help="regenerate versioned patches from .vendor/") + mode.add_argument("--check", action="store_true", + help="verify each patch reproduces its materialized tree exactly (default)") mode.add_argument("--self-test", action="store_true") args = parser.parse_args() if args.self_test: self_test() return - manifest = tomllib.loads((ROOT / "Cargo.toml").read_text()) - version = manifest["workspace"]["dependencies"]["gpui"]["version"] - if not version.startswith("="): - raise ValueError("the workspace GPUI dependency must have an exact version pin") - version = version[1:] + manifest, version = workspace_pin() for name in PACKAGES: - fork = ROOT / manifest["patch"]["crates-io"][name]["path"] - package = tomllib.loads((fork / "Cargo.toml").read_text())["package"] - if package["name"] != name or package["version"] != version: - raise ValueError(f"{fork}: fork name/version differs from the workspace pin") + destination = patched_path(manifest, name, version) + package = f"{name}-{version}" + patch = PATCH_DIR / f"{package}.patch" + if args.materialize: + state = materialize(name, version, destination, patch, package) + print(f"{package}: {state} in {destination.relative_to(ROOT).as_posix()}") + continue + require_materialized(destination, name, version) base = registry_package(name, version) - label = f"{name}-{version}" - patch = ROOT / "docs/upstream/patches" / f"{label}.patch" if args.write: patch.parent.mkdir(parents=True, exist_ok=True) - patch.write_text(patch_text(base, fork, label)) - check_patch(base, fork, patch, label) - print(f"{label}: patch matches fork and replays exactly") + patch.write_text(patch_text(base, destination, package)) + write_stamp(destination, name, version, digest(patch.read_bytes()), tree_digest(base)) + check_patch(base, destination, patch, package) + print(f"{package}: patch applies exactly to the published source and reproduces .vendor/") if __name__ == "__main__": diff --git a/.shots/lint.ps1 b/.shots/lint.ps1 index d93cd5fe..cc7faaac 100644 --- a/.shots/lint.ps1 +++ b/.shots/lint.ps1 @@ -14,11 +14,10 @@ $root = Split-Path -Parent $PSScriptRoot Push-Location $root try { # 1. Every member crate must opt in, or the policy is not what it looks like. - # Exclude vendored crates that form their own workspace roots (the GPUI - # forks); they keep upstream's lint configuration, not this workspace's. - $vendored = @('gpui_web', 'gpui_pre', 'gpui_pre_apple', 'gpui_pre_wgpu', 'gpui_pre_windows') + # Only HeroGPUI's own crates live under crates/ now: the patched GPUI + # forks are materialized into .vendor/, keep upstream's lint + # configuration, and are never walked here. $members = Get-ChildItem -Path (Join-Path $root 'crates') -Directory | - Where-Object { $_.Name -notin $vendored } | ForEach-Object { Join-Path $_.FullName 'Cargo.toml' } $members += (Join-Path $root 'gallery/Cargo.toml') diff --git a/.shots/package_audit.py b/.shots/package_audit.py index 3261930e..c9d99f07 100644 --- a/.shots/package_audit.py +++ b/.shots/package_audit.py @@ -53,8 +53,8 @@ def main(): if dependency.get("package") != packages[name]: errors.append(f"{name}: must rename the {packages[name]} package") # The pin is exact on purpose. `gpui-pre-platform` requires the rest of - # the family at an exact `=` version, and the vendored `gpui-pre-web` - # fork's own version has to satisfy that same requirement or + # the family at an exact `=` version, and the materialized + # `gpui-pre-web` fork's version has to satisfy that same requirement or # `[patch.crates-io]` stops applying with no error at all. A caret let a # bare `cargo update` walk off the pin once already. if not dependency.get("version", "").startswith("="): @@ -66,9 +66,10 @@ def main(): version = requirement.lstrip("=") locked = manifest(ROOT / "Cargo.lock")["package"] # The published family remains the dependency contract of every package, - # while the workspace's `[patch.crates-io]` block substitutes local, - # version-identical renderer forks during development. The lockfile is - # therefore intentionally path-resolved for the five patched members. + # while the workspace's `[patch.crates-io]` block substitutes the + # materialized, version-identical renderer forks from `.vendor/`. The + # lockfile is therefore intentionally path-resolved for the five patched + # members. patched_gpui = { "gpui-pre", "gpui-pre-apple", @@ -92,24 +93,44 @@ def main(): if any((entry.get("source") or "").startswith("git+") for entry in locked): errors.append("lockfile still contains a git source; the crates cannot be published") - # The vendored `gpui-pre-web` fork (`crates/gpui_web`) reaches the wasm32 - # graph only through `[patch.crates-io]`, and cargo drops that override - # silently when the fork's version no longer satisfies what - # `gpui-pre-platform` asks for -- the fork's two `events.rs` hunks would - # just disappear from the browser build. Assert the version match and that - # the lock resolves the crate to the local path (no registry `source`). - fork = manifest(ROOT / "crates/gpui_web/Cargo.toml")["package"] - if version and fork.get("version") != version: - errors.append( - f"crates/gpui_web: vendored fork is {fork.get('version')!r}, " - f"not the pinned {version}; [patch.crates-io] will not apply" - ) + # No fork source is checked in: the deviation is recorded as a patch under + # docs/upstream/patches/ and `.shots/gpui_patches.py --materialize` applies + # it to the pinned published source under the gitignored `.vendor/`. So the + # version agreement this audit guards is spelled in two committed places -- + # the `[patch.crates-io]` path and the patch filename -- and both have to + # carry the pin. Cargo drops a `[patch.crates-io]` override silently when + # the substitute's version no longer satisfies what `gpui-pre-platform` + # asks for; the `gpui-pre-web` fork's two `events.rs` hunks and its + # `default = []` feature deviation would just disappear from the browser + # build, and nothing else would report it. `--check` verifies the patch + # content; this verifies the wiring, with no Rust build and no `.vendor/` + # tree, so it still runs in the parity job. + overrides = workspace.get("patch", {}).get("crates-io", {}) + for name in sorted(patched_gpui): + entry = overrides.get(name) + if not isinstance(entry, dict) or not entry.get("path"): + errors.append(f"{name}: no [patch.crates-io] path override") + continue + if entry.get("git"): + errors.append(f"{name}: [patch.crates-io] must not use a git source") + expected_path = f".vendor/{name}-{version}" + if version and entry["path"] != expected_path: + errors.append( + f"{name}: [patch.crates-io] path is {entry['path']!r}, not {expected_path!r}; " + "the override would apply the wrong version or none at all" + ) + patch_file = ROOT / "docs/upstream/patches" / f"{name}-{version}.patch" + if version and not patch_file.is_file(): + errors.append( + f"{name}: docs/upstream/patches/{name}-{version}.patch is missing; " + "nothing can materialize the fork" + ) web_entries = [entry for entry in locked if entry["name"] == "gpui-pre-web"] if len(web_entries) != 1: errors.append("gpui-pre-web: lockfile does not resolve to exactly one version") elif web_entries[0].get("source"): errors.append( - "gpui-pre-web: lockfile carries a source, so the vendored fork is not patched in" + "gpui-pre-web: lockfile carries a source, so the materialized fork is not patched in" ) elif version and web_entries[0].get("version") != version: errors.append(f"gpui-pre-web: lockfile version is not the pinned {version}") diff --git a/.shots/test_interaction_inventory.py b/.shots/test_interaction_inventory.py index 60c431b5..09cf7149 100644 --- a/.shots/test_interaction_inventory.py +++ b/.shots/test_interaction_inventory.py @@ -175,7 +175,7 @@ def test_snapshot_ignores_build_output_directories(self): source.parent.mkdir(parents=True, exist_ok=True) source.write_text('source') previous = SOURCE_SNAPSHOT(self.root) - artifact = self.root / 'crates/gpui_pre/target/debug/build/out/generated.rs' + artifact = self.root / 'crates/herogpui-components/target/debug/build/out/generated.rs' artifact.parent.mkdir(parents=True, exist_ok=True) artifact.write_text('generated') self.assertEqual(SOURCE_SNAPSHOT(self.root), previous) diff --git a/AGENTS.md b/AGENTS.md index 87d48b76..49eb8c72 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,18 +3,45 @@ HeroGPUI is a native Rust/GPUI port of HeroUI v3.2.5. The repository targets Rust 1.98 and the published `gpui-pre` crates pinned in `Cargo.toml` and `Cargo.lock` at exactly `=0.3.3` — zed-industries' own prerelease publish -of the GPUI sources, 0.3.3 being a snapshot of `zed@5b055fa`. Use those -unpacked sources for API evidence — -`~/.cargo/registry/src/index.crates.io-*/gpui-pre-0.3.3/` — not a Zed git -checkout, not the older third-party `gpui-unofficial` republish this replaced, -and not the unrelated crates.io `gpui` 0.2.2 crate. `gpui-pre`'s version -numbers are its own; they do not track Zed release tags. The pin is exact -because `gpui-pre-platform` requires the family at an exact `=` version and the -vendored `gpui-pre-web` fork's version must satisfy that same requirement or -`[patch.crates-io]` silently stops applying; 0.3.1 and 0.3.2 additionally must -not be resolved, because `gpui-pre-macros` 0.3.1 breaks every +of the GPUI sources, 0.3.3 being a snapshot of `zed@5b055fa`. `gpui-pre`'s +version numbers are its own; they do not track Zed release tags. The pin is +exact because `gpui-pre-platform` requires the family at an exact `=` version +and the patched `gpui-pre-web` fork's version must satisfy that same +requirement or `[patch.crates-io]` silently stops applying; 0.3.1 and 0.3.2 +additionally must not be resolved, because `gpui-pre-macros` 0.3.1 breaks every `debug_assertions`-off build (see `RELEASING.md`). +## Bootstrap the patched GPUI sources before any cargo command + +Five of those packages are forked. The repository stores **only the patches**, +under `docs/upstream/patches/`; the patched sources are not checked in. Before +the first `cargo` command in a fresh clone — and before pointing +rust-analyzer at it, because rust-analyzer runs `cargo metadata` — run: + +```sh +python3 .shots/gpui_patches.py --materialize +``` + +That copies the pinned published packages out of cargo's registry cache into +the gitignored `.vendor/` tree and applies each patch, exactly, with no fuzz and +no offset. It is idempotent: a warm run is a hash comparison that finishes in +well under a second, so it is safe to put in front of anything. Skipping it is +not a soft failure — `[patch.crates-io]` names five paths that do not exist yet, +and cargo aborts at manifest load with `failed to load source for dependency`. +`crates/herogpui-components/tests/rounded_clip_shaders.rs` also includes the +renderer's own `shaders.rs` from `.vendor/` by `#[path]`, so even `cargo fmt +--all` fails without it. Every CI job that touches cargo runs the step first, +through `.github/actions/rust-env` or its own copy. + +Use the pristine unpacked registry sources for API evidence — +`~/.cargo/registry/src/index.crates.io-*/gpui-pre-0.3.3/` — and `.vendor/` when +what you need is the patched behaviour. Neither is a Zed git checkout, the older +third-party `gpui-unofficial` republish this replaced, or the unrelated +crates.io `gpui` 0.2.2 crate. Never commit anything under `.vendor/`; to change +the fork, edit the materialized tree and re-record it with +`python3 .shots/gpui_patches.py --write`, which rewrites the patch and is +verified by `--check`. + ## Before editing 1. Run `git status --short` and inspect the relevant diff. Preserve unrelated @@ -100,10 +127,12 @@ all. Two facts about how, because both are easy to get wrong: - The switch is **not** `default-features = false` on a dependency edge of ours. `gpui_platform` depends on `gpui_web` with default features on, so a second edge from this workspace is unioned with that one and changes nothing. - The only lever is `default` in the vendored fork's own manifest, - `crates/gpui_web/Cargo.toml`, which `[patch.crates-io]` substitutes. It is - set to `default = []` there, documented at the `[features]` table as the - fork's third deviation from upstream. + The only lever is `default` in the forked manifest itself, which + `[patch.crates-io]` substitutes. It is set to `default = []` by a hunk in + `docs/upstream/patches/gpui-pre-web-0.3.3.patch` — the fork's third deviation + from upstream — and reaches the build as + `.vendor/gpui-pre-web-0.3.3/Cargo.toml`. Lose that hunk and the wasm job + needs a nightly pin again. - Nothing was using it. `crates/herogpui-web` starts the app with `gpui_platform::single_threaded_web()`, and the multi-threaded platform runs its background executors on web workers over shared wasm memory, which a @@ -128,4 +157,5 @@ sources, so after rebuilding it regenerate its manifests in the same change with `pnpm run wasm:manifest` from `web/`. They pin the artifact and every gallery example body by hash, and `pnpm run extract:check` fails when the two have parted company. `docs/upstream/gpui-web-scroll-and-ime.md` covers the one -remaining fork, the +9/-5 lines in `crates/gpui_web`. +remaining source fork of that crate, +9/-5 lines in `src/events.rs`, recorded +in `docs/upstream/patches/gpui-pre-web-0.3.3.patch`. diff --git a/Cargo.toml b/Cargo.toml index 398d3067..e0d9844b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,16 +9,13 @@ members = [ "gallery", ] -# The vendored `gpui_web` fork is a workspace root of its own (see its -# manifest's header), reached only through `[patch.crates-io]` at the bottom of -# this file. Naming it here says that omission from `members` is deliberate. -exclude = [ - "crates/gpui_pre", - "crates/gpui_pre_apple", - "crates/gpui_pre_wgpu", - "crates/gpui_pre_windows", - "crates/gpui_web", -] +# `.vendor/` is where `.shots/gpui_patches.py --materialize` writes the patched +# GPUI sources; it is gitignored and holds no HeroGPUI code. Each materialized +# manifest declares its own `[workspace]` table, so excluding the root here is +# belt and braces -- but it is also the line that says the omission from +# `members` is deliberate, and it keeps a stray `cargo metadata` from walking +# ~153,000 lines of dependency source it has no business in. +exclude = [".vendor"] [workspace.package] version = "0.1.0" @@ -43,14 +40,16 @@ categories = ["gui"] # `gpui_platform`'s wasm32 dependency, which enables its default features; a # second, `default-features = false` edge from this workspace would be unioned # with that one and change nothing. The `multithreaded` feature is therefore -# switched in the vendored fork's own manifest (`crates/gpui_web/Cargo.toml`), -# which `[patch.crates-io]` at the bottom of this file substitutes for it. +# switched in the forked manifest itself -- a `default = []` hunk in +# `docs/upstream/patches/gpui-pre-web-0.3.3.patch`, materialized into +# `.vendor/gpui-pre-web-0.3.3/Cargo.toml`, which `[patch.crates-io]` at the +# bottom of this file substitutes for the published crate. # # Both pins are exact (`=0.3.3`) rather than caret. `gpui-pre-platform` depends # on the rest of the family at an exact `=` version of its own, so a caret here # buys no flexibility while letting a bare `cargo update` walk the workspace to -# a version the vendored `gpui-pre-web` fork no longer matches -- at which point -# `[patch.crates-io]` silently stops applying. 0.3.1 and 0.3.2 must not be +# a version the recorded `gpui-pre-web` patch no longer matches -- at which +# point `[patch.crates-io]` silently stops applying. 0.3.1 and 0.3.2 must not be # resolved in any case: `gpui-pre-macros` leaves the inner # `__gpui_pre_derive_inspector_reflection` helper ungated in 0.3.1 while its # body calls into a `#[cfg(any(feature = "inspector", debug_assertions))]` @@ -164,16 +163,30 @@ strip = "symbols" # wasm checkout used, when GPUI came from a Zed git rev) matches no source in # that graph and applies *nothing*, with no error. Prove it is live with # `cargo tree -i gpui-pre-web --target wasm32-unknown-unknown`, which must -# print the `crates/gpui_web` path source. +# print the `.vendor/gpui-pre-web-0.3.3` path source. +# +# These five paths do not exist in a fresh clone. The repository records the +# fork as patches under `docs/upstream/patches/` and nothing else; +# `.shots/gpui_patches.py --materialize` copies the pinned published sources +# out of cargo's registry cache into `.vendor/` and applies them. Cargo cannot +# do that for us and it fails hard, not softly, when the path is absent -- +# "failed to load source for dependency" at manifest load, before any build -- +# so the materialization step runs first in every CI job that touches cargo, +# and a fresh clone (including one rust-analyzer is about to index) must run it +# before the first cargo command. These are `path` entries and not `git` ones +# on purpose: cargo refuses to publish a crate whose workspace carries a git +# dependency, which is the whole reason GPUI comes from the registry at all +# (see RELEASING.md). [patch.crates-io] # The published gpui-pre 0.3.3 clips `overflow_hidden()` to a rectangle even -# when the element has rounded corners. Keep the exact upstream snapshot in -# the workspace so the shared content mask can carry those corners through the -# renderer; every component then gets the same fix automatically. The fork -# delta and the rebase procedure are recorded in -# docs/upstream/gpui-rounded-content-mask.md. -gpui-pre = { path = "crates/gpui_pre" } -gpui-pre-apple = { path = "crates/gpui_pre_apple" } -gpui-pre-wgpu = { path = "crates/gpui_pre_wgpu" } -gpui-pre-windows = { path = "crates/gpui_pre_windows" } -gpui-pre-web = { path = "crates/gpui_web" } +# when the element has rounded corners. Materializing the exact upstream +# snapshot plus our patch lets the shared content mask carry those corners +# through the renderer; every component then gets the same fix automatically. +# The fork delta and the rebase procedure are recorded in +# docs/upstream/gpui-rounded-content-mask.md, and the `gpui-pre-web` fork in +# docs/upstream/gpui-web-scroll-and-ime.md. +gpui-pre = { path = ".vendor/gpui-pre-0.3.3" } +gpui-pre-apple = { path = ".vendor/gpui-pre-apple-0.3.3" } +gpui-pre-wgpu = { path = ".vendor/gpui-pre-wgpu-0.3.3" } +gpui-pre-windows = { path = ".vendor/gpui-pre-windows-0.3.3" } +gpui-pre-web = { path = ".vendor/gpui-pre-web-0.3.3" } diff --git a/README.md b/README.md index 83ec3f80..b4b39326 100644 --- a/README.md +++ b/README.md @@ -133,10 +133,19 @@ HeroUI spelling wins, and GPUI's keep the `gpui::` path (`gpui::Size`). A desktop gallery ships with the library and documents every component: ```bash +python3 .shots/gpui_patches.py --materialize # once per clone, before any cargo command cargo run -p herogpui-gallery # open the component gallery cargo install --path gallery --locked # install the gallery CLI from this checkout ``` +HeroGPUI patches five published `gpui-pre` packages and checks in only the +patches, under `docs/upstream/patches/`. The first line above applies them to +the pinned registry sources under the gitignored `.vendor/`, which +`[patch.crates-io]` points at. It is idempotent and near-instant when warm, but +it is not optional: without it cargo stops at `failed to load source for +dependency` before it builds anything. Consumers of the published crates are +unaffected -- this is a workspace-local development substitution. + `HEROGPUI_PAGE` and `HEROGPUI_THEME` select the page and appearance; `HEROGPUI_WINDOW_SIZE` sets the window size. diff --git a/RELEASING.md b/RELEASING.md index d87ef45e..9cc5941a 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -8,18 +8,38 @@ git revision: cargo refuses to publish any crate that carries a git dependency, so every `cargo publish` below would have failed outright. GPUI now comes from the published `gpui-pre` crates named in `[workspace.dependencies]`, and that registry dependency is what makes the steps below executable. Reintroducing -a git dependency anywhere in the workspace re-breaks publishing. +a git dependency anywhere in the workspace re-breaks publishing. The five +`[patch.crates-io]` overrides are deliberately `path` entries into `.vendor/` +for that reason, and must stay `path` entries: a `git` override would put a git +dependency back in the graph and make every crate below unpublishable again. + +## Materialize the patched GPUI sources first + +No step in this document works in a fresh clone until the patched GPUI sources +exist. The repository carries only the patches, under `docs/upstream/patches/`, +and `[patch.crates-io]` points at five `.vendor/` paths that are gitignored: + +```sh +python3 .shots/gpui_patches.py --materialize +``` + +Without it, cargo stops at manifest load with `failed to load source for +dependency` — `cargo package`, `cargo publish` and `cargo publish --dry-run` +included. It is idempotent, so run it whenever in doubt. `.vendor/` is a build +input, never a release artifact: the published crates depend on the registry +`gpui-pre` family, and the patch overrides are a workspace-local development +substitution that `cargo publish` does not carry into a `.crate`. ## Why the GPUI pin is exact `[workspace.dependencies]` pins `gpui-pre` and `gpui-pre-platform` at -`=0.3.3`, and the vendored `gpui-pre-web` fork in `crates/gpui_web` carries the -same version. Do not relax either to a caret. `gpui-pre-platform` requires the -rest of the family at an exact `=` version of its own, so the caret bought no -flexibility while letting a bare `cargo update` walk the workspace onto a -version the fork no longer matched — at which point `[patch.crates-io]` stops -applying with no error and the fork's `events.rs` hunks silently leave the -browser build. +`=0.3.3`, and the recorded `gpui-pre-web` patch carries the same version in its +filename and in the `[patch.crates-io]` path it materializes to. Do not relax +either to a caret. `gpui-pre-platform` requires the rest of the family at an +exact `=` version of its own, so the caret bought no flexibility while letting a +bare `cargo update` walk the workspace onto a version the fork no longer matched +— at which point `[patch.crates-io]` stops applying with no error and the fork's +`events.rs` hunks silently leave the browser build. 0.3.1 and 0.3.2 must not be resolved. `gpui-pre-macros` 0.3.1 leaves the inner `__gpui_pre_derive_inspector_reflection` helper ungated while its body calls @@ -56,7 +76,8 @@ credentials and none of these steps are required. 1. Update `[workspace.package].version` and all four version requirements under `[workspace.dependencies]` to the same SemVer value. -2. Run the complete local gate from `AGENTS.md`, plus: +2. Run the complete local gate from `AGENTS.md` (which starts with the + materialization above), plus: ```powershell cargo package -p herogpui-core --allow-dirty --no-verify --list diff --git a/crates/gpui_pre/Cargo.lock b/crates/gpui_pre/Cargo.lock deleted file mode 100644 index b505fe42..00000000 --- a/crates/gpui_pre/Cargo.lock +++ /dev/null @@ -1,5112 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "accesskit" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b7f7f85a7e5f68090000ed7622545829afd484d210358702ae4cb97dd0c320" -dependencies = [ - "enumn", - "uuid", -] - -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "aligned" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" -dependencies = [ - "as-slice", -] - -[[package]] -name = "aligned-vec" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" -dependencies = [ - "equator", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - -[[package]] -name = "anstyle" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" - -[[package]] -name = "anyhow" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" - -[[package]] -name = "ar_archive_writer" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" -dependencies = [ - "object", -] - -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" - -[[package]] -name = "arg_enum_proc_macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "as-slice" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-compression" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a89bce6054c720275ac2432fbba080a66a2106a44a1b804553930ca6909f4e0" -dependencies = [ - "compression-codecs", - "compression-core", - "futures-core", - "futures-io", - "pin-project-lite", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "av-scenechange" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" -dependencies = [ - "aligned", - "anyhow", - "arg_enum_proc_macro", - "arrayvec", - "log", - "num-rational", - "num-traits", - "pastey", - "rayon", - "thiserror 2.0.17", - "v_frame", - "y4m", -] - -[[package]] -name = "av1-grain" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link 0.2.1", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags 2.13.1", - "cexpr", - "clang-sys", - "itertools 0.11.0", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex 1.3.0", - "syn 2.0.117", -] - -[[package]] -name = "bit-set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" - -[[package]] -name = "bit_field" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" - -[[package]] -name = "bitstream-io" -version = "4.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" -dependencies = [ - "core2", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block2" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" -dependencies = [ - "objc2", -] - -[[package]] -name = "borsh" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" -dependencies = [ - "cfg_aliases", -] - -[[package]] -name = "built" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytemuck" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "bzip2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" -dependencies = [ - "libbz2-rs-sys", -] - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - -[[package]] -name = "cc" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex 2.0.1", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "cgl" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" -dependencies = [ - "libc", -] - -[[package]] -name = "chrono" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link 0.2.1", -] - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "4.5.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f" -dependencies = [ - "clap_builder", -] - -[[package]] -name = "clap_builder" -version = "4.5.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730" -dependencies = [ - "anstyle", - "clap_lex", -] - -[[package]] -name = "clap_lex" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" - -[[package]] -name = "cocoa" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation 0.9.4", - "core-graphics 0.23.2", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "libc", - "objc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "compression-codecs" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8a506ec4b81c460798f572caead636d57d3d7e940f998160f52bd254bf2d23" -dependencies = [ - "bzip2", - "compression-core", - "flate2", - "memchr", -] - -[[package]] -name = "compression-core" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "convert_case" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "core-graphics" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" -dependencies = [ - "bitflags 2.13.1", - "core-foundation 0.10.0", - "core-graphics-types 0.2.0", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-helmer-fork" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32eb7c354ae9f6d437a6039099ce7ecd049337a8109b23d73e48e8ffba8e9cd5" -dependencies = [ - "bitflags 2.13.1", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" -dependencies = [ - "bitflags 2.13.1", - "core-foundation 0.10.0", - "libc", -] - -[[package]] -name = "core-graphics2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4416167a69126e617f8d0a214af0e3c1dbdeffcb100ddf72dcd1a1ac9893c146" -dependencies = [ - "bitflags 2.13.1", - "block", - "cfg-if", - "core-foundation 0.10.0", - "libc", -] - -[[package]] -name = "core-text" -version = "21.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a593227b66cbd4007b2a050dfdd9e1d1318311409c8d600dc82ba1b15ca9c130" -dependencies = [ - "core-foundation 0.10.0", - "core-graphics 0.24.0", - "foreign-types", - "libc", -] - -[[package]] -name = "core-video" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139679cc63eb9504bdbe37e37874b0247136177655f0008588781e90863afa62" -dependencies = [ - "block", - "core-foundation 0.10.0", - "core-graphics2", - "io-surface", - "libc", - "metal", -] - -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - -[[package]] -name = "core_maths" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" -dependencies = [ - "libm", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "criterion" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" -dependencies = [ - "anes", - "cast", - "ciborium", - "clap", - "criterion-plot", - "is-terminal", - "itertools 0.10.5", - "num-traits", - "once_cell", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools 0.10.5", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - -[[package]] -name = "ctor" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d83cb7e7a873830708d6b02a78cd36a592c6fa14bf267b68725103b85c0d77f" -dependencies = [ - "link-section", - "linktime-proc-macro", -] - -[[package]] -name = "data-url" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" - -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "convert_case 0.10.0", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.117", - "unicode-xid", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dispatch2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" -dependencies = [ - "bitflags 2.13.1", - "objc2", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "dlib" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" -dependencies = [ - "libloading", -] - -[[package]] -name = "dwrote" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b35532432acc8b19ceed096e35dfa088d3ea037fe4f3c085f1f97f33b4d02" -dependencies = [ - "lazy_static", - "libc", - "winapi", - "wio", -] - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "embed-resource" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a075fc573c64510038d7ee9abc7990635863992f83ebc52c8b433b8411a02e" -dependencies = [ - "cc", - "memchr", - "rustc_version", - "toml", - "vswhom", - "winreg", -] - -[[package]] -name = "enumn" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "equator" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" -dependencies = [ - "equator-macro", -] - -[[package]] -name = "equator-macro" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "erased-serde" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" -dependencies = [ - "serde", - "serde_core", - "typeid", -] - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "etagere" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" -dependencies = [ - "euclid", - "svg_fmt", -] - -[[package]] -name = "euclid" -version = "0.22.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" -dependencies = [ - "num-traits", -] - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - -[[package]] -name = "exr" -version = "1.74.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" -dependencies = [ - "bit_field", - "half", - "lebe", - "miniz_oxide", - "rayon-core", - "smallvec", - "zune-inflate", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "fax" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" -dependencies = [ - "fax_derive", -] - -[[package]] -name = "fax_derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" - -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - -[[package]] -name = "flate2" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" - -[[package]] -name = "float-ord" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" - -[[package]] -name = "float_next_after" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" - -[[package]] -name = "flume" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" -dependencies = [ - "fastrand", - "futures-core", - "futures-sink", - "spin 0.9.8", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "fontconfig-parser" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" -dependencies = [ - "roxmltree 0.20.0", -] - -[[package]] -name = "fontdb" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" -dependencies = [ - "fontconfig-parser", - "log", - "memmap2", - "slotmap", - "tinyvec", - "ttf-parser", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "freetype-sys" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-concurrency" -version = "7.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175cd8cca9e1d45b87f18ffa75088f2099e3c4fe5e2f83e42de112560bea8ea6" -dependencies = [ - "fixedbitset", - "futures-core", - "futures-lite", - "pin-project", - "smallvec", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", - "wasip3", -] - -[[package]] -name = "gif" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" -dependencies = [ - "color_quant", - "weezl", -] - -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "gpui-pre" -version = "0.3.3" -dependencies = [ - "accesskit", - "anyhow", - "async-channel", - "async-task", - "backtrace", - "bindgen", - "bitflags 2.13.1", - "chrono", - "core-video", - "criterion", - "ctor", - "derive_more", - "embed-resource", - "etagere", - "futures", - "futures-concurrency", - "getrandom 0.3.4", - "gpui-pre-collections", - "gpui-pre-http-client", - "gpui-pre-macros", - "gpui-pre-refineable", - "gpui-pre-scheduler", - "gpui-pre-shared-string", - "gpui-pre-sum-tree", - "gpui-pre-util", - "gpui-pre-util-macros", - "gpui-pre-ztracing", - "hdrhistogram", - "heapless", - "image", - "inventory", - "itertools 0.14.0", - "log", - "lyon", - "num_cpus", - "objc2", - "objc2-metal", - "parking", - "parking_lot", - "pin-project", - "pollster 0.4.0", - "postage", - "profiling", - "proptest", - "rand 0.9.4", - "raw-window-handle", - "regex", - "resvg", - "schemars", - "seahash", - "serde", - "serde_json", - "slotmap", - "smallvec", - "spin 0.10.0", - "stacksafe", - "strum", - "taffy", - "thiserror 2.0.17", - "tracing", - "ttf-parser", - "url", - "usvg", - "uuid", - "waker-fn", - "web-time", - "windows 0.62.2", - "zed-font-kit", - "zed-scap", -] - -[[package]] -name = "gpui-pre-collections" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c8efa2e51e368c8538a7be1ea9a12127ca03abe6cc01d9d3474e9ac4f53016" -dependencies = [ - "gpui-pre-util", - "indexmap", - "rustc-hash", -] - -[[package]] -name = "gpui-pre-derive-refineable" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a098d319acc9f84bf159944f96c5ea43a4f4cd7ed759f984acbd15719495aa0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gpui-pre-http-client" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3495a45a28cb800c8626d2053406114bcaca26d390a4d4b183365b5bb8fdaf02" -dependencies = [ - "anyhow", - "async-compression", - "bytes", - "derive_more", - "futures", - "http", - "http-body", - "log", - "parking_lot", - "serde", - "serde_json", - "serde_urlencoded", - "url", -] - -[[package]] -name = "gpui-pre-macros" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be2db7b5097b4d523b2bce933604bcc5acdaf679bb9a150e8299e6c07efc29c" -dependencies = [ - "heck", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gpui-pre-perf" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1db0c046b93c2a29120f8ee4c04bc80a4d7d6117d1164ef349faface8943491" -dependencies = [ - "gpui-pre-collections", - "serde", - "serde_json", -] - -[[package]] -name = "gpui-pre-refineable" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864e2e54a3029481dae5b6aae3ba2905f2fb1dfe66ced913b68c9ff527f8e327" -dependencies = [ - "gpui-pre-derive-refineable", -] - -[[package]] -name = "gpui-pre-scheduler" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b58a78c4e0c032900704ea49922641c534cf8bbf1bf22eda6ba00a76e88c6c3" -dependencies = [ - "async-task", - "backtrace", - "chrono", - "flume", - "futures", - "parking_lot", - "rand 0.9.4", - "web-time", -] - -[[package]] -name = "gpui-pre-shared-string" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fc99fe88e44173758a500522d3f4059a6541da4b471af720e3f60cf34a2bc3" -dependencies = [ - "schemars", - "serde", - "smol_str", -] - -[[package]] -name = "gpui-pre-sum-tree" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002baed852f20cef1188d3d5e0f749025fc718e4d6cea026b9077a9a6c10d042" -dependencies = [ - "gpui-pre-ztracing", - "heapless", - "log", - "rayon", - "tracing", -] - -[[package]] -name = "gpui-pre-util" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe779c4cb00929aafcb2b307cd1240588aebb5d1eb328c59b80f77c05a41fad" -dependencies = [ - "anyhow", - "log", - "which", -] - -[[package]] -name = "gpui-pre-util-macros" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f0ccc4bccb6a31786095d15fc9f40d6a4c6a295522e3460331dd7e929ff2f79" -dependencies = [ - "gpui-pre-perf", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gpui-pre-zlog" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1416ea5f018e3a8a1c8be266332c443583f28f8f87379a84ab1e77622173ac0" -dependencies = [ - "anyhow", - "chrono", - "gpui-pre-collections", - "log", -] - -[[package]] -name = "gpui-pre-ztracing" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cc64a25a3cc4e4f1d8acb00074d3925339dae657c020083735738ba8af96bf7" -dependencies = [ - "gpui-pre-zlog", - "gpui-pre-ztracing-macro", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "gpui-pre-ztracing-macro" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3ea75672348f37d94472e579f5979ee19b744d0fa5aaadc2fe129ccafa80b" - -[[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", -] - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "hdrhistogram" -version = "7.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" -dependencies = [ - "byteorder", - "num-traits", -] - -[[package]] -name = "heapless" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" -dependencies = [ - "hash32", - "stable_deref_trait", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "http" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core 0.62.2", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" - -[[package]] -name = "icu_properties" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" - -[[package]] -name = "icu_provider" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "image" -version = "0.25.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" -dependencies = [ - "bytemuck", - "byteorder-lite", - "color_quant", - "exr", - "gif", - "image-webp", - "moxcms", - "num-traits", - "png 0.18.0", - "qoi", - "ravif", - "rayon", - "tiff", - "zune-core", - "zune-jpeg", -] - -[[package]] -name = "image-webp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" -dependencies = [ - "byteorder-lite", - "quick-error 2.0.1", -] - -[[package]] -name = "imagesize" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" - -[[package]] -name = "imgref" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", -] - -[[package]] -name = "interpolate_name" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "inventory" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" -dependencies = [ - "rustversion", -] - -[[package]] -name = "io-surface" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "554b8c5d64ec09a3a520fe58e4d48a73e00ff32899cdcbe32a4877afd4968b8e" -dependencies = [ - "cgl", - "core-foundation 0.10.0", - "core-foundation-sys", - "leaky-cow", -] - -[[package]] -name = "is-terminal" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" -dependencies = [ - "cfg-if", - "futures-util", - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "kurbo" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b60dfc32f652b926df6192e55525b16d186c69d47876c3ead4da5cc9f8450e2" -dependencies = [ - "arrayvec", - "euclid", - "polycool", - "smallvec", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leak" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd100e01f1154f2908dfa7d02219aeab25d0b9c7fa955164192e3245255a0c73" - -[[package]] -name = "leaky-cow" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a8225d44241fd324a8af2806ba635fc7c8a7e9a7de4d5cf3ef54e71f5926fc" -dependencies = [ - "leak", -] - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "lebe" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" - -[[package]] -name = "libbz2-rs-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "libfuzzer-sys" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" -dependencies = [ - "arbitrary", - "cc", -] - -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link 0.2.1", -] - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "libredox" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" -dependencies = [ - "bitflags 2.13.1", - "libc", -] - -[[package]] -name = "link-section" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee1a0d6e252afe82e7bc2db42fba60e02ddf3b1accaf8cb21d96e34ba61f3d4" - -[[package]] -name = "linktime-proc-macro" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348d0075b1fc163b26d72a7f75fc5141daf2fd1bdf128d873cbaf6785d495bdf" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "litemap" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -dependencies = [ - "serde_core", - "value-bag", -] - -[[package]] -name = "loop9" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" -dependencies = [ - "imgref", -] - -[[package]] -name = "lyon" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcb7d54d54c8937364c9d41902d066656817dce1e03a44e5533afebd1ef4352" -dependencies = [ - "lyon_algorithms", - "lyon_tessellation", -] - -[[package]] -name = "lyon_algorithms" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c0829e28c4f336396f250d850c3987e16ce6db057ffe047ce0dd54aab6b647" -dependencies = [ - "lyon_path", - "num-traits", -] - -[[package]] -name = "lyon_geom" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e16770d760c7848b0c1c2d209101e408207a65168109509f8483837a36cf2e7" -dependencies = [ - "arrayvec", - "euclid", - "num-traits", -] - -[[package]] -name = "lyon_path" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aeca86bcfd632a15984ba029b539ffb811e0a70bf55e814ef8b0f54f506fdeb" -dependencies = [ - "lyon_geom", - "num-traits", -] - -[[package]] -name = "lyon_tessellation" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f586142e1280335b1bc89539f7c97dd80f08fc43e9ab1b74ef0a42b04aa353" -dependencies = [ - "float_next_after", - "lyon_path", - "num-traits", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "maybe-rayon" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" -dependencies = [ - "cfg-if", - "rayon", -] - -[[package]] -name = "memchr" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" - -[[package]] -name = "memmap2" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" -dependencies = [ - "libc", -] - -[[package]] -name = "metal" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" -dependencies = [ - "bitflags 2.13.1", - "block", - "core-graphics-types 0.2.0", - "foreign-types", - "log", - "objc", - "paste", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "moxcms" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" -dependencies = [ - "num-traits", - "pxfm", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - -[[package]] -name = "ntapi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" -dependencies = [ - "winapi", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-bigint" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[package]] -name = "objc-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - -[[package]] -name = "objc2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" -dependencies = [ - "objc2-encode", -] - -[[package]] -name = "objc2-core-foundation" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" -dependencies = [ - "bitflags 2.13.1", - "dispatch2", - "objc2", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - -[[package]] -name = "objc2-foundation" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" -dependencies = [ - "bitflags 2.13.1", - "objc2", -] - -[[package]] -name = "objc2-metal" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" -dependencies = [ - "bitflags 2.13.1", - "block2", - "dispatch2", - "objc2", - "objc2-core-foundation", - "objc2-foundation", -] - -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[package]] -name = "objc_id" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -dependencies = [ - "objc", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "oorandom" -version = "11.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link 0.2.1", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pastey" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" - -[[package]] -name = "pathfinder_geometry" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" -dependencies = [ - "log", - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_simd" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4500030c302e4af1d423f36f3b958d1aecb6c04184356ed5a833bf6b60435777" -dependencies = [ - "rustc_version", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - -[[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "plotters" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" - -[[package]] -name = "plotters-svg" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "png" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" -dependencies = [ - "bitflags 2.13.1", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "pollster" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" - -[[package]] -name = "pollster" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" - -[[package]] -name = "polycool" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50596ddc09eb5ad5f75cacd40209568e66df71baf86e1499a0e99c4cff12a5a6" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "postage" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" -dependencies = [ - "atomic", - "crossbeam-queue", - "futures", - "log", - "parking_lot", - "pin-project", - "pollster 0.2.5", - "static_assertions", - "thiserror 1.0.69", -] - -[[package]] -name = "potential_utf" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" -dependencies = [ - "zerovec", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - -[[package]] -name = "proc-macro-crate" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" -dependencies = [ - "toml_edit", -] - -[[package]] -name = "proc-macro2" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "profiling" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" -dependencies = [ - "quote", - "syn 2.0.117", -] - -[[package]] -name = "proptest" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.13.1", - "num-traits", - "proptest-macro", - "rand 0.9.4", - "rand_chacha 0.9.0", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "proptest-macro" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaa288b896cb2b345da7b7f2110ab19e51565b83495b56fcec98a62f8b1f33e" -dependencies = [ - "convert_case 0.11.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "psm" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8" -dependencies = [ - "ar_archive_writer", - "cc", -] - -[[package]] -name = "pxfm" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cbdf373972bf78df4d3b518d07003938e2c7d1fb5891e55f9cb6df57009d84" -dependencies = [ - "num-traits", -] - -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quick-xml" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" -dependencies = [ - "memchr", -] - -[[package]] -name = "quote" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rand_xorshift" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" -dependencies = [ - "rand_core 0.9.3", -] - -[[package]] -name = "rav1e" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" -dependencies = [ - "aligned-vec", - "arbitrary", - "arg_enum_proc_macro", - "arrayvec", - "av-scenechange", - "av1-grain", - "bitstream-io", - "built", - "cfg-if", - "interpolate_name", - "itertools 0.14.0", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive", - "num-traits", - "paste", - "profiling", - "rand 0.9.4", - "rand_chacha 0.9.0", - "simd_helpers", - "thiserror 2.0.17", - "v_frame", - "wasm-bindgen", -] - -[[package]] -name = "ravif" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" -dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error 2.0.1", - "rav1e", - "rayon", - "rgb", -] - -[[package]] -name = "raw-window-handle" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" - -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.13.1", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror 1.0.69", -] - -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" - -[[package]] -name = "resvg" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b563218631706d614e23059436526d005b50ab5f2d506b55a17eb65c5eb83419" -dependencies = [ - "gif", - "image-webp", - "log", - "pico-args", - "rgb", - "svgtypes", - "tiny-skia", - "usvg", - "zune-jpeg", -] - -[[package]] -name = "rgb" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "roxmltree" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" - -[[package]] -name = "roxmltree" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb" -dependencies = [ - "memchr", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.13.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "rusty-fork" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" -dependencies = [ - "fnv", - "quick-error 1.2.3", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "rustybuzz" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" -dependencies = [ - "bitflags 2.13.1", - "bytemuck", - "core_maths", - "log", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schemars" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" -dependencies = [ - "dyn-clone", - "indexmap", - "ref-cast", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.117", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "screencapturekit" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5eeeb57ac94960cfe5ff4c402be6585ae4c8d29a2cf41b276048c2e849d64e" -dependencies = [ - "screencapturekit-sys", -] - -[[package]] -name = "screencapturekit-sys" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22411b57f7d49e7fe08025198813ee6fd65e1ee5eff4ebc7880c12c82bde4c60" -dependencies = [ - "block", - "dispatch", - "objc", - "objc-foundation", - "objc_id", - "once_cell", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_fmt" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_json" -version = "1.0.151" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" -dependencies = [ - "indexmap", - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_spanned" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" -dependencies = [ - "serde_core", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "simd_helpers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" -dependencies = [ - "quote", -] - -[[package]] -name = "simplecss" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" -dependencies = [ - "log", -] - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "slotmap" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" -dependencies = [ - "version_check", -] - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "smol_str" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aaa7368fcf4852a4c2dd92df0cace6a71f2091ca0a23391ce7f3a31833f1523" -dependencies = [ - "borsh", - "serde_core", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spin" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" -dependencies = [ - "lock_api", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "stacker" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707f49d46706bacf8a2b00d51dace3f9de527c13eec3778f570c411f89e69967" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "windows-sys 0.61.2", -] - -[[package]] -name = "stacksafe" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95f9c34983ac74195c710c473db6fdf1085f64a47dbaa0090d1bea03be70da66" -dependencies = [ - "stacker", - "stacksafe-macro", -] - -[[package]] -name = "stacksafe-macro" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6feeae42a2d6b0dcb8aeb2f08d9e48cdac600239cf8a20fc59f9e252e86bdfe1" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.3", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -dependencies = [ - "float-cmp", -] - -[[package]] -name = "strum" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sval" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94c4464e595f0284970fd9c7e9013804d035d4a61ab74b113242c874c05814d" - -[[package]] -name = "sval_buffer" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0f46e34b20a39e6a2bf02b926983149b3af6609fd1ee8a6e63f6f340f3e2164" -dependencies = [ - "sval", - "sval_ref", -] - -[[package]] -name = "sval_dynamic" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d0970e53c92ab5381d3b2db1828da8af945954d4234225f6dd9c3afbcef3f5" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_fmt" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e5e6e1613e1e7fc2e1a9fdd709622e54c122ceb067a60d170d75efd491a839" -dependencies = [ - "itoa", - "ryu", - "sval", -] - -[[package]] -name = "sval_json" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aec382f7bfa6e367b23c9611f129b94eb7daaf3d8fae45a8d0a0211eb4d4c8e6" -dependencies = [ - "itoa", - "ryu", - "sval", -] - -[[package]] -name = "sval_nested" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3049d0f99ce6297f8f7d9953b35a0103b7584d8f638de40e64edb7105fa578ae" -dependencies = [ - "sval", - "sval_buffer", - "sval_ref", -] - -[[package]] -name = "sval_ref" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88913e77506085c0a8bf6912bb6558591a960faf5317df6c1d9b227224ca6e1" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_serde" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f579fd7254f4be6cd7b450034f856b78523404655848789c451bacc6aa8b387d" -dependencies = [ - "serde_core", - "sval", - "sval_nested", -] - -[[package]] -name = "svg_fmt" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" - -[[package]] -name = "svgtypes" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "695b5790b3131dafa99b3bbfd25a216edb3d216dad9ca208d4657bfb8f2abc3d" -dependencies = [ - "kurbo", - "siphasher", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sysinfo" -version = "0.31.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" -dependencies = [ - "core-foundation-sys", - "libc", - "memchr", - "ntapi", - "rayon", - "windows 0.57.0", -] - -[[package]] -name = "taffy" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c034e05f6ee85a12daa63863c2245797715075c70649947aa0da54f3f2ab1d0f" -dependencies = [ - "arrayvec", - "serde", - "slotmap", - "smallvec", -] - -[[package]] -name = "tao-core-video-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271450eb289cb4d8d0720c6ce70c72c8c858c93dd61fc625881616752e6b98f6" -dependencies = [ - "cfg-if", - "core-foundation-sys", - "libc", - "objc", -] - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.1", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl 2.0.17", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "tiff" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" -dependencies = [ - "fax", - "flate2", - "half", - "quick-error 2.0.1", - "weezl", - "zune-jpeg", -] - -[[package]] -name = "tiny-skia" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if", - "log", - "png 0.17.16", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - -[[package]] -name = "tinystr" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "toml" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" -dependencies = [ - "indexmap", - "serde_core", - "serde_spanned", - "toml_datetime", - "toml_parser", - "toml_writer", - "winnow", -] - -[[package]] -name = "toml_datetime" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" -dependencies = [ - "indexmap", - "toml_datetime", - "toml_parser", - "winnow", -] - -[[package]] -name = "toml_parser" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" -dependencies = [ - "winnow", -] - -[[package]] -name = "toml_writer" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" - -[[package]] -name = "tracing" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tracing-core" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "ttf-parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -dependencies = [ - "core_maths", -] - -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - -[[package]] -name = "unicode-bidi-mirroring" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" - -[[package]] -name = "unicode-ccc" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-properties" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" - -[[package]] -name = "unicode-script" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" - -[[package]] -name = "unicode-segmentation" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" - -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "url" -version = "2.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "usvg" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e419dff010bb12512b0ae9e3d2f318dfbdf0167fde7eb05465134d4e8756076f" -dependencies = [ - "base64", - "data-url", - "flate2", - "fontdb", - "imagesize", - "kurbo", - "log", - "pico-args", - "roxmltree 0.21.1", - "rustybuzz", - "simplecss", - "siphasher", - "strict-num", - "svgtypes", - "tiny-skia-path", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" -dependencies = [ - "getrandom 0.3.4", - "js-sys", - "serde", - "sha1_smol", - "wasm-bindgen", -] - -[[package]] -name = "v_frame" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "value-bag" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" -dependencies = [ - "value-bag-serde1", - "value-bag-sval2", -] - -[[package]] -name = "value-bag-serde1" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16530907bfe2999a1773ca5900a65101e092c70f642f25cc23ca0c43573262c5" -dependencies = [ - "erased-serde", - "serde_core", - "serde_fmt", -] - -[[package]] -name = "value-bag-sval2" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00ae130edd690eaa877e4f40605d534790d1cf1d651e7685bd6a144521b251f" -dependencies = [ - "sval", - "sval_buffer", - "sval_dynamic", - "sval_fmt", - "sval_json", - "sval_ref", - "sval_serde", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "vswhom" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" -dependencies = [ - "libc", - "vswhom-sys", -] - -[[package]] -name = "vswhom-sys" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "wait-timeout" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" -dependencies = [ - "libc", -] - -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen 0.46.0", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.117", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.13.1", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - -[[package]] -name = "web-sys" -version = "0.3.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "weezl" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" - -[[package]] -name = "which" -version = "8.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3ef584124b911bcc3875c2f1472e80f24361ceb789bd1c62b3e9a3df9ff43c" -dependencies = [ - "libc", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" -dependencies = [ - "windows-core 0.57.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections 0.2.0", - "windows-core 0.61.2", - "windows-future 0.2.1", - "windows-link 0.1.3", - "windows-numerics 0.2.0", -] - -[[package]] -name = "windows" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" -dependencies = [ - "windows-collections 0.3.2", - "windows-core 0.62.2", - "windows-future 0.3.2", - "windows-numerics 0.3.1", -] - -[[package]] -name = "windows-capture" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a4df73e95feddb9ec1a7e9c2ca6323b8c97d5eeeff78d28f1eccdf19c882b24" -dependencies = [ - "parking_lot", - "rayon", - "thiserror 2.0.17", - "windows 0.61.3", - "windows-future 0.2.1", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", -] - -[[package]] -name = "windows-collections" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" -dependencies = [ - "windows-core 0.62.2", -] - -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", - "windows-threading 0.1.0", -] - -[[package]] -name = "windows-future" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" -dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", - "windows-threading 0.2.1", -] - -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", -] - -[[package]] -name = "windows-numerics" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" -dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", -] - -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-threading" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.55.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" -dependencies = [ - "cfg-if", - "windows-sys 0.59.0", -] - -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi", -] - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.13.1", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "writeable" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "xcb" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f07c123b796139bfe0603e654eaf08e132e52387ba95b252c78bad3640ba37ea" -dependencies = [ - "bitflags 1.3.2", - "libc", - "quick-xml", - "x11", -] - -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - -[[package]] -name = "y4m" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" - -[[package]] -name = "yeslogic-fontconfig-sys" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503a066b4c037c440169d995b869046827dbc71263f6e8f3be6d77d4f3229dbd" -dependencies = [ - "dlib", - "once_cell", - "pkg-config", -] - -[[package]] -name = "yoke" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zed-font-kit" -version = "0.14.1-zed" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3898e450f36f852edda72e3f985c34426042c4951790b23b107f93394f9bff5" -dependencies = [ - "bitflags 2.13.1", - "byteorder", - "core-foundation 0.10.0", - "core-graphics 0.24.0", - "core-text", - "dirs", - "dwrote", - "float-ord", - "freetype-sys", - "lazy_static", - "libc", - "log", - "pathfinder_geometry", - "pathfinder_simd", - "walkdir", - "winapi", - "yeslogic-fontconfig-sys", -] - -[[package]] -name = "zed-scap" -version = "0.0.8-zed" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b338d705ae33a43ca00287c11129303a7a0aa57b101b72a1c08c863f698ac8" -dependencies = [ - "anyhow", - "cocoa", - "core-graphics-helmer-fork", - "log", - "objc", - "rand 0.8.6", - "screencapturekit", - "screencapturekit-sys", - "sysinfo", - "tao-core-video-sys", - "windows 0.61.3", - "windows-capture", - "x11", - "xcb", -] - -[[package]] -name = "zerocopy" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zerotrie" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f212a141d820099d57ffafb9569be9617a6f27d3dc881fbee8fb56642f917a9" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.3", -] - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" - -[[package]] -name = "zune-core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "zune-jpeg" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" -dependencies = [ - "zune-core", -] diff --git a/crates/gpui_pre/Cargo.toml b/crates/gpui_pre/Cargo.toml deleted file mode 100644 index d5b305c7..00000000 --- a/crates/gpui_pre/Cargo.toml +++ /dev/null @@ -1,670 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies. -# -# If you are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2024" -name = "gpui-pre" -version = "0.3.3" -authors = ["Nathan Sobo "] -build = "build.rs" -publish = true -autolib = false -autobins = false -autoexamples = false -autotests = false -autobenches = false -description = "Zed's GPU-accelerated UI framework (gpui-pre snapshot of zed@5b055fa)" -homepage = "https://gpui.rs" -readme = "README.md" -keywords = [ - "desktop", - "gui", - "immediate", -] -categories = ["gui"] -license = "Apache-2.0" -repository = "https://github.com/zed-industries/zed" -resolver = "2" - -[package.metadata.cargo-shear] -ignored = [ - "bindgen", - "font-kit", - "getrandom", - "objc2", - "objc2-metal", - "tracing", -] -ignored-paths = ["src/_ownership_and_data_flow.rs"] - -# `ztracing::instrument` re-exports tracing's attribute when its cfg is on, and -# that macro expands to `tracing::` paths resolved in this crate; machete greps -# tokens and cannot see the expansion. The vendored manifest keeps upstream's -# direct dependency rather than drifting from the published crate. -[package.metadata.cargo-machete] -ignored = ["tracing"] - -[package.metadata.gpui-pre] -zed-crate = "gpui" -zed-version = "0.2.2" -zed-rev = "5b055fa789a8b8d38ac951a6e0cde272f66b4495" - -[features] -bench = ["bench-support"] -bench-support = [ - "profiler", - "dep:criterion", -] -default = [ - "font-kit", - "wayland", - "x11", - "windows-manifest", -] -inspector = ["gpui_macros/inspector"] -leak-detection = ["backtrace"] -profiler = ["dep:hdrhistogram"] -screen-capture = ["scap"] -stacker = ["dep:stacksafe"] -test-support = [ - "leak-detection", - "collections/test-support", - "http_client/test-support", - "wayland", - "x11", - "proptest", -] -wayland = [] -windows-manifest = ["dep:embed-resource"] -x11 = ["scap?/x11"] - -[lib] -name = "gpui" -path = "src/gpui.rs" -doctest = false - -[[example]] -name = "a11y" -path = "examples/a11y.rs" - -[[example]] -name = "active_state_bug" -path = "examples/active_state_bug.rs" - -[[example]] -name = "anchor" -path = "examples/anchor.rs" - -[[example]] -name = "animation" -path = "examples/animation.rs" - -[[example]] -name = "data_table" -path = "examples/data_table.rs" - -[[example]] -name = "drag_drop" -path = "examples/drag_drop.rs" - -[[example]] -name = "focus_visible" -path = "examples/focus_visible.rs" - -[[example]] -name = "gif_viewer" -path = "examples/gif_viewer.rs" - -[[example]] -name = "gradient" -path = "examples/gradient.rs" - -[[example]] -name = "grid_layout" -path = "examples/grid_layout.rs" - -[[example]] -name = "hello_world" -path = "examples/hello_world.rs" - -[[example]] -name = "image" -path = "examples/image/image.rs" - -[[example]] -name = "image_gallery" -path = "examples/image_gallery.rs" - -[[example]] -name = "image_loading" -path = "examples/image_loading.rs" - -[[example]] -name = "input" -path = "examples/input.rs" - -[[example]] -name = "layer_shell" -path = "examples/layer_shell.rs" - -[[example]] -name = "list_example" -path = "examples/list_example.rs" - -[[example]] -name = "mouse_pressure" -path = "examples/mouse_pressure.rs" - -[[example]] -name = "move_entity_between_windows" -path = "examples/move_entity_between_windows.rs" - -[[example]] -name = "on_window_close_quit" -path = "examples/on_window_close_quit.rs" - -[[example]] -name = "opacity" -path = "examples/opacity.rs" - -[[example]] -name = "ownership_post" -path = "examples/ownership_post.rs" - -[[example]] -name = "painting" -path = "examples/painting.rs" - -[[example]] -name = "paths_bench" -path = "examples/paths_bench.rs" - -[[example]] -name = "pattern" -path = "examples/pattern.rs" - -[[example]] -name = "popover" -path = "examples/popover.rs" - -[[example]] -name = "scrollable" -path = "examples/scrollable.rs" - -[[example]] -name = "set_menus" -path = "examples/set_menus.rs" - -[[example]] -name = "shadow" -path = "examples/shadow.rs" - -[[example]] -name = "svg" -path = "examples/svg/svg.rs" - -[[example]] -name = "system_notifications" -path = "examples/system_notifications.rs" - -[[example]] -name = "tab_stop" -path = "examples/tab_stop.rs" - -[[example]] -name = "testing" -path = "examples/testing.rs" - -[[example]] -name = "text" -path = "examples/text.rs" - -[[example]] -name = "text_layout" -path = "examples/text_layout.rs" - -[[example]] -name = "text_wrapper" -path = "examples/text_wrapper.rs" - -[[example]] -name = "tree" -path = "examples/tree.rs" - -[[example]] -name = "uniform_list" -path = "examples/uniform_list.rs" - -[[example]] -name = "view_example" -path = "examples/view_example/view_example_main.rs" - -[[example]] -name = "window" -path = "examples/window.rs" - -[[example]] -name = "window_movable" -path = "examples/window_movable.rs" - -[[example]] -name = "window_positioning" -path = "examples/window_positioning.rs" - -[[example]] -name = "window_shadow" -path = "examples/window_shadow.rs" - -[[test]] -name = "action_macros" -path = "tests/action_macros.rs" - -[dependencies.accesskit] -version = "0.24.0" -features = ["enumn"] - -[dependencies.anyhow] -version = "1.0.86" - -[dependencies.async-channel] -version = "2.5.0" - -[dependencies.async-task] -version = "4.7" - -[dependencies.backtrace] -version = "0.3" -optional = true - -[dependencies.bitflags] -version = "2.6.0" - -[dependencies.chrono] -version = "0.4" -features = ["serde"] - -[dependencies.collections] -version = "=0.3.3" -package = "gpui-pre-collections" - -[dependencies.criterion] -version = "0.5" -features = ["html_reports"] -optional = true - -[dependencies.ctor] -version = "1.0.12" - -[dependencies.derive_more] -version = "2.1.1" -features = [ - "add", - "add_assign", - "deref", - "deref_mut", - "display", - "from", - "from_str", - "mul", - "mul_assign", - "not", -] - -[dependencies.etagere] -version = "0.2" - -[dependencies.futures] -version = "0.3.32" - -[dependencies.futures-concurrency] -version = "7.7.1" - -[dependencies.gpui_macros] -version = "=0.3.3" -package = "gpui-pre-macros" - -[dependencies.gpui_shared_string] -version = "=0.3.3" -package = "gpui-pre-shared-string" - -[dependencies.gpui_util] -version = "=0.3.3" -package = "gpui-pre-util" - -[dependencies.hdrhistogram] -version = "7" -optional = true -default-features = false - -[dependencies.heapless] -version = "0.9.2" - -[dependencies.http_client] -version = "=0.3.3" -package = "gpui-pre-http-client" - -[dependencies.image] -version = "0.25.1" -features = [ - "bmp", - "dds", - "exr", - "ff", - "gif", - "hdr", - "ico", - "jpeg", - "png", - "pnm", - "qoi", - "rayon", - "tga", - "tiff", - "webp", -] -default-features = false - -[dependencies.inventory] -version = "0.3.19" - -[dependencies.itertools] -version = "0.14.0" - -[dependencies.log] -version = "0.4.16" -features = [ - "kv_unstable_serde", - "serde", -] - -[dependencies.lyon] -version = "1.0" - -[dependencies.num_cpus] -version = "1.13" - -[dependencies.parking] -version = "2.0.0" - -[dependencies.parking_lot] -version = "0.12.1" - -[dependencies.pin-project] -version = "1.1.10" - -[dependencies.pollster] -version = "0.4.0" - -[dependencies.postage] -version = "0.5" -features = ["futures-traits"] - -[dependencies.profiling] -version = "1" - -[dependencies.proptest] -version = "1" -features = ["attr-macro"] -optional = true -package = "proptest" - -[dependencies.rand] -version = "0.9" - -[dependencies.raw-window-handle] -version = "0.6" - -[dependencies.refineable] -version = "=0.3.3" -package = "gpui-pre-refineable" - -[dependencies.regex] -version = "1.5" - -[dependencies.resvg] -version = "0.46.0" -features = [ - "text", - "system-fonts", - "memmap-fonts", - "raster-images", -] -default-features = false - -[dependencies.scheduler] -version = "=0.3.3" -package = "gpui-pre-scheduler" - -[dependencies.schemars] -version = "1.0" -features = ["indexmap2"] - -[dependencies.seahash] -version = "4.1" - -[dependencies.serde] -version = "1.0.221" -features = [ - "derive", - "rc", -] - -[dependencies.serde_json] -version = "1.0.144" -features = [ - "preserve_order", - "raw_value", -] - -[dependencies.slotmap] -version = "1.0.6" - -[dependencies.smallvec] -version = "1.6" -features = [ - "union", - "const_new", -] - -[dependencies.spin] -version = "0.10.0" - -[dependencies.stacksafe] -version = "1.0" -optional = true - -[dependencies.strum] -version = "0.28" -features = ["derive"] - -[dependencies.sum_tree] -version = "=0.3.3" -package = "gpui-pre-sum-tree" - -[dependencies.taffy] -version = "=0.13.0" - -[dependencies.thiserror] -version = "2.0.12" - -[dependencies.tracing] -version = "0.1.40" - -[dependencies.ttf-parser] -version = "0.25" - -[dependencies.url] -version = "2.2" - -[dependencies.usvg] -version = "0.46.0" -default-features = false - -[dependencies.util_macros] -version = "=0.3.3" -package = "gpui-pre-util-macros" - -[dependencies.uuid] -version = "1.1.2" -features = [ - "v4", - "v5", - "v7", - "serde", -] - -[dependencies.waker-fn] -version = "1.2.0" - -[dependencies.web-time] -version = "1.1.0" - -[dependencies.ztracing] -version = "=0.3.3" -package = "gpui-pre-ztracing" - -[build-dependencies.embed-resource] -version = "3.0" -optional = true - -[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "windows"))'.dependencies.scap] -version = "0.0.8-zed" -optional = true -default-features = false -package = "zed-scap" - -[target.'cfg(target_family = "wasm")'.dependencies.getrandom] -version = "0.3.4" -features = ["wasm_js"] - -[target.'cfg(target_family = "wasm")'.dependencies.uuid] -version = "1.1.2" -features = [ - "v4", - "v5", - "v7", - "serde", - "js", -] - -[target.'cfg(target_os = "macos")'.dependencies.core-video] -version = "0.5.2" -features = ["metal"] - -[target.'cfg(target_os = "macos")'.dependencies.font-kit] -version = "0.14.1-zed" -optional = true -package = "zed-font-kit" - -[target.'cfg(target_os = "macos")'.dependencies.log] -version = "0.4.16" -features = [ - "kv_unstable_serde", - "serde", -] - -[target.'cfg(target_os = "macos")'.dependencies.objc2] -version = "0.6" -optional = true - -[target.'cfg(target_os = "macos")'.dependencies.objc2-metal] -version = "0.3" -optional = true - -[target.'cfg(target_os = "macos")'.build-dependencies.bindgen] -version = "0.72" - -[target.'cfg(target_os = "windows")'.dependencies.windows] -version = "0.62" -features = [ - "Data_Xml_Dom", - "Foundation_Numerics", - "Globalization_DateTimeFormatting", - "Storage_Search", - "Storage_Streams", - "System_Threading", - "UI_Notifications", - "UI_ViewManagement", - "Wdk_System_SystemServices", - "Win32_Foundation", - "Win32_Globalization", - "Win32_Graphics_Direct3D", - "Win32_Graphics_Direct3D11", - "Win32_Graphics_Direct3D_Fxc", - "Win32_Graphics_DirectComposition", - "Win32_Graphics_DirectWrite", - "Win32_Graphics_DirectManipulation", - "Win32_Graphics_Dwm", - "Win32_Graphics_Dxgi", - "Win32_Graphics_Dxgi_Common", - "Win32_Graphics_Gdi", - "Win32_Graphics_Imaging", - "Win32_Graphics_Hlsl", - "Win32_Networking_WinSock", - "Win32_Security", - "Win32_Security_Credentials", - "Win32_Security_Cryptography", - "Win32_Storage_FileSystem", - "Win32_Storage_Packaging_Appx", - "Win32_System_Com", - "Win32_System_Com_StructuredStorage", - "Win32_System_Console", - "Win32_System_Diagnostics_Debug", - "Win32_System_DataExchange", - "Win32_System_IO", - "Win32_System_JobObjects", - "Win32_System_LibraryLoader", - "Win32_System_Memory", - "Win32_System_Ole", - "Win32_System_Performance", - "Win32_System_Pipes", - "Win32_System_RestartManager", - "Win32_System_SystemInformation", - "Win32_System_SystemServices", - "Win32_System_Threading", - "Win32_System_Variant", - "Win32_System_WinRT", - "Win32_UI_Controls", - "Win32_UI_HiDpi", - "Win32_UI_Input_Ime", - "Win32_UI_Input_KeyboardAndMouse", - "Win32_UI_Input_Pointer", - "Win32_UI_Shell", - "Win32_UI_Shell_Common", - "Win32_UI_Shell_PropertiesSystem", - "Win32_UI_WindowsAndMessaging", - "Win32_Media", - "Win32_Foundation", - "Win32_System_Power", -] - -[lints.clippy] -dbg_macro = "deny" -declare_interior_mutable_const = "deny" -disallowed_methods = "deny" -large_enum_variant = "allow" -let_underscore_future = "allow" -nonminimal_bool = "allow" -redundant_clone = "deny" -single_range_in_vec_init = "allow" -todo = "deny" -too_many_arguments = "allow" -type_complexity = "allow" - -[lints.clippy.style] -level = "allow" -priority = -1 - -[lints.rust.unexpected_cfgs] -level = "allow" -priority = 0 - -[workspace] diff --git a/crates/gpui_pre/LICENSE-APACHE b/crates/gpui_pre/LICENSE-APACHE deleted file mode 100644 index 461a0fe5..00000000 --- a/crates/gpui_pre/LICENSE-APACHE +++ /dev/null @@ -1,222 +0,0 @@ -Copyright 2022 - 2025 Zed Industries, Inc. - - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - - http://www.apache.org/licenses/LICENSE-2.0 - - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - - -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - - 1. Definitions. - - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - - END OF TERMS AND CONDITIONS diff --git a/crates/gpui_pre/README.md b/crates/gpui_pre/README.md deleted file mode 100644 index 132e5ac1..00000000 --- a/crates/gpui_pre/README.md +++ /dev/null @@ -1,98 +0,0 @@ -# Welcome to GPUI! - -GPUI is a hybrid immediate and retained mode, GPU accelerated, UI framework -for Rust, designed to support a wide variety of applications. - -## Getting Started - -GPUI is still in active development as we work on the Zed code editor, and is still pre-1.0. There will often be breaking changes between versions. You'll also need to use the latest version of stable Rust. Add `gpui`, and optionally `gpui_platform`, to your `Cargo.toml`: - -```toml -gpui = { version = "*" } -gpui_platform = { version = "*", features = ["font-kit", "wayland", "x11"] } -``` - -Everything in a standalone GPUI app starts with an `Application`. You can create one with `gpui_platform::application()`, which picks the windowing and text backends for the host OS, and kick off your application by passing a callback to `Application::run()`. Inside this callback, you can create a new window with `App::open_window()` and register your first root view. - -```rust,no_run -use gpui::*; - -fn main() { - gpui_platform::application().run(|cx: &mut App| { - // .. - }); -} -``` - -### `gpui_platform` - -The features on `gpui_platform` are platform-specific, so the list above is a safe cross-platform default. If you build for a single platform, you can trim it: - -- **macOS** — Rendering uses Metal and is always available, but glyph rasterization needs `font-kit`. Without it, GPUI falls back to a placeholder text system that lays text out but renders no glyphs. - - ```toml - gpui_platform = { version = "*", features = ["font-kit"] } - ``` - -- **Linux / FreeBSD** — enable at least one windowing backend for desktop windows: `wayland`, `x11`, or both. These features also compile the renderer and text system, so no separate text feature is needed. - - ```toml - gpui_platform = { version = "*", features = ["wayland", "x11"] } - ``` - -- **Windows** — no features are required. Windowing uses Win32 and text uses DirectWrite. `font-kit` has no effect here. - -### Additional Topics - -- [Ownership and data flow](_ownership_and_data_flow) -- [Accessibility](_accessibility) - -### Dependencies - -GPUI has various system dependencies that it needs in order to work. - -#### macOS - -On macOS, GPUI uses Metal for rendering. In order to use Metal, you need to do the following: - -- Install [Xcode](https://apps.apple.com/us/app/xcode/id497799835?mt=12) from the macOS App Store, or from the [Apple Developer](https://developer.apple.com/download/all/) website. Note this requires a developer account. - -> Ensure you launch Xcode after installing, and install the macOS components, which is the default option. - -- Install [Xcode command line tools](https://developer.apple.com/xcode/resources/) - - ```sh - xcode-select --install - ``` - -- Ensure that the Xcode command line tools are using your newly installed copy of Xcode: - - ```sh - sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer - ``` - -## The Big Picture - -GPUI offers three different [registers]() depending on your needs: - -- State management and communication with `Entity`'s. Whenever you need to store application state that communicates between different parts of your application, you'll want to use GPUI's entities. Entities are owned by GPUI and are only accessible through an owned smart pointer similar to an `Rc`. See the `app::context` module for more information. - -- High level, declarative UI with views. All UI in GPUI starts with a view. A view is simply an `Entity` that can be rendered, by implementing the `Render` trait. At the start of each frame, GPUI will call this render method on the root view of a given window. Views build a tree of `elements`, lay them out and style them with a tailwind-style API, and then give them to GPUI to turn into pixels. See the `div` element for an all purpose swiss-army knife of rendering. - -- Low level, imperative UI with Elements. Elements are the building blocks of UI in GPUI, and they provide a nice wrapper around an imperative API that provides as much flexibility and control as you need. Elements have total control over how they and their child elements are rendered and can be used for making efficient views into large lists, implement custom layouting for a code editor, and anything else you can think of. See the `element` module for more information. - -Each of these registers has one or more corresponding contexts that can be accessed from all GPUI services. This context is your main interface to GPUI, and is used extensively throughout the framework. - -## Other Resources - -In addition to the systems above, GPUI provides a range of smaller services that are useful for building complex applications: - -- Actions are user-defined structs that are used for converting keystrokes into logical operations in your UI. Use this for implementing keyboard shortcuts, such as cmd-q. See the `action` module for more information. - -- Platform services, such as `quit the app` or `open a URL` are available as methods on the `app::App`. - -- An async executor that is integrated with the platform's event loop. See the `executor` module for more information., - -- The `[gpui::test]` macro provides a convenient way to write tests for your GPUI applications. Tests also have their own kind of context, a `TestAppContext` which provides ways of simulating common platform input. See `app::test_context` and `test` modules for more details. - -Currently, the best way to learn about these APIs is to read the Zed source code or drop a question in the [Zed Discord](https://zed.dev/community-links). We're working on improving the documentation, creating more examples, and will be publishing more guides to GPUI on our [blog](https://zed.dev/blog). diff --git a/crates/gpui_pre/build.rs b/crates/gpui_pre/build.rs deleted file mode 100644 index b1bfd219..00000000 --- a/crates/gpui_pre/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")] - -fn main() { - println!("cargo::rustc-check-cfg=cfg(gles)"); - - let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); - - if target_os == "windows" { - #[cfg(feature = "windows-manifest")] - embed_resource(); - } -} - -#[cfg(feature = "windows-manifest")] -fn embed_resource() { - let manifest = std::path::Path::new("resources/windows/gpui.manifest.xml"); - let rc_file = std::path::Path::new("resources/windows/gpui.rc"); - println!("cargo:rerun-if-changed={}", manifest.display()); - println!("cargo:rerun-if-changed={}", rc_file.display()); - embed_resource::compile(rc_file, embed_resource::NONE) - .manifest_required() - .unwrap(); -} diff --git a/crates/gpui_pre/docs/contexts.md b/crates/gpui_pre/docs/contexts.md deleted file mode 100644 index 9295c775..00000000 --- a/crates/gpui_pre/docs/contexts.md +++ /dev/null @@ -1,33 +0,0 @@ -# Contexts - -GPUI makes extensive use of _context parameters_ (typically named `cx`) to provide access to application state and services. These contexts are references passed to functions, enabling interaction with global state, windows, entities, and system services. - ---- - -## `App` - -The root context granting access to the application's global state. This context owns all entities' data and can be used to read or update the data referenced by an `Entity`. - -## `Context` - -A context provided when interacting with an `Entity`, with additional methods related to that specific entity such as notifying observers and emitting events. This context dereferences into `App`, meaning any function which can take an `App` reference can also take a `Context` reference, allowing you to access the application's global state. - -## `AsyncApp` and `AsyncWindowContext` - -Whereas the above contexts are always passed to your code as references, you can call `to_async` on the reference to create an async context, which has a static lifetime and can be held across `await` points in async code. When you interact with entities with an async context, the calls become fallible, because the context may outlive the window or even the app itself. - -## `TestAppContext` - -These are similar to the async contexts above, but they panic if you attempt to access a non-existent app or window, and they also contain other features specific to tests. - ---- - -# Non-Context Core Types - -## `Window` - -Provides access to the state of an application window. This type has a root view (an `Entity` implementing `Render`) which it can read/update, but since it is not a context, you must pass a `&mut App` (or a context which dereferences to it) to do so, along with other functions interacting with global state. You can obtain a `Window` from an `WindowHandle` by calling `WindowHandle::update`. - -## `Entity` - -A handle to a structure requiring state. This data is owned by the `App` and can be accessed and modified via references to contexts. If `T` implements `Render`, then the entity is sometimes referred to as a view. Entities can be observed by other entities and windows, allowing a closure to be called when `notify` is called on the entity's `Context`. diff --git a/crates/gpui_pre/docs/key_dispatch.md b/crates/gpui_pre/docs/key_dispatch.md deleted file mode 100644 index ae7d828a..00000000 --- a/crates/gpui_pre/docs/key_dispatch.md +++ /dev/null @@ -1,100 +0,0 @@ -# Key Dispatch - -GPUI is designed for keyboard-first interactivity. - -To expose functionality to the mouse, you render a button with a click handler. - -To expose functionality to the keyboard, you bind an _action_ in a _key context_. - -Actions are similar to framework-level events like `MouseDown`, `KeyDown`, etc, but you can define them yourself: - -```rust -mod menu { - #[gpui::action] - struct MoveUp; - - #[gpui::action] - struct MoveDown; -} -``` - -Actions are frequently unit structs, for which we have a macro. The above could also be written: - -```rust -mod menu { - actions!(gpui, [MoveUp, MoveDown]); -} -``` - -Actions can also be more complex types: - -```rust -mod menu { - #[gpui::action] - struct Move { - direction: Direction, - select: bool, - } -} -``` - -To bind actions, chain `on_action` on to your element: - -```rust -impl Render for Menu { - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .on_action(|this: &mut Menu, move: &MoveUp, window: &mut Window, cx: &mut Context| { - // ... - }) - .on_action(|this, move: &MoveDown, cx| { - // ... - }) - .children(unimplemented!()) - } -} -``` - -In order to bind keys to actions, you need to declare a _key context_ for part of the element tree by calling `key_context`. - -```rust -impl Render for Menu { - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .key_context("menu") - .on_action(|this: &mut Menu, move: &MoveUp, window: &mut Window, cx: &mut Context| { - // ... - }) - .on_action(|this, move: &MoveDown, cx| { - // ... - }) - .children(unimplemented!()) - } -} -``` - -Now you can target your context in the keymap. Note how actions are identified in the keymap by their fully-qualified type name. - -```json -{ - "context": "menu", - "bindings": { - "up": "menu::MoveUp", - "down": "menu::MoveDown" - } -} -``` - -If you had opted for the more complex type definition, you'd provide the serialized representation of the action alongside the name: - -```json -{ - "context": "menu", - "bindings": { - "up": ["menu::Move", {direction: "up", select: false}] - "down": ["menu::Move", {direction: "down", select: false}] - "shift-up": ["menu::Move", {direction: "up", select: true}] - "shift-down": ["menu::Move", {direction: "down", select: true}] - } -} -``` diff --git a/crates/gpui_pre/examples/README.md b/crates/gpui_pre/examples/README.md deleted file mode 100644 index dab1c302..00000000 --- a/crates/gpui_pre/examples/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# GPUI Examples - -Examples can be run from the Zed repository root: - -```sh -cargo run -p gpui --example hello_world -``` - -## Where to start - -- `hello_world` shows the basic shape of a GPUI application: create an - `Application`, open a window, create a root view, and render a `div`. -- `input` demonstrates text input, focus, selections, clipboard actions, and - keyboard bindings. -- `uniform_list` shows how to render a simple virtualized list. -- `testing` demonstrates `#[gpui::test]`, `TestAppContext`, actions, focus, and - window-based tests. - -## Layout and styling - -- `grid_layout` demonstrates CSS-grid-style layout. -- `opacity` demonstrates opacity styling. -- `pattern` shows patterned backgrounds. -- `shadow` demonstrates box shadows. -- `text` shows styled text rendering. -- `text_layout` demonstrates text alignment, decoration, weights, and wrapping. -- `text_wrapper` shows wrapping text content. - -## Interaction - -- `anchor` demonstrates anchored positioning. -- `data_table` combines virtualized list rendering with table-style rows and a - custom scrollbar. -- `drag_drop` shows draggable elements and drop targets. -- `focus_visible` demonstrates keyboard-visible focus styling. -- `mouse_pressure` demonstrates pressure-sensitive pointer input where supported. -- `popover` shows floating layers with `deferred` and `anchored`. -- `scrollable` demonstrates scrollable content. -- `tab_stop` shows keyboard tab navigation. - -## Images, drawing, and animation - -- `animation` demonstrates GPUI animations and animated SVG transforms. -- `gif_viewer` shows GIF rendering. -- `gradient` demonstrates linear gradients and color spaces. -- `image` shows local and remote image loading, image sizing, and asset setup. -- `image_gallery` demonstrates image caching and loading remote images. -- `image_loading` shows image loading states and asset loading. -- `painting` demonstrates custom drawing with paths and canvas. -- `svg` shows SVG rendering. - -## Windows and application behavior - -- `move_entity_between_windows` shows moving an entity between windows. -- `on_window_close_quit` demonstrates quitting when a window closes. -- `set_menus` shows application menu setup. -- `system_notifications` demonstrates posting, replacing, dismissing, and responding to operating-system notifications. -- `window` demonstrates creating normal, dialog, popup, and floating windows. -- `window_positioning` demonstrates window bounds and placement. -- `window_shadow` demonstrates window shadow styling. - -## Specialized examples - -These examples are useful when working on GPUI itself, but they may not be the -best starting point for new applications: - -- `active_state_bug` is a focused active-state reproduction. -- `layer_shell` demonstrates Linux layer-shell windows. -- `list_example` demonstrates bottom-aligned list state and scrollbar behavior. -- `ownership_post` supports the ownership and data-flow documentation. -- `paths_bench` is a path rendering benchmark. -- `tree` renders a deep tree of nested elements. diff --git a/crates/gpui_pre/examples/a11y.rs b/crates/gpui_pre/examples/a11y.rs deleted file mode 100644 index ff389f33..00000000 --- a/crates/gpui_pre/examples/a11y.rs +++ /dev/null @@ -1,272 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -//! Accessibility (AccessKit) demo app. -//! -//! Run with: `cargo run -p gpui --example a11y` -//! -//! Or on Linux: `cargo run -p gpui --features gpui_platform/wayland,gpui_platform/x11 --example a11y` -//! -//! This app uses GPUI's accessibility APIs to attach structured information to -//! the element tree, which allows assistive technology to see and interact with -//! the UI programmatically. -//! -//! The app behaves as follows: -//! - It opens a single window. -//! - The window's title is "GPUI Accessibility Demo". -//! - The window has a sequence of UI elements, stacked vertically: -//! - A heading with the text "Accessibility Demo". -//! - A row containing two elements: -//! - A spin button (role `SpinButton`) labelled "Counter: ", where -//! `` is the current count. It supports `Increment` and `Decrement` -//! accessible actions, and also increments on click. The numeric value -//! is clamped to a minimum of 0. -//! - A button labelled "Reset counter" that resets the count to 0. -//! - A row containing two elements: -//! - A switch, that can be toggled, and starts disabled. Toggling the switch -//! does nothing. -//! - The text "Enable feature". -//! - A "to-do" list, with three items, each represented with a `Text` element: -//! - "1. Write code" -//! - "2. Run tests" -//! - "3. Ship it" - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - AccessibleAction, App, Bounds, Context, FocusHandle, KeyBinding, Role, SharedString, Toggled, - Window, WindowBounds, WindowOptions, actions, div, prelude::*, px, rgb, size, text, -}; -use gpui_platform::application; - -actions!(a11y_example, [Tab, TabPrev]); - -struct A11yDemo { - focus_handle: FocusHandle, - count: i32, - enabled: bool, -} - -impl A11yDemo { - fn new(window: &mut Window, cx: &mut Context) -> Self { - let focus_handle = cx.focus_handle(); - window.focus(&focus_handle, cx); - Self { - focus_handle, - count: 0, - enabled: false, - } - } -} - -impl Render for A11yDemo { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .id("root") - .role(Role::Application) - .aria_label("Accessibility Demo") - .track_focus(&self.focus_handle) - .on_action(cx.listener(|_, _: &Tab, window, cx| window.focus_next(cx))) - .on_action(cx.listener(|_, _: &TabPrev, window, cx| window.focus_prev(cx))) - .size_full() - .flex() - .flex_col() - .gap_4() - .p_4() - .bg(rgb(0x1e1e2e)) - .text_color(rgb(0xcdd6f4)) - // Heading - .child( - div() - .id("heading") - .role(Role::Heading) - .aria_level(1) - .aria_label("Accessibility Demo") - .text_xl() - .font_weight(gpui::FontWeight::BOLD) - .child(text!("Accessibility Demo")), - ) - // Counter — uses a SpinButton role with Increment/Decrement - // actions so screen readers can adjust the value directly. - // Click also works via the built-in handler. - .child( - div() - .flex() - .items_center() - .gap_3() - .child( - div() - .id("counter") - .focusable() - .tab_stop(true) - .role(Role::SpinButton) - .aria_label(SharedString::from(format!("Counter: {}", self.count))) - .aria_numeric_value(self.count as f64) - .aria_min_numeric_value(0.0) - .on_a11y_action(AccessibleAction::Increment, { - let this = cx.entity().downgrade(); - move |_, _, cx| { - this.update(cx, |this, cx| { - this.count += 1; - cx.notify(); - }) - .ok(); - } - }) - .on_a11y_action(AccessibleAction::Decrement, { - let this = cx.entity().downgrade(); - move |_, _, cx| { - this.update(cx, |this, cx| { - this.count = (this.count - 1).max(0); - cx.notify(); - }) - .ok(); - } - }) - .on_click(cx.listener(|this, _, _, cx| { - this.count += 1; - cx.notify(); - })) - .px_3() - .py_1() - .rounded_md() - .bg(rgb(0x89b4fa)) - .text_color(rgb(0x1e1e2e)) - .cursor_pointer() - .child(text!(format!("Count: {}", self.count))), - ) - .child( - div() - .id("reset") - .focusable() - .tab_stop(true) - .role(Role::Button) - .aria_label("Reset counter") - .px_3() - .py_1() - .rounded_md() - .bg(rgb(0x585b70)) - .cursor_pointer() - .on_click(cx.listener(|this, _, _, cx| { - this.count = 0; - cx.notify(); - })) - .child(text!("Reset")), - ), - ) - // A toggle switch - .child( - div() - .flex() - .items_center() - .gap_2() - .child( - div() - .id("toggle") - .focusable() - .tab_stop(true) - .role(Role::Switch) - .aria_label("Enable feature") - .aria_toggled(if self.enabled { - Toggled::True - } else { - Toggled::False - }) - .w(px(44.)) - .h(px(24.)) - .rounded_full() - .cursor_pointer() - .when(self.enabled, |el| el.bg(rgb(0x89b4fa))) - .when(!self.enabled, |el| el.bg(rgb(0x585b70))) - .child( - div() - .size(px(20.)) - .rounded_full() - .bg(gpui::white()) - .mt(px(2.)) - .when(self.enabled, |el| el.ml(px(22.))) - .when(!self.enabled, |el| el.ml(px(2.))), - ) - .on_click(cx.listener(|this, _, _, cx| { - this.enabled = !this.enabled; - cx.notify(); - })), - ) - .child(text!("Enable feature")), - ) - // A short list - .child( - div() - .id("task-list") - .role(Role::List) - .aria_label("Tasks") - .flex() - .flex_col() - .gap_1() - .children( - ["Write code", "Run tests", "Ship it"] - .iter() - .enumerate() - .map(|(i, label)| { - div() - .id(("task", i)) - .role(Role::ListItem) - .aria_label(SharedString::from(*label)) - .aria_position_in_set(i + 1) - .aria_size_of_set(3) - .py_1() - .px_2() - // Note: even though this `text!` macro - // produces multiple elements, it doesn't - // need its own unique ID because the parent - // div has different IDs for each string. - .child(text!(format!("{}. {}", i + 1, label))) - }), - ), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - cx.bind_keys([ - KeyBinding::new("tab", Tab, None), - KeyBinding::new("shift-tab", TabPrev, None), - ]); - - let bounds = Bounds::centered(None, size(px(500.), px(400.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - titlebar: Some(gpui::TitlebarOptions { - title: Some("GPUI Accessibility Demo".into()), - ..Default::default() - }), - ..Default::default() - }, - |window, cx| cx.new(|cx| A11yDemo::new(window, cx)), - ) - .unwrap(); - - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - env_logger::builder() - .filter_level(log::LevelFilter::Warn) - .filter_module("gpui", log::LevelFilter::Info) - .init(); - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/active_state_bug.rs b/crates/gpui_pre/examples/active_state_bug.rs deleted file mode 100644 index f767ed27..00000000 --- a/crates/gpui_pre/examples/active_state_bug.rs +++ /dev/null @@ -1,47 +0,0 @@ -/// Click the button — the `.active()` background gets stuck on every other click. -use gpui::*; -use gpui_platform::application; - -struct Example; - -impl Render for Example { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - // Colors from Zed's default dark theme - let bg = hsla(215. / 360., 0.12, 0.15, 1.); - let text = hsla(221. / 360., 0.11, 0.86, 1.); - let hover = hsla(225. / 360., 0.118, 0.267, 1.); - let active = hsla(220. / 360., 0.118, 0.20, 1.); - - div().bg(bg).size_full().p_1().child( - div() - .id("button") - .px_2() - .py_0p5() - .rounded_md() - .text_sm() - .text_color(text) - .hover(|s| s.bg(hover)) - .active(|s| s.bg(active)) - .on_click(|_, _, _| {}) - .child("Click me"), - ) - } -} - -fn main() { - application().run(|cx: &mut App| { - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(Bounds::centered( - None, - size(px(200.), px(60.)), - cx, - ))), - ..Default::default() - }, - |_, cx| cx.new(|_| Example), - ) - .unwrap(); - cx.activate(true); - }); -} diff --git a/crates/gpui_pre/examples/anchor.rs b/crates/gpui_pre/examples/anchor.rs deleted file mode 100644 index b4b280b7..00000000 --- a/crates/gpui_pre/examples/anchor.rs +++ /dev/null @@ -1,207 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - Anchor, AnchoredPositionMode, App, Axis, Bounds, Context, Half as _, InteractiveElement, - ParentElement, Pixels, Point, Render, SharedString, Size, Window, WindowBounds, WindowOptions, - anchored, deferred, div, point, prelude::*, px, rgb, size, -}; -use gpui_platform::application; - -struct AnchorDemo { - hovered_button: Option, -} - -struct ButtonDemo { - label: SharedString, - corner: Option, -} - -fn resolved_position(corner: Anchor, button_size: Size) -> Point { - let offset = Point { - x: px(0.), - y: -button_size.height, - }; - - offset - + match corner.other_side_along(Axis::Vertical) { - Anchor::TopLeft => point(px(0.0), px(0.0)), - Anchor::TopCenter => point(button_size.width.half(), px(0.0)), - Anchor::TopRight => point(button_size.width, px(0.0)), - Anchor::LeftCenter => point(button_size.width, button_size.height.half()), - Anchor::RightCenter => point(px(0.), button_size.height.half()), - Anchor::BottomLeft => point(px(0.0), button_size.height), - Anchor::BottomCenter => point(button_size.width / 2.0, button_size.height), - Anchor::BottomRight => point(button_size.width, button_size.height), - } -} - -impl AnchorDemo { - fn buttons() -> Vec { - vec![ - ButtonDemo { - label: "TopLeft".into(), - corner: Some(Anchor::TopLeft), - }, - ButtonDemo { - label: "TopCenter".into(), - corner: Some(Anchor::TopCenter), - }, - ButtonDemo { - label: "TopRight".into(), - corner: Some(Anchor::TopRight), - }, - ButtonDemo { - label: "LeftCenter".into(), - corner: Some(Anchor::LeftCenter), - }, - ButtonDemo { - label: "Center".into(), - corner: None, - }, - ButtonDemo { - label: "RightCenter".into(), - corner: Some(Anchor::RightCenter), - }, - ButtonDemo { - label: "BottomLeft".into(), - corner: Some(Anchor::BottomLeft), - }, - ButtonDemo { - label: "BottomCenter".into(), - corner: Some(Anchor::BottomCenter), - }, - ButtonDemo { - label: "BottomRight".into(), - corner: Some(Anchor::BottomRight), - }, - ] - } -} - -impl Render for AnchorDemo { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let buttons = Self::buttons(); - let button_size = size(px(120.0), px(65.0)); - - div() - .flex() - .flex_col() - .size_full() - .items_center() - .justify_center() - .bg(gpui::white()) - .gap_4() - .p_10() - .child("Popover with Anchor") - .child( - div() - .size_128() - .grid() - .grid_cols(3) - .gap_6() - .relative() - .children(buttons.iter().enumerate().map(|(index, button)| { - let is_hovered = self.hovered_button == Some(index); - let is_hoverable = button.corner.is_some(); - div() - .relative() - .child( - div() - .id(("button", index)) - .w(button_size.width) - .h(button_size.height) - .flex() - .items_center() - .justify_center() - .bg(gpui::white()) - .when(is_hoverable, |this| { - this.border_1() - .rounded_lg() - .border_color(gpui::black()) - .hover(|style| { - style.bg(gpui::black()).text_color(gpui::white()) - }) - .on_hover(cx.listener( - move |this, hovered, _window, cx| { - if *hovered { - this.hovered_button = Some(index); - } else if this.hovered_button == Some(index) { - this.hovered_button = None; - } - cx.notify(); - }, - )) - .child(button.label.clone()) - }), - ) - .when_some(self.hovered_button.filter(|_| is_hovered), |this, index| { - let button = &buttons[index]; - let Some(corner) = button.corner else { - return this; - }; - - let position = resolved_position(corner, button_size); - this.child(deferred( - anchored() - .anchor(corner) - .position(position) - .position_mode(AnchoredPositionMode::Local) - .snap_to_window() - .child( - div() - .py_0p5() - .px_2() - .bg(gpui::black().opacity(0.75)) - .text_color(rgb(0xffffff)) - .rounded_sm() - .shadow_sm() - .min_w(px(100.0)) - .text_sm() - .child(button.label.clone()), - ), - )) - }) - })), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(Bounds::centered( - None, - size(px(750.), px(600.)), - cx, - ))), - ..Default::default() - }, - |_, cx| { - cx.new(|_| AnchorDemo { - hovered_button: None, - }) - }, - ) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/animation.rs b/crates/gpui_pre/examples/animation.rs deleted file mode 100644 index e055b7da..00000000 --- a/crates/gpui_pre/examples/animation.rs +++ /dev/null @@ -1,373 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use std::time::Duration; - -use anyhow::Result; -use gpui::{ - Animation, AnimationExt as _, AnimationPhase, App, AssetSource, Bounds, Context, MouseButton, - MouseDownEvent, MouseMoveEvent, Pixels, SharedString, SpringAnimation, SpringConfig, - Transformation, Window, WindowBounds, WindowOptions, bounce, div, ease_in_out, percentage, - prelude::*, px, relative, rgba, size, svg, -}; -use gpui_platform::application; - -struct Assets {} - -impl AssetSource for Assets { - fn load(&self, path: &str) -> Result>> { - std::fs::read(path) - .map(Into::into) - .map_err(Into::into) - .map(Some) - } - - fn list(&self, path: &str) -> Result> { - Ok(std::fs::read_dir(path)? - .filter_map(|entry| { - Some(SharedString::from( - entry.ok()?.path().to_string_lossy().into_owned(), - )) - }) - .collect::>()) - } -} - -const ARROW_CIRCLE_SVG: &str = concat!( - env!("CARGO_MANIFEST_DIR"), - "/examples/image/arrow_circle.svg" -); - -struct AnimationExample { - spring_phase: u8, - spring_damping: f32, - spring_damping_drag: Option<(Pixels, f32)>, -} - -impl Render for AnimationExample { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - const MINIMUM_DAMPING: f32 = 2.0; - const MAXIMUM_DAMPING: f32 = 32.0; - const DAMPING_SLIDER_WIDTH: Pixels = px(224.0); - - let spring_phase = self.spring_phase; - let spring_position = px(98.0 * f32::from(spring_phase)); - let spring_damping = self.spring_damping; - let spring = SpringConfig::new(170.0, spring_damping, 1.0); - let (_, damping_ratio) = spring.canonical(); - let damping_fraction = - (spring_damping - MINIMUM_DAMPING) / (MAXIMUM_DAMPING - MINIMUM_DAMPING); - - div() - .flex() - .flex_col() - .size_full() - .bg(gpui::white()) - .text_color(gpui::black()) - .justify_around() - .child( - div() - .flex() - .flex_col() - .size_full() - .justify_around() - .child( - div() - .id("content") - .flex() - .flex_col() - .h(px(150.)) - .overflow_y_scroll() - .w_full() - .flex_1() - .justify_center() - .items_center() - .text_xl() - .gap_4() - .child("Hello Animation") - .child( - div() - .id("spring-demo") - .flex() - .flex_col() - .gap_2() - .p_2() - .w(px(240.0)) - .rounded_md() - .bg(rgba(0x00000010)) - .cursor_pointer() - .on_mouse_down( - MouseButton::Left, - cx.listener(|this, _, _, cx| { - this.spring_phase = (this.spring_phase + 1) % 3; - cx.notify(); - }), - ) - .child(format!( - "Target phase {spring_phase}: click rapidly to redirect momentum" - )) - .child( - div() - .id("spring-damping-controls") - .flex() - .flex_col() - .gap_1() - .text_sm() - .on_click(|_, _, cx| cx.stop_propagation()) - .child(format!( - "Drag damping: {spring_damping:.1} (ζ {damping_ratio:.2})" - )) - .child( - div() - .id("spring-damping") - .relative() - .h(px(20.0)) - .w(DAMPING_SLIDER_WIDTH) - .cursor_pointer() - .on_mouse_down( - MouseButton::Left, - cx.listener( - |this, - event: &MouseDownEvent, - _, - cx| { - this.spring_damping_drag = Some(( - event.position.x, - this.spring_damping, - )); - cx.stop_propagation(); - }, - ), - ) - .on_mouse_move(cx.listener( - |this, - event: &MouseMoveEvent, - _, - cx| { - let Some(( - start_position, - start_damping, - )) = this.spring_damping_drag - else { - return; - }; - - let delta = (event.position.x - - start_position) - / DAMPING_SLIDER_WIDTH; - this.spring_damping = (start_damping - + delta - * (MAXIMUM_DAMPING - - MINIMUM_DAMPING)) - .clamp( - MINIMUM_DAMPING, - MAXIMUM_DAMPING, - ); - cx.stop_propagation(); - cx.notify(); - }, - )) - .on_mouse_up( - MouseButton::Left, - cx.listener(|this, _, _, cx| { - this.spring_damping_drag = None; - cx.stop_propagation(); - }), - ) - .on_mouse_up_out( - MouseButton::Left, - cx.listener(|this, _, _, cx| { - this.spring_damping_drag = None; - cx.stop_propagation(); - }), - ) - .child( - div() - .absolute() - .top(px(8.0)) - .h(px(4.0)) - .w_full() - .rounded_full() - .bg(rgba(0x00000030)), - ) - .child( - div() - .absolute() - .top(px(8.0)) - .h(px(4.0)) - .w(relative(damping_fraction)) - .rounded_full() - .bg(rgba(0x3b82f6ff)), - ) - .child( - div() - .absolute() - .top(px(3.0)) - .left(relative(damping_fraction)) - .ml(px(-7.0)) - .size(px(14.0)) - .rounded_full() - .bg(rgba(0x2563ebff)), - ), - ), - ) - .child( - div().relative().h(px(32.0)).w_full().child( - div() - .absolute() - .top_1() - .size(px(24.0)) - .rounded_full() - .bg(rgba(0x3b82f6ff)) - .cursor_pointer() - .debug_selector(|| "spring-position".into()) - .with_spring( - "spring-position", - SpringAnimation::new(spring) - .to(spring_position) - .with_epsilon(0.25), - |this, left| this.left(left), - ), - ), - ) - .child( - div().h(px(24.0)).rounded_sm().with_spring( - "spring-phase", - SpringAnimation::new(spring) - .to(AnimationPhase(f32::from(spring_phase))) - .with_epsilon(0.001), - |this, phase| { - let (width, color) = if phase.0 <= 1.0 { - ( - phase.interpolate_between( - 0.0..=1.0, - px(48.0), - px(224.0), - ), - phase.interpolate_between_clamped( - 0.0..=1.0, - rgba(0xf97316ff), - rgba(0x22c55eff), - ), - ) - } else { - ( - phase.interpolate_between( - 1.0..=2.0, - px(224.0), - px(96.0), - ), - phase.interpolate_between_clamped( - 1.0..=2.0, - rgba(0x22c55eff), - rgba(0xa855f7ff), - ), - ) - }; - this.w(width).bg(color) - }, - ), - ), - ) - .child( - svg() - .size_20() - .overflow_hidden() - .path(ARROW_CIRCLE_SVG) - .text_color(gpui::black()) - .with_animation( - "image_circle", - Animation::new(Duration::from_secs(2)) - .repeat() - .with_easing(bounce(ease_in_out)), - |svg, delta| { - svg.with_transformation(Transformation::rotate( - percentage(delta), - )) - }, - ), - ), - ) - .child( - div() - .flex() - .h(px(64.)) - .w_full() - .p_2() - .justify_center() - .items_center() - .border_t_1() - .border_color(gpui::black().opacity(0.1)) - .bg(gpui::black().opacity(0.05)) - .child("Other Panel"), - ), - ) - } -} - -fn run_example() { - application().with_assets(Assets {}).run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let options = WindowOptions { - window_bounds: Some(WindowBounds::Windowed(Bounds::centered( - None, - size(px(300.), px(300.)), - cx, - ))), - ..Default::default() - }; - cx.open_window(options, |_, cx| { - cx.activate(false); - cx.new(|_| AnimationExample { - spring_phase: 0, - spring_damping: 14.0, - spring_damping_drag: None, - }) - }) - .unwrap(); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} - -#[cfg(all(test, feature = "test-support"))] -mod tests { - use gpui::{Modifiers, TestAppContext}; - - use super::*; - - #[gpui::test] - fn clicking_spring_position_changes_the_target(cx: &mut TestAppContext) { - let (view, cx) = cx.add_window_view(|_, _| AnimationExample { - spring_phase: 0, - spring_damping: 14.0, - spring_damping_drag: None, - }); - cx.simulate_resize(size(px(300.0), px(300.0))); - cx.run_until_parked(); - - let position_bounds = cx - .debug_bounds("spring-position") - .expect("spring position should be rendered"); - let position = position_bounds.center(); - cx.simulate_mouse_move(position, None, Modifiers::default()); - cx.simulate_click(position, Modifiers::default()); - cx.run_until_parked(); - - assert_eq!(cx.update(|_, cx| view.read(cx).spring_phase), 1); - } -} diff --git a/crates/gpui_pre/examples/data_table.rs b/crates/gpui_pre/examples/data_table.rs deleted file mode 100644 index 2bee4763..00000000 --- a/crates/gpui_pre/examples/data_table.rs +++ /dev/null @@ -1,494 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use std::{ops::Range, rc::Rc, time::Duration}; - -use gpui::{ - App, Bounds, Context, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, Render, - SharedString, UniformListScrollHandle, Window, WindowBounds, WindowOptions, canvas, div, point, - prelude::*, px, rgb, size, uniform_list, -}; -use gpui_platform::application; - -const TOTAL_ITEMS: usize = 10000; -const SCROLLBAR_THUMB_WIDTH: Pixels = px(8.); -const SCROLLBAR_THUMB_HEIGHT: Pixels = px(100.); - -pub struct Quote { - name: SharedString, - symbol: SharedString, - last_done: f64, - prev_close: f64, - open: f64, - high: f64, - low: f64, - timestamp: Duration, - volume: i64, - turnover: f64, - ttm: f64, - market_cap: f64, - float_cap: f64, - shares: f64, - pb: f64, - pe: f64, - eps: f64, - dividend: f64, - dividend_yield: f64, - dividend_per_share: f64, - dividend_date: SharedString, - dividend_payment: f64, -} - -impl Quote { - pub fn random() -> Self { - use rand::Rng; - let mut rng = rand::rng(); - // simulate a base price in a realistic range - let prev_close = rng.random_range(100.0..200.0); - let change = rng.random_range(-5.0..5.0); - let last_done = prev_close + change; - let open = prev_close + rng.random_range(-3.0..3.0); - let high = (prev_close + rng.random_range::(0.0..10.0)).max(open); - let low = (prev_close - rng.random_range::(0.0..10.0)).min(open); - let timestamp = Duration::from_secs(rng.random_range(0..86400)); - let volume = rng.random_range(1_000_000..100_000_000); - let turnover = last_done * volume as f64; - let symbol = { - let mut ticker = String::new(); - if rng.random_bool(0.5) { - ticker.push_str(&format!( - "{:03}.{}", - rng.random_range(100..1000), - rng.random_range(0..10) - )); - } else { - ticker.push_str(&format!( - "{}{}", - rng.random_range('A'..='Z'), - rng.random_range('A'..='Z') - )); - } - ticker.push_str(&format!(".{}", rng.random_range('A'..='Z'))); - ticker - }; - let name = format!( - "{} {} - #{}", - symbol, - rng.random_range(1..100), - rng.random_range(10000..100000) - ); - let ttm = rng.random_range(0.0..10.0); - let market_cap = rng.random_range(1_000_000.0..10_000_000.0); - let float_cap = market_cap + rng.random_range(1_000.0..10_000.0); - let shares = rng.random_range(100.0..1000.0); - let pb = market_cap / shares; - let pe = market_cap / shares; - let eps = market_cap / shares; - let dividend = rng.random_range(0.0..10.0); - let dividend_yield = rng.random_range(0.0..10.0); - let dividend_per_share = rng.random_range(0.0..10.0); - let dividend_date = SharedString::new(format!( - "{}-{}-{}", - rng.random_range(2000..2023), - rng.random_range(1..12), - rng.random_range(1..28) - )); - let dividend_payment = rng.random_range(0.0..10.0); - - Self { - name: name.into(), - symbol: symbol.into(), - last_done, - prev_close, - open, - high, - low, - timestamp, - volume, - turnover, - pb, - pe, - eps, - ttm, - market_cap, - float_cap, - shares, - dividend, - dividend_yield, - dividend_per_share, - dividend_date, - dividend_payment, - } - } - - fn change(&self) -> f64 { - (self.last_done - self.prev_close) / self.prev_close * 100.0 - } - - fn change_color(&self) -> gpui::Hsla { - if self.change() > 0.0 { - gpui::green() - } else { - gpui::red() - } - } - - fn turnover_ratio(&self) -> f64 { - self.volume as f64 / self.turnover * 100.0 - } -} - -#[derive(IntoElement)] -struct TableRow { - ix: usize, - quote: Rc, -} -impl TableRow { - fn new(ix: usize, quote: Rc) -> Self { - Self { ix, quote } - } - - fn render_cell(&self, key: &str, width: Pixels, color: gpui::Hsla) -> impl IntoElement { - div() - .whitespace_nowrap() - .truncate() - .w(width) - .px_1() - .child(match key { - "id" => div().child(format!("{}", self.ix)), - "symbol" => div().child(self.quote.symbol.clone()), - "name" => div().child(self.quote.name.clone()), - "last_done" => div() - .text_color(color) - .child(format!("{:.3}", self.quote.last_done)), - "prev_close" => div() - .text_color(color) - .child(format!("{:.3}", self.quote.prev_close)), - "change" => div() - .text_color(color) - .child(format!("{:.2}%", self.quote.change())), - "timestamp" => div() - .text_color(color) - .child(format!("{:?}", self.quote.timestamp.as_secs())), - "open" => div() - .text_color(color) - .child(format!("{:.2}", self.quote.open)), - "low" => div() - .text_color(color) - .child(format!("{:.2}", self.quote.low)), - "high" => div() - .text_color(color) - .child(format!("{:.2}", self.quote.high)), - "ttm" => div() - .text_color(color) - .child(format!("{:.2}", self.quote.ttm)), - "eps" => div() - .text_color(color) - .child(format!("{:.2}", self.quote.eps)), - "market_cap" => { - div().child(format!("{:.2} M", self.quote.market_cap / 1_000_000.0)) - } - "float_cap" => div().child(format!("{:.2} M", self.quote.float_cap / 1_000_000.0)), - "turnover" => div().child(format!("{:.2} M", self.quote.turnover / 1_000_000.0)), - "volume" => div().child(format!("{:.2} M", self.quote.volume as f64 / 1_000_000.0)), - "turnover_ratio" => div().child(format!("{:.2}%", self.quote.turnover_ratio())), - "pe" => div().child(format!("{:.2}", self.quote.pe)), - "pb" => div().child(format!("{:.2}", self.quote.pb)), - "shares" => div().child(format!("{:.2}", self.quote.shares)), - "dividend" => div().child(format!("{:.2}", self.quote.dividend)), - "yield" => div().child(format!("{:.2}%", self.quote.dividend_yield)), - "dividend_per_share" => { - div().child(format!("{:.2}", self.quote.dividend_per_share)) - } - "dividend_date" => div().child(format!("{}", self.quote.dividend_date)), - "dividend_payment" => div().child(format!("{:.2}", self.quote.dividend_payment)), - _ => div().child("--"), - }) - } -} - -const FIELDS: [(&str, f32); 24] = [ - ("id", 64.), - ("symbol", 64.), - ("name", 180.), - ("last_done", 80.), - ("prev_close", 80.), - ("open", 80.), - ("low", 80.), - ("high", 80.), - ("ttm", 50.), - ("market_cap", 96.), - ("float_cap", 96.), - ("turnover", 120.), - ("volume", 100.), - ("turnover_ratio", 96.), - ("pe", 64.), - ("pb", 64.), - ("eps", 64.), - ("shares", 96.), - ("dividend", 64.), - ("yield", 64.), - ("dividend_per_share", 64.), - ("dividend_date", 96.), - ("dividend_payment", 64.), - ("timestamp", 120.), -]; - -impl RenderOnce for TableRow { - fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement { - let color = self.quote.change_color(); - div() - .flex() - .flex_row() - .border_b_1() - .border_color(rgb(0xE0E0E0)) - .bg(if self.ix.is_multiple_of(2) { - rgb(0xFFFFFF) - } else { - rgb(0xFAFAFA) - }) - .py_0p5() - .px_2() - .children(FIELDS.map(|(key, width)| self.render_cell(key, px(width), color))) - } -} - -struct DataTable { - /// Use `Rc` to share the same quote data across multiple items, avoid cloning. - quotes: Vec>, - visible_range: Range, - scroll_handle: UniformListScrollHandle, - /// The position in thumb bounds when dragging start mouse down. - drag_position: Option>, -} - -impl DataTable { - fn new() -> Self { - Self { - quotes: Vec::new(), - visible_range: 0..0, - scroll_handle: UniformListScrollHandle::new(), - drag_position: None, - } - } - - fn generate(&mut self) { - self.quotes = (0..TOTAL_ITEMS).map(|_| Rc::new(Quote::random())).collect(); - } - - fn table_bounds(&self) -> Bounds { - self.scroll_handle.0.borrow().base_handle.bounds() - } - - fn scroll_top(&self) -> Pixels { - self.scroll_handle.0.borrow().base_handle.offset().y - } - - fn scroll_height(&self) -> Pixels { - self.scroll_handle - .0 - .borrow() - .last_item_size - .unwrap_or_default() - .contents - .height - } - - fn render_scrollbar(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { - let scroll_height = self.scroll_height(); - let table_bounds = self.table_bounds(); - let table_height = table_bounds.size.height; - if table_height == px(0.) { - return div().id("scrollbar"); - } - - let percentage = -self.scroll_top() / scroll_height; - let offset_top = (table_height * percentage).clamp( - px(4.), - (table_height - SCROLLBAR_THUMB_HEIGHT - px(4.)).max(px(4.)), - ); - let entity = cx.entity(); - let scroll_handle = self.scroll_handle.0.borrow().base_handle.clone(); - - div() - .id("scrollbar") - .absolute() - .top(offset_top) - .right_1() - .h(SCROLLBAR_THUMB_HEIGHT) - .w(SCROLLBAR_THUMB_WIDTH) - .bg(rgb(0xC0C0C0)) - .hover(|this| this.bg(rgb(0xA0A0A0))) - .rounded_lg() - .child( - canvas( - |_, _, _| (), - move |thumb_bounds, _, window, _| { - window.on_mouse_event({ - let entity = entity.clone(); - move |ev: &MouseDownEvent, _, _, cx| { - if !thumb_bounds.contains(&ev.position) { - return; - } - - entity.update(cx, |this, _| { - this.drag_position = Some( - ev.position - thumb_bounds.origin - table_bounds.origin, - ); - }) - } - }); - window.on_mouse_event({ - let entity = entity.clone(); - move |_: &MouseUpEvent, _, _, cx| { - entity.update(cx, |this, _| { - this.drag_position = None; - }) - } - }); - - window.on_mouse_event(move |ev: &MouseMoveEvent, _, _, cx| { - if !ev.dragging() { - return; - } - - let Some(drag_pos) = entity.read(cx).drag_position else { - return; - }; - - let inside_offset = drag_pos.y; - let percentage = ((ev.position.y - table_bounds.origin.y - + inside_offset) - / (table_bounds.size.height)) - .clamp(0., 1.); - - let offset_y = ((scroll_height - table_bounds.size.height) - * percentage) - .clamp(px(0.), scroll_height - SCROLLBAR_THUMB_HEIGHT); - scroll_handle.set_offset(point(px(0.), -offset_y)); - cx.notify(entity.entity_id()); - }) - }, - ) - .size_full(), - ) - } -} - -impl Render for DataTable { - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .bg(gpui::white()) - .text_sm() - .size_full() - .p_4() - .gap_2() - .flex() - .flex_col() - .child(format!( - "Total {} items, visible range: {:?}", - self.quotes.len(), - self.visible_range - )) - .child( - div() - .flex() - .flex_col() - .flex_1() - .overflow_hidden() - .border_1() - .border_color(rgb(0xE0E0E0)) - .rounded_sm() - .child( - div() - .flex() - .flex_row() - .w_full() - .overflow_hidden() - .border_b_1() - .border_color(rgb(0xE0E0E0)) - .text_color(rgb(0x555555)) - .bg(rgb(0xF0F0F0)) - .py_1() - .px_2() - .text_xs() - .children(FIELDS.map(|(key, width)| { - div() - .whitespace_nowrap() - .flex_shrink_0() - .truncate() - .px_1() - .w(px(width)) - .child(key.replace("_", " ").to_uppercase()) - })), - ) - .child( - div() - .relative() - .size_full() - .child( - uniform_list( - "items", - self.quotes.len(), - cx.processor(move |this, range: Range, _, _| { - this.visible_range = range.clone(); - let mut items = Vec::with_capacity(range.end - range.start); - for i in range { - if let Some(quote) = this.quotes.get(i) { - items.push(TableRow::new(i, quote.clone())); - } - } - items - }), - ) - .size_full() - .track_scroll(&self.scroll_handle), - ) - .child(self.render_scrollbar(window, cx)), - ), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - cx.open_window( - WindowOptions { - focus: true, - window_bounds: Some(WindowBounds::Windowed(Bounds::centered( - None, - size(px(1280.0), px(1000.0)), - cx, - ))), - ..Default::default() - }, - |_, cx| { - cx.new(|_| { - let mut table = DataTable::new(); - table.generate(); - table - }) - }, - ) - .unwrap(); - - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/drag_drop.rs b/crates/gpui_pre/examples/drag_drop.rs deleted file mode 100644 index 8f038ef3..00000000 --- a/crates/gpui_pre/examples/drag_drop.rs +++ /dev/null @@ -1,158 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, Half, Hsla, Pixels, Point, Window, WindowBounds, WindowOptions, div, - prelude::*, px, rgb, size, -}; -use gpui_platform::application; - -#[derive(Clone, Copy)] -struct DragInfo { - ix: usize, - color: Hsla, - position: Point, -} - -impl DragInfo { - fn new(ix: usize, color: Hsla) -> Self { - Self { - ix, - color, - position: Point::default(), - } - } - - fn position(mut self, pos: Point) -> Self { - self.position = pos; - self - } -} - -impl Render for DragInfo { - fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement { - let size = gpui::size(px(120.), px(50.)); - - div() - .pl(self.position.x - size.width.half()) - .pt(self.position.y - size.height.half()) - .child( - div() - .flex() - .justify_center() - .items_center() - .w(size.width) - .h(size.height) - .bg(self.color.opacity(0.5)) - .text_color(gpui::white()) - .text_xs() - .shadow_md() - .child(format!("Item {}", self.ix)), - ) - } -} - -struct DragDrop { - drop_on: Option, -} - -impl DragDrop { - fn new() -> Self { - Self { drop_on: None } - } -} - -impl Render for DragDrop { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let items = [gpui::blue(), gpui::red(), gpui::green()]; - - div() - .size_full() - .flex() - .flex_col() - .gap_5() - .bg(gpui::white()) - .justify_center() - .items_center() - .text_color(rgb(0x333333)) - .child(div().text_xl().text_center().child("Drop & Drop")) - .child( - div() - .w_full() - .mb_10() - .justify_center() - .flex() - .flex_row() - .gap_4() - .items_center() - .children(items.into_iter().enumerate().map(|(ix, color)| { - let drag_info = DragInfo::new(ix, color); - - div() - .id(("item", ix)) - .size_32() - .flex() - .justify_center() - .items_center() - .border_2() - .border_color(color) - .text_color(color) - .cursor_move() - .hover(|this| this.bg(color.opacity(0.2))) - .child(format!("Item ({})", ix)) - .on_drag(drag_info, |info: &DragInfo, position, _, cx| { - cx.new(|_| info.position(position)) - }) - })), - ) - .child( - div() - .id("drop-target") - .w_128() - .h_32() - .flex() - .justify_center() - .items_center() - .border_3() - .border_color(self.drop_on.map(|info| info.color).unwrap_or(gpui::black())) - .when_some(self.drop_on, |this, info| this.bg(info.color.opacity(0.5))) - .on_drop(cx.listener(|this, info: &DragInfo, _, _| { - this.drop_on = Some(*info); - })) - .child("Drop items here"), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(800.), px(600.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| cx.new(|_| DragDrop::new()), - ) - .unwrap(); - - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/example_support/fonts.rs b/crates/gpui_pre/examples/example_support/fonts.rs deleted file mode 100644 index 39c3be19..00000000 --- a/crates/gpui_pre/examples/example_support/fonts.rs +++ /dev/null @@ -1,44 +0,0 @@ -#[cfg(target_family = "wasm")] -use std::borrow::Cow; - -use gpui::App; - -#[cfg(target_family = "wasm")] -pub fn load_fonts(cx: &App) -> bool { - let fonts = [ - Cow::Borrowed( - include_bytes!("../../../../assets/fonts/ibm-plex-sans/IBMPlexSans-Regular.ttf") - .as_slice(), - ), - Cow::Borrowed( - include_bytes!("../../../../assets/fonts/ibm-plex-sans/IBMPlexSans-Italic.ttf") - .as_slice(), - ), - Cow::Borrowed( - include_bytes!("../../../../assets/fonts/ibm-plex-sans/IBMPlexSans-SemiBold.ttf") - .as_slice(), - ), - Cow::Borrowed( - include_bytes!("../../../../assets/fonts/ibm-plex-sans/IBMPlexSans-SemiBoldItalic.ttf") - .as_slice(), - ), - Cow::Borrowed( - include_bytes!("../../../../assets/fonts/lilex/Lilex-Regular.ttf").as_slice(), - ), - Cow::Borrowed(include_bytes!("../../../../assets/fonts/lilex/Lilex-Bold.ttf").as_slice()), - Cow::Borrowed(include_bytes!("../../../../assets/fonts/lilex/Lilex-Italic.ttf").as_slice()), - Cow::Borrowed( - include_bytes!("../../../../assets/fonts/lilex/Lilex-BoldItalic.ttf").as_slice(), - ), - ]; - if let Err(error) = cx.text_system().add_fonts(fonts.into()) { - web_sys::console::error_1(&format!("failed to load application fonts: {error:#}").into()); - return false; - } - true -} - -#[cfg(not(target_family = "wasm"))] -pub fn load_fonts(_cx: &App) -> bool { - true -} diff --git a/crates/gpui_pre/examples/focus_visible.rs b/crates/gpui_pre/examples/focus_visible.rs deleted file mode 100644 index c5b3d5df..00000000 --- a/crates/gpui_pre/examples/focus_visible.rs +++ /dev/null @@ -1,235 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, Div, ElementId, FocusHandle, KeyBinding, SharedString, Stateful, Window, - WindowBounds, WindowOptions, actions, div, prelude::*, px, size, -}; -use gpui_platform::application; - -actions!(example, [Tab, TabPrev, Quit]); - -struct Example { - focus_handle: FocusHandle, - items: Vec<(FocusHandle, &'static str)>, - message: SharedString, -} - -impl Example { - fn new(window: &mut Window, cx: &mut Context) -> Self { - let items = vec![ - ( - cx.focus_handle().tab_index(1).tab_stop(true), - "Button with .focus() - always shows border when focused", - ), - ( - cx.focus_handle().tab_index(2).tab_stop(true), - "Button with .focus_visible() - only shows border with keyboard", - ), - ( - cx.focus_handle().tab_index(3).tab_stop(true), - "Button with both .focus() and .focus_visible()", - ), - ]; - - let focus_handle = cx.focus_handle(); - window.focus(&focus_handle, cx); - - Self { - focus_handle, - items, - message: SharedString::from( - "Try clicking vs tabbing! Click shows no border, Tab shows border.", - ), - } - } - - fn on_tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context) { - window.focus_next(cx); - self.message = SharedString::from("Pressed Tab - focus-visible border should appear!"); - } - - fn on_tab_prev(&mut self, _: &TabPrev, window: &mut Window, cx: &mut Context) { - window.focus_prev(cx); - self.message = - SharedString::from("Pressed Shift-Tab - focus-visible border should appear!"); - } - - fn on_quit(&mut self, _: &Quit, _window: &mut Window, cx: &mut Context) { - cx.quit(); - } -} - -impl Render for Example { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - fn button_base(id: impl Into, label: &'static str) -> Stateful
{ - div() - .id(id) - .h_16() - .w_full() - .flex() - .justify_center() - .items_center() - .bg(gpui::rgb(0x2563eb)) - .text_color(gpui::white()) - .rounded_md() - .cursor_pointer() - .hover(|style| style.bg(gpui::rgb(0x1d4ed8))) - .child(label) - } - - div() - .id("app") - .track_focus(&self.focus_handle) - .on_action(cx.listener(Self::on_tab)) - .on_action(cx.listener(Self::on_tab_prev)) - .on_action(cx.listener(Self::on_quit)) - .size_full() - .flex() - .flex_col() - .p_8() - .gap_6() - .bg(gpui::rgb(0xf3f4f6)) - .child( - div() - .text_2xl() - .font_weight(gpui::FontWeight::BOLD) - .text_color(gpui::rgb(0x111827)) - .child("CSS focus-visible Demo"), - ) - .child( - div() - .p_4() - .rounded_md() - .bg(gpui::rgb(0xdbeafe)) - .text_color(gpui::rgb(0x1e3a8a)) - .child(self.message.clone()), - ) - .child( - div() - .flex() - .flex_col() - .gap_4() - .child( - div() - .flex() - .flex_col() - .gap_2() - .child( - div() - .text_sm() - .font_weight(gpui::FontWeight::BOLD) - .text_color(gpui::rgb(0x374151)) - .child("1. Regular .focus() - always visible:"), - ) - .child( - button_base("button1", self.items[0].1) - .track_focus(&self.items[0].0) - .focus(|style| { - style.border_4().border_color(gpui::rgb(0xfbbf24)) - }) - .on_click(cx.listener(|this, _, _, cx| { - this.message = - "Clicked button 1 - focus border is visible!".into(); - cx.notify(); - })), - ), - ) - .child( - div() - .flex() - .flex_col() - .gap_2() - .child( - div() - .text_sm() - .font_weight(gpui::FontWeight::BOLD) - .text_color(gpui::rgb(0x374151)) - .child("2. New .focus_visible() - only keyboard:"), - ) - .child( - button_base("button2", self.items[1].1) - .track_focus(&self.items[1].0) - .focus_visible(|style| { - style.border_4().border_color(gpui::rgb(0x10b981)) - }) - .on_click(cx.listener(|this, _, _, cx| { - this.message = - "Clicked button 2 - no border! Try Tab instead.".into(); - cx.notify(); - })), - ), - ) - .child( - div() - .flex() - .flex_col() - .gap_2() - .child( - div() - .text_sm() - .font_weight(gpui::FontWeight::BOLD) - .text_color(gpui::rgb(0x374151)) - .child( - "3. Both .focus() (yellow) and .focus_visible() (green):", - ), - ) - .child( - button_base("button3", self.items[2].1) - .track_focus(&self.items[2].0) - .focus(|style| { - style.border_4().border_color(gpui::rgb(0xfbbf24)) - }) - .focus_visible(|style| { - style.border_4().border_color(gpui::rgb(0x10b981)) - }) - .on_click(cx.listener(|this, _, _, cx| { - this.message = - "Clicked button 3 - yellow border. Tab shows green!" - .into(); - cx.notify(); - })), - ), - ), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - cx.bind_keys([ - KeyBinding::new("tab", Tab, None), - KeyBinding::new("shift-tab", TabPrev, None), - KeyBinding::new("cmd-q", Quit, None), - ]); - - let bounds = Bounds::centered(None, size(px(800.), px(600.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |window, cx| cx.new(|cx| Example::new(window, cx)), - ) - .unwrap(); - - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/gif_viewer.rs b/crates/gpui_pre/examples/gif_viewer.rs deleted file mode 100644 index 80b56aa7..00000000 --- a/crates/gpui_pre/examples/gif_viewer.rs +++ /dev/null @@ -1,62 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{App, Context, Render, Window, WindowOptions, div, img, prelude::*}; -use gpui_platform::application; -use std::path::PathBuf; - -struct GifViewer { - gif_path: PathBuf, -} - -impl GifViewer { - fn new(gif_path: PathBuf) -> Self { - Self { gif_path } - } -} - -impl Render for GifViewer { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div().size_full().child( - img(self.gif_path.clone()) - .size_full() - .object_fit(gpui::ObjectFit::Contain) - .id("gif"), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let gif_path = - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples/image/black-cat-typing.gif"); - - cx.open_window( - WindowOptions { - focus: true, - ..Default::default() - }, - |_, cx| cx.new(|_| GifViewer::new(gif_path)), - ) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - env_logger::init(); - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/gradient.rs b/crates/gpui_pre/examples/gradient.rs deleted file mode 100644 index df1398ff..00000000 --- a/crates/gpui_pre/examples/gradient.rs +++ /dev/null @@ -1,278 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, ColorSpace, Context, Half, Render, Window, WindowOptions, canvas, div, - linear_color_stop, linear_gradient, point, prelude::*, px, size, -}; -use gpui_platform::application; - -struct GradientViewer { - color_space: ColorSpace, -} - -impl GradientViewer { - fn new() -> Self { - Self { - color_space: ColorSpace::default(), - } - } -} - -impl Render for GradientViewer { - fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { - let color_space = self.color_space; - - div() - .bg(gpui::white()) - .size_full() - .p_4() - .flex() - .flex_col() - .gap_3() - .child( - div() - .flex() - .gap_2() - .justify_between() - .items_center() - .child("Gradient Examples") - .child( - div().flex().gap_2().items_center().child( - div() - .id("method") - .flex() - .px_3() - .py_1() - .text_sm() - .bg(gpui::black()) - .text_color(gpui::white()) - .child(format!("{}", color_space)) - .active(|this| this.opacity(0.8)) - .on_click(cx.listener(move |this, _, _, cx| { - this.color_space = match this.color_space { - ColorSpace::Oklab => ColorSpace::Srgb, - ColorSpace::Srgb => ColorSpace::Oklab, - }; - cx.notify(); - })), - ), - ), - ) - .child( - div() - .flex() - .flex_1() - .gap_3() - .child( - div() - .size_full() - .rounded_xl() - .flex() - .items_center() - .justify_center() - .bg(gpui::red()) - .text_color(gpui::white()) - .child("Solid Color"), - ) - .child( - div() - .size_full() - .rounded_xl() - .flex() - .items_center() - .justify_center() - .bg(gpui::blue()) - .text_color(gpui::white()) - .child("Solid Color"), - ), - ) - .child( - div() - .flex() - .flex_1() - .gap_3() - .h_24() - .text_color(gpui::white()) - .child( - div().flex_1().rounded_xl().bg(linear_gradient( - 45., - linear_color_stop(gpui::red(), 0.), - linear_color_stop(gpui::blue(), 1.), - ) - .color_space(color_space)), - ) - .child( - div().flex_1().rounded_xl().bg(linear_gradient( - 135., - linear_color_stop(gpui::red(), 0.), - linear_color_stop(gpui::green(), 1.), - ) - .color_space(color_space)), - ) - .child( - div().flex_1().rounded_xl().bg(linear_gradient( - 225., - linear_color_stop(gpui::green(), 0.), - linear_color_stop(gpui::blue(), 1.), - ) - .color_space(color_space)), - ) - .child( - div().flex_1().rounded_xl().bg(linear_gradient( - 315., - linear_color_stop(gpui::green(), 0.), - linear_color_stop(gpui::yellow(), 1.), - ) - .color_space(color_space)), - ), - ) - .child( - div() - .flex() - .flex_1() - .gap_3() - .h_24() - .text_color(gpui::white()) - .child( - div().flex_1().rounded_xl().bg(linear_gradient( - 0., - linear_color_stop(gpui::red(), 0.), - linear_color_stop(gpui::white(), 1.), - ) - .color_space(color_space)), - ) - .child( - div().flex_1().rounded_xl().bg(linear_gradient( - 90., - linear_color_stop(gpui::blue(), 0.), - linear_color_stop(gpui::white(), 1.), - ) - .color_space(color_space)), - ) - .child( - div().flex_1().rounded_xl().bg(linear_gradient( - 180., - linear_color_stop(gpui::green(), 0.), - linear_color_stop(gpui::white(), 1.), - ) - .color_space(color_space)), - ) - .child( - div().flex_1().rounded_xl().bg(linear_gradient( - 360., - linear_color_stop(gpui::yellow(), 0.), - linear_color_stop(gpui::white(), 1.), - ) - .color_space(color_space)), - ), - ) - .child( - div().flex_1().rounded_xl().bg(linear_gradient( - 0., - linear_color_stop(gpui::green(), 0.05), - linear_color_stop(gpui::yellow(), 0.95), - ) - .color_space(color_space)), - ) - .child( - div().flex_1().rounded_xl().bg(linear_gradient( - 90., - linear_color_stop(gpui::blue(), 0.05), - linear_color_stop(gpui::red(), 0.95), - ) - .color_space(color_space)), - ) - .child( - div() - .flex() - .flex_1() - .gap_3() - .child( - div().flex().flex_1().gap_3().child( - div().flex_1().rounded_xl().bg(linear_gradient( - 90., - linear_color_stop(gpui::blue(), 0.5), - linear_color_stop(gpui::red(), 0.5), - ) - .color_space(color_space)), - ), - ) - .child( - div().flex_1().rounded_xl().bg(linear_gradient( - 180., - linear_color_stop(gpui::green(), 0.), - linear_color_stop(gpui::blue(), 0.5), - ) - .color_space(color_space)), - ), - ) - .child(div().h_24().child(canvas( - move |_, _, _| {}, - move |bounds, _, window, _| { - let size = size(bounds.size.width * 0.8, px(80.)); - let square_bounds = Bounds { - origin: point( - bounds.size.width.half() - size.width.half(), - bounds.origin.y, - ), - size, - }; - let height = square_bounds.size.height; - let horizontal_offset = height; - let vertical_offset = px(30.); - let mut builder = gpui::PathBuilder::fill(); - builder.move_to(square_bounds.bottom_left()); - builder - .line_to(square_bounds.origin + point(horizontal_offset, vertical_offset)); - builder.line_to( - square_bounds.top_right() + point(-horizontal_offset, vertical_offset), - ); - - builder.line_to(square_bounds.bottom_right()); - builder.line_to(square_bounds.bottom_left()); - let path = builder.build().unwrap(); - window.paint_path( - path, - linear_gradient( - 180., - linear_color_stop(gpui::red(), 0.), - linear_color_stop(gpui::blue(), 1.), - ) - .color_space(color_space), - ); - }, - ))) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - cx.open_window( - WindowOptions { - focus: true, - ..Default::default() - }, - |_, cx| cx.new(|_| GradientViewer::new()), - ) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/grid_layout.rs b/crates/gpui_pre/examples/grid_layout.rs deleted file mode 100644 index 51f9b13e..00000000 --- a/crates/gpui_pre/examples/grid_layout.rs +++ /dev/null @@ -1,95 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, Hsla, Window, WindowBounds, WindowOptions, container_query, div, - prelude::*, px, rgb, size, -}; -use gpui_platform::application; - -// https://en.wikipedia.org/wiki/Holy_grail_(web_design) -// -// Resize the window: the layout is chosen by `container_query` based on the -// measured size of the container, collapsing to a single stacked column when -// it becomes too narrow for the three-column grid. -struct HolyGrailExample {} - -impl Render for HolyGrailExample { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - container_query(|container_size, _window, _cx| { - let block = |color: Hsla| { - div() - .size_full() - .bg(color) - .border_1() - .border_dashed() - .rounded_md() - .border_color(gpui::white()) - .items_center() - }; - - let header = block(gpui::white()).child(format!("Header — {}", container_size.width)); - let table_of_contents = block(gpui::red()).child("Table of contents"); - let content = block(gpui::green()).child("Content"); - let ad = block(gpui::blue()).child("AD :(").text_color(gpui::white()); - let footer = block(gpui::black()) - .text_color(gpui::white()) - .child("Footer"); - - let container = div().gap_1().bg(rgb(0x505050)).shadow_lg().size_full(); - - if container_size.width < px(400.) { - container - .flex() - .flex_col() - .child(header.h_12().flex_none()) - .child(table_of_contents.h_20().flex_none()) - .child(content.flex_1()) - .child(ad.h_20().flex_none()) - .child(footer.h_12().flex_none()) - } else { - container - .grid() - .grid_cols(5) - .grid_rows(5) - .child(header.row_span(1).col_span_full()) - .child(table_of_contents.col_span(1).h_56()) - .child(content.col_span(3).row_span(3)) - .child(ad.col_span(1).row_span(3)) - .child(footer.row_span(1).col_span_full()) - } - }) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| cx.new(|_| HolyGrailExample {}), - ) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/hello_world.rs b/crates/gpui_pre/examples/hello_world.rs deleted file mode 100644 index d18cb048..00000000 --- a/crates/gpui_pre/examples/hello_world.rs +++ /dev/null @@ -1,127 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, SharedString, Window, WindowBounds, WindowOptions, div, prelude::*, px, - rgb, size, -}; -use gpui_platform::application; - -struct HelloWorld { - text: SharedString, -} - -impl Render for HelloWorld { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .flex() - .flex_col() - .gap_3() - .bg(rgb(0x505050)) - .size(px(500.0)) - .justify_center() - .items_center() - .shadow_lg() - .border_1() - .border_color(rgb(0x0000ff)) - .text_xl() - .text_color(rgb(0xffffff)) - .child(format!("Hello, {}!", self.text)) - .child( - div() - .flex() - .gap_2() - .child( - div() - .size_8() - .bg(gpui::red()) - .border_1() - .border_dashed() - .rounded_md() - .border_color(gpui::white()), - ) - .child( - div() - .size_8() - .bg(gpui::green()) - .border_1() - .border_dashed() - .rounded_md() - .border_color(gpui::white()), - ) - .child( - div() - .size_8() - .bg(gpui::blue()) - .border_1() - .border_dashed() - .rounded_md() - .border_color(gpui::white()), - ) - .child( - div() - .size_8() - .bg(gpui::yellow()) - .border_1() - .border_dashed() - .rounded_md() - .border_color(gpui::white()), - ) - .child( - div() - .size_8() - .bg(gpui::black()) - .border_1() - .border_dashed() - .rounded_md() - .rounded_md() - .border_color(gpui::white()), - ) - .child( - div() - .size_8() - .bg(gpui::white()) - .border_1() - .border_dashed() - .rounded_md() - .border_color(gpui::black()), - ), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| { - cx.new(|_| HelloWorld { - text: "World".into(), - }) - }, - ) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/image/app-icon.png b/crates/gpui_pre/examples/image/app-icon.png deleted file mode 100644 index 08b6d8afa0d1088e50f1e9697e01407c9c623f6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 168173 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

wibT(J_@q= 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 bd;t68F!!G*O<;hQ%9nV!d%My8eKYMZ9A(F| diff --git a/crates/gpui_pre/examples/image/arrow_circle.svg b/crates/gpui_pre/examples/image/arrow_circle.svg deleted file mode 100644 index 90e352bd..00000000 --- a/crates/gpui_pre/examples/image/arrow_circle.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/crates/gpui_pre/examples/image/black-cat-typing.gif b/crates/gpui_pre/examples/image/black-cat-typing.gif deleted file mode 100644 index 671a102d224c3c1b12f3c768f8532894433780a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4471092 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�xCG6&a-wot907|56x9XyUMJtuiTxl;~#DoMLwTiAQ&Mxd9Rm*~1U{X>m( z!&Ajcw!9kUr;wzMsFh9dg6mRlhYGAk_il4}a#Lhe#Iso;qWTKVY=eCj!I3NHyCtkV zgdn|9 zv&Ulf_`o&Z(py}yiQ<~zAn>Axy2f0Kr;^mClB)a1b+hl7?~Ge9r4O$+>!?wN9yJdIv2{=! zT@TO)r_&!^1I-;H%=1nw)dReQOur^6Y5q$;p$F7<%=3eXL(3`8df$-xQz6`vFFTXn z^|b%QOi3}oZl=H)_zHam0)8q%ma91_5vgvA=HdX{0y4UO3WQt688?l(kX?}=6&ZHc zM~X>%sm-!xNWwmS1URWlTWSF62zZco9ewM%w)c%)`k~>8L&jc*#I6Vxu&lA?2UP)NQE>048kS_wMJ}Je6MEnJXDCdTJY!PJMI)A~$LN!yb`hlDSXf zen*yi8i&(Ic5%|?_K}HDGPr}OXM!9cYa%q_xAN~5&W;q@6y4{H>GYQg3ey$VBNii8imjR6c zEENkgCl`91`!6Zoy^u{R>rQ&?p zfE^j<2ld`NO2HgA6rHk?4l&*W$HnC;6S#U(UbB9W4$wRvA$1a=t^>L)CX?Avu1EkK>l5L+F;XlkAwqKgE_J1~#Tt%1nH!ME5S^A;_s58kn z3Iw7?S(M)JYALR3dqDlCIu&7yNanqWQH1nrftWsAbd%sNL;Fga=LBxOPsT1j* zmpf?w#MBFF(ihbp{^_+5w3do{>WMh#4OzZ9Hg&Gq!vMol(h-^3E`vzg870ha-Yzhm z2mH^LY(#SXb*G0HHr?trZuMi_COlF(a3hJ24oUI*pZCP0GVn*<#D}^G+u8o5`UYMN z(EjU$qY#Ql-S+lodEaq0hJ&$-q^ZDJrOb4P?p{tXCStlUZeTB?-#iFtkN zCTgu;!(S!5Yw;_4zFqhWQp9WruuI~(H7jEjV||1?hQ?`hhhs)&mQeG}W8P1;Di25y zh&A9?75`}#2#=r&c8YL)Ma7`kPrYKE{cx*d;9fY1^p8^uK2k@9$R6zg zVCiG}lChHY!NEOe-Z*Zhuea3rY>|XGT&FHAS^+++OWpwPOLG~L*aax-03;bE%F$#= zDA*9fQD~!>d|;bFCai_svc$)YJsR*>^f#hMOd>7RmA zcy@_rFPHGz_p2R8uD8HTt+caO&)db5ezp~qz*LF^+wxz4j7gf}H=z1=T4tXyLV%X7 z>V&s=f(FEVeQi|+%j!848#l*qoBs#G zt4)}5sT{$?*azOdkymMGuoZxkm%-XgDa>Y0-_D(_)D0qM1t~{!uKU+2TD8k}&&r!n za7`1+V}rCD5FWC5>*sslcoHP^tP|e*%+%jwt#kjI-knpMGE+4u;+VRjz8~_8{jfaQ zE>{IVS4$;wC(O7Lb|kRz@SwFC*m?B+pGns=8!^ZEdA#G<&%O9p7>-lL?BP z(hVJ#4i%Z|m@CssgyYc!7jr}AgeenjxJ>>S6oR*280y#>kmIz9whnku%r}5q*((Z! zduA%rFH=1(>YVZ^Q`IUnm+_MD%@zHEOGmVy^4#Wj_Dr4bqaE}h_sqJOZ;zYS8}_7T zpp8E(r?JNJm`YgH_@a9}_s9b>NmF@&NoBiY{i9yDyMXH1)=3L2(=L{2*(HaXoRs&J zlm(UpGe=9z!gP^mlBu=?(DwW*fV>xCfNE$A#Ojc<(#T)F14&XUS7qevF1^Lm$8FjR zj~Q1=U^x=YK($a(_C7g<0<)6>K@vjcx-fb%0U+Lh)Q^CymcfpT4HJe%`43eJUGUuL z26gw|{Z82>+h!}-<>!9;*==|k?E@3nF3d2;EwNPsr>|a@lAmpqx4l-^8|c0F4q(M3 zyc{OJ5saa;i&Y@99vols=isgDUoWV;{Q2N`_GxhwksT3CtIIMlEn<~?`TBNKW-BFK zrp82h7L>a&e*F#T)MK_;C`E&@Yh=Fke-xdIKNJ4<$9KQj#mr^yZLV{PxnDw?`#tw0 zX|54!q{2tFi!nlTsobi~ElF}ql4|CXTN6g6tk$a#4P0Z20gJjHNKk5*VKL z^F0%}X6CnMcGKZs_a&xoX6zfyblg{(hC_(PvYn+jt6TIQ1)%~0>bOU=>(>GCmruvv zGbvEaIB?TC@w9bAdCv~P_oSL*jc2SI)GYNOrW@xXecpfH-uqfX8Emv7xe_p``W2Y5 zEDpo$(N7UvJ>c;W!7kSx#M4SM=V+9#;tXj%Pvf_la$q=+d+&c9 zF(DwUhEV%tzs<5|gPO*cqhE?_SJWKWidR_wYDPqHU&7EqB_cnqDrZ~e%?yKl4hY^$ zt!b{?;DL9C$%o9uJ3d+Xig8Cxac(=?SBP|s^VEx2$a5r!BnY; zp3zze-%7<_7QXI%{yxq*M0zeb#P$4vN3(6Z>1nR;(adz9J#^jq*;^4HQIxq1BCYqz z?`11Wm#`Pc1Lv+GDe{V|K_I`sX7K3WVhnT^%OG;Avb4|3!qe zf+v_n{ADqDUn%YQXhu)+y`IEfeX6zJmo8a1?)sIO|2_E;i*|k!Y~SlXCRfz_u!5Kg zGJ%|JxpzW?CzeITwGx)U6-xRu1vOeP%C*5}nH_}tNm+k>$qLvuzJq5q^pA?12_z0V z_rKVK4e$&m#dXJk{aS(T%IBL^*%M#7cka|yzFPa{?6;a`IZ&hKwb4^I(?@ge89V}# z|A_4z%KFuU=8d28(Qo(xVVuLlyT)EShMX(-G`C(FZ8GRd9j!75axDZ(t?&%2bE(HC zU_S%I;9_+gb+U3Xx+zebm)<-I1c3-VFZdOJmsz{Z7*@`n322yHYQ$5S2z4Z)w7}(T z+h2*)$b(HNTMH+$WJ22_8O9`hC40gh`T+*Twp^%q z?s#tcmBMmeA1M^|E9aAZHtoV?RGCAEmKP`Q($hAt?7*ZK0+(}bKea*-;k5^*PZr~` z$+oSb1uK%kdiV3H$NL1g|Iys{^vH)t!mEK7Jzto)|8457Nl*T>{ma|SMY}E;Rv<6e z|Er_2eHGpqePR9G!+VDJ)}w3gjt(2gAdK^`N}xF>yFeR8AEDfu8g7kDj*-*YP+n8GL}Mj$2`#*!Q-U@ku2sj zLyOZ?@&ns$1NDTo61Z42rm(m8(JWJ z^bQWNrO37(Mm#HFNUt@>(7-%4A#$-B3+(of2c{YjTK zK1+;#_3sTTfFh|=?cRkuK7~uMfyV8-?p@vYDXt=^>F4!ox-n^l3L3Lcro~J;Q!1mT z3Yy+5cAk8z7I*Lm#r4`nN0(Zgm1wx8M%pZRn|clLvs-Y8ufv8)TI3&ht#{L$(Xlfr z;xV#c8s0l&yb`9A8a5ELBVff-M=pHa>*s{_p2L4f%|&GJciY9tt@}B6xogVO$;(~C zGZF|oe@GALT;lhDx@j5c>j5G`iIm1>Ij5?pBbwLZU?ZV z7{w5WcG{8_A@X3S$oKfo{1P3@umlm*lh>!xsUt{D0YnRA$776o=rr^bs0rxVEVNz$TP_vjffaY-m|6pu zqJJ<`6Z4Iy%4Iw&wwGr_Y!w*_ipPZLK- zLt!({ZL`!oUmerzQf}f(nsQ%J1H;}76B>XwstO4UR+bcpHFGpdgKJMShHL}-f_kp@ zmljK=^}^qN_i(bGc~jP)<5==}poop{{4zqBpiah`AMrox{r2Ob|BxR} zF3FEDhwv4F4@w-CGX|l`J3u!!?G^cX=a10rjeBiOZV#@)nN_4%j7pi;1|*30uqT zI55w0vmFPVv0MNZzZ>|lJ#G=98xg#r64Wd2HWUJD-YY5n`+;KNm6IoRBR?L?HbGZsW7_UFRK?)J2&ag7RqBxEy-}?CI_1K#6eXp9+`VUW^@wPjI@x>nVjM-Op zvK#1_W8l4?-C*5H?s8==5fTesZtlzfJ10cWcYtqx{IooIRGh5#4gh(AqPc~ zpJM6~0V^#rm?9?7^Al2wzQ=IOxq_zS2N6r26h0r=D!}KkuJp8haCLz^0az*n*7!mz zMl5a!%4=h_7)JwgfQm5)X(MMI{+o z$-1`^VB_J}gV(i~>X3R_byLe;DSQd=$(~lI8EEVIap!#7%t<0(4S*FdotXrYEdf}V zRJD*K3!*9t665Vr-6jq4%OiM0FC)vX-U37z6&(KG+Y0X_M$V91W?)*%#8dQvbl(Am zak?h&^w0DO&vDoEiU#!Nc(n4dg6F^d)$&McWzmyM0%W`C?~?a^{c^{rmVY1fxV@^Q z#LQQ;UD`y8iIXDI*E3RYQOLXql8_?R)D4;hU@}GjTPnR26r=Su70(bZ+7tWz>q5Li z_h9C_TErzCf8|`E;#$mpOZ1rN6CVM=@r8=87OJNS_@easFFHm{L}QEL8iO*HOhOx% zkO`tq`r|=FV;CKfPDQ~DWP_x`TL=3+2Ja*RH!n$Q7Y(KH`%B#DKjIC1PDA+;11JaR zE5aWGTDF$STy4kpe6nZ|TwXY^roN0y(~c|iEI246bYco6=nQ+{tyPnM+hY%`v|elV zRI?|kZY~{69ItOH8c>3mDMkU2zhv4$u{wcZl@=1}E3ZA^OqX7od?!Fz35X49ZO$Pn zyoalt1gN(Y6{Zsa%h9v;AHEiXmDK$dBLiJ~sWP9M4Ca~W@MKMPDnU2jEhes)(NzJu z?;bI9HoXBYiPr4%Q#)VkD-bfm?x&~w9rFdMcDVkclH^KAs@4J}1W>sr&8z2QrNq_0 zW#Sm)o!@Bbaf#o{3K6Ly8n3#TNngq=feRdBYH?WI7LQkLWAJ4W+!}NO$8~&tB?ONR z^dGMA7l*()L&o1{O$rp7Mey&_@Q9XEpNAo?z%`wLCRjs*W8mAR-oDV90NHpMF}+moXXQpq>zm?v~p)W3D@&QXvAGl1TH z2N41UUM*rmKTA~;1`Oo_A*)1mkNG%x1X&mzJA}2qo1$jvhp(d+lmV5qfoj&#T@f&a zFrnZ5!d4N_PMUJbgOV-R&Bf~z!@l@~-RDCTr+Wx~;W7ltV*0*jgi3z0&b- zHFDr496zS7)Q`e2nbj>wy{M&E;sbbJQMIpN6PFRm62Y;gEZo%A=~lO0-fX?HO#WpFM){UQ0lfBy7?@_&ROxlsPs;uW%k#X53K7ugAQinB3%HFV|yl zaCS}sHOokfpIb~fCA)xuIEg^bfsSH$mbCCun`=Zfn$L`VwERMX*<}*8ounMJW*j%g zmW<$yiEv}0jWs7(Nl#TB*};ody~`?#ly$9MLjPce{&jbcL!Aq6K;~c=jKfdE3OsX; zKI7^1jRSsurH7wyP#rnSg@}%Fd)I#e42`7h(KYs~&v2ud6zL`)IXA=|MmnH~k} zcH+TP094uNBPvtIn`qJsz%4D+ZO+rpE`IX35RGX7fZaT~ebIDO8ih_vnq3Oasif5F^Cx$)u?niu-xkZzWqQMSm6_1 zb8T^b?wr!3wjxL_X$82|X&mxvOS|>Bv_kpu3N@e|Rc!@4j2TX_%h933np&K#iRv8}0dw98W>@LPzp{Fm)GYal2 zbKu!YBj2J1w18L0zv@>|k4CTxg7R$7{Cjj9pj?E^>hOtpL8}sXMeJiKR*2B6R)JFm zaC4gW%3}=f5J`4Yd-DhKYE)fz1-#ZgoHOyby7Jv!fOpQX>uPo9HE9CPUWn|&sRxmv4x+-FppwZM7@_a6i_Da2k@dVRe}lb{#NCdH!3BCcD{|`611Qe+h=xh<#SB8wuV~+yRo^loLxdmlM-8{@$};p4vqvjl_+VL~`RVGX<&fbkwsP)^>dVpVtA@CzN-aP?+>gSdu1^-O zOcWoerVu=%ic!7!wq2^KYf`ny_sDLvVv!N5*&5XzO$b;#b39wYI{J|Jw{Hxp zg2{VyuJEv?-P74xLRP7KriF*=@!kxpz6#f;jN;z1hIE#2h%aK^&G)DIza`DD#brGrDm|woBL_&hL;uL=$}bSJ{J8)xsIJzbJ4;<)nK%E# z)tM-`Z7Rfvbld1g=>BiT>fWa-q4bz+nfM;zJI)f>$=pjJLKfafGjJ6u_t9=|nikd# z0dd&>taS#^aW42A(VerN#!agN326NEK+7=}G`6DUGgC(Vcl+#{ea42MA8<;Jg5gDJ z9q8@L;_m&*M8wKV6}D)>Q6$qQG}OqG34CriNWZ!m6AKdQUWck5j3%@V$EEZUT7(30 z^z2N#pKzDzfsqF%BM$V9p03Rtj-8(vAgXlFDaWyvfgQUvgsDbC+&{CHo}8;k1_FJG zXLGj`fz5zkXoWz=MdR`3_xIH8QBG&jA{{baeqe8gQ9)#@wrOAdUk6apAEqWF#I*yJx6)HR2d49i z?QLbAw7CAi!Uzr|xLWlYM$EqPuX8I@HE0pm7h{dXr#Gc{erFiHG1}V`H(dsOB}Q=qeguhlr{^b0r5t@8Ql9coJTTLAZP6pOok}wbK8l`1r!4^UDxI<(j z45uqy8u8QgOuHYc!WiI^ZF%e%jDF!~2zHl4AZMS7gbK#WNtrGfe^)`Qj8jV2gf-^} z)y*u+v+RE9FghI*_5xwY(j^>l$n zKJj$HSZ)DORcVmucPe2z)KSpMUj|~;liwFF3*xncigZ=imrx|G0^5)DnJ({P#s5Cz zW3?;Z%rCG$!#v0Z>$fcDp&G9s=dK#6Q0kJQ(>ZRIT`_>&G13r++TNpFZ{*WCk4IU> z&gIJQ;yl?H(gHa?dT&WfE#n65+8kaCF-`kghB7_LtOwf;EIWHr7Vu>#!(0IyW5{hT z83u5Q}vJ&ryd}Q&z z=_E7=qW)Pu8enBY4VnAORY)MyAu66gEncJ~RI5<;n6`|T@!QeEMQKwjBiV94Uk1T#b67GVl!Juk%3%|+BHB1yEW@M1 ztcURV>LWsfelo0JMXUbgA_zJ51T&?zc4GI2TGhCRi)K(x@x`N$-BK~$(jSL zEqe1Eu2Eewb41{e4SqXxDZuAHz}A2H_nqc{mT;&6AYy~a;dzxSL`$>{7@ArhTF%c_ z6&AUNG&?C&*PM(PV&ZgJCKgq@0JVDuLzn7|u3n(p=RzCe*8$v(ZE3>Wlo{qzd|k&szGb@pRT+ zMxI>WZn*mLqUwq2C}me7cPFp~i=wlfu5W;h1p$iR0Pa@(@S-THxAEhE35%%>bZ*h+ z^oh1#h(8puTuI!}Nz#K?S7+M`Lq$vK>-78EpQNF{eXdHzNd?C0i-=uP=S&CEilCQ@ zj-$w+`b8jhSUb=23TWSs3?J+E$K=C8t-Xfbg{JMqK|;7zO;|vw4onK)iDMhyAVWy} zja(;y+zAc14EAuX$H^e)m!mI#%G{`yBf`(Mh_p;2neF9VQGk%fas&4t5mZf!jx(^8Qfhi&M(6`Adie8{c&8|k9Y^w8wOz7oO8i@ z9jH28R`Jv-2jLo!|86_L7uD>tt@QxZ4ay{};AfVkKk%8NC% z37m(-5I$!Y?Ef%Z>B6odXL##6ZEhaSZY;fzj?ElYUIRnA4T2m&tIR|D|3O&(knTul zwCP`UhgwvAbSRpP(5|bw_|Cdc4g;cTuep%yy2VZ#b}swkRC*)I{=1-(IV}$WBJ`E7 z)P#wkIcahA-&_G(v|`xFYgKTgvm*FJgP3G2=Qpu$FvHV(fZZCW>voS^t8mVF@9r%Z zO!$|<^`QZt+|#DpmAzYxezwz18t?5lodBopv#Ogte26RE6ktbPtj}#NT(=7utu7oS z*uGH;C{b9vpXLf1um6i~nFc&Sl88)kTR@wY-xu}qZZmy7S-Rq#oF`{A|~AB1z{=iXk& z4mJWf11qXkJZ@jX<)>exKN&uMwN<3@-#m!AIj8a@t{(l2@qiXmn4H(TM7V7NnY8cO zV+;DH`*P@%1OKnOL(`MQNbn17i?MNE*8wLd25w%jWbALnI|mH_NbCNI8-KeG;M19H z$L6Jfeb}kg#vXvqzxBq z`%8dK04Sfa|9osDkMEm6g}&EA9utwB#bAw__IqN|_lW&oWhDNI0UcvPpKZONOvEmV zR%tl_ErO=R96^P^hdBQJ0cn3{r2y{o(fCH0vN8j;Sz47d zGL3n3km6qV85fdZNH7bEeuve0EJuEfLkls}g7}Z?MwdiJ&{_NkB8t7fh3pC*o;jWr z7Mr~85FMaz%yhiPaEbf@-7i%dWMJQvbGbP9LNKCoiFbpKs<>&h74o%5=#lVRb_39Z zTpu(YQa%Fc4S>e4Zz|)vC_mbju^g4sMOQ(O>v1cU0kf^2M%4nYD&dP6_h|Stfx)|V zRE45ioK8+$2KEv?=k@`acl?|>tBAqqh=v>LlK+tDD>CAvUQ`KUP8aP!M7N5-vHaAx z)7Bqphv!)(x=d2$7Y(X_s6n;H1`*!_Q|l_MHsrQVe~Wlo>U9JRsfK`#4}fmE60Fv3 z-h8vMqTBkfGnstfYZB&p2C^Drqai?NF_26mQplt~Q2_Foi1)`3p5}H)0dSX=u4kFA zE`vSq>hgXO^6wB}xdN^vB{$>HU`Z&{#^1u3QQBpD{4*yx0PfBQj>~5}kS848loSdww1C)1x)PziE!j!teLz@ce|nsUclXX5Z?J2$?_wU-6eOCakwR0jjqr-e$n zj~~IIQg)w@WgrjGA@|GWt(C|z&Xq|c&|?9pO8oXceCRt#<$j{9>`__>!70S{XbVn8 zPoQO!MEGIuV{#vq<3hM-=TmkLqQXR72gt57Rs2I#yLc+KJ<4kBZeh#SJxKHy0IXxq zO|Yoa`LjCf&7ODbswo9Us8YlQ26jxU(0xnw@a!JpH;aL2^^t@N)GvT>CDe;V*foW1 zRdiTYfK04NW;rVr(0QsB;`M~EO+{>}`ZUvY;-8hxOMfas zuN0%e*;`Og|HY%QQVv5Y3we!*6iZ5#p2=*%akC^c-`@((QiVtYFia{4p$k2NkWY#B zqjaPba~tJ~y=9zkLb-O$N_r$8KCkU$kqAOlVh)+dSazBYXhZcAVlg-`qNIkx-<<_=MIKR<9S*qvUv^9p`vKv+=BxSakQr$K$$ z?TuhdMHF8EzD`FU=`%VuCUc&m&n&3O^r*Z{t&FDf2BeNYtiY0==>0?|=kM6U`)7lH zk?$~Nki;EUQs9Loi1E6OSxW)fd&fmDEh~QYRfC%RXxHmwyZ3(GBxG@0L_f{8S*YLY zHS4~qa^2RV6^0%{su>(D`gp{Bdu&cihufHb$|Hl6F<{CAmd7Rjg{eTxM(M>jK#+sL zyDE&t6s=0~cQix4pczEqd_Gg~0h3Z_fFR9SHrR)E996BsQy&R;08TDtYfd@pono^+M)6P?4RNIa8{s5H6YH*GW z&^H?%B!!eQ(LkJEa{yOGA@Iozs^Fc%GYUp~Yg8=TM35Epe3j(lvd$g6Wf=X-aCaV- z3oy=BK|LS>I|bkeLvKN3Std>XlTQ?A->;4qhKkx@Koq zG!2OtRUaC1Kfh5E7NUCNKb4!mR7aEII|gur^e`?3SG%G1jA}7JE1n@o+|N##N{g88 zNO`b+s)|ukCGZyGG($x)Z?aMso^ShUaQ#B}wz$`qu0uf|&RJ>Dt#<_+PVDkreBpIX z(feKRrE*EtajO5$+ki{qJ0!2u5jO4M-|ZU6O@xM*P(cO+LD6w55D(|ltB>v>f9srO zls#1_pPvP;E{FXQ+eEI1#y84XAnc5#z&~YzKVpCvQQw`BVRako9sn*Alpmex`mz^} z1MoI2Gfo76e?$E4ifE;uF{f>4Pw?>7K_@XGZwrYWaBOFjx!fmdU*s((p(Clt~3!*2pIV^o}wKyCg#A8nF9bMDDopBOGQAz*)u8sYP%=Jp^G8 z67~V_f;ej5Tn;zh4Hn)9Xo|vgmeQ$104pN;65#wgy{5QGw%u@d=dq zHHvKO#O-@&;e(}^;aj-tbvvg4o9Ge{`ZO0?^hRX?cR_4);c1n+L%By1?)-^a?}gF+ zEgI~s_r<$8mK(s-k5^MmyD$BGW-b3tQ~J(&>GT-`Cip^yW^uh|rR8DZ3?hX2z<}a! zOdLr=0a;b@F`Wg!hB`v%?Zr%n92529#XTmD6hhT80I)tTwmYpeEv2xQdZ5yAjJiH@ zn4S(^@>}{1f#jP81vqBWk+(#&W*op@WU8U=;I%$b{10p}R_t^hG(99f@t@eo&@oy9 zN8^BHbmUGEnU{}w#jmXVjIH|YxPQGnVB*}T*MYw7O^U*ygzOt_OHQ#0md7u zADP*U4@*I_nGmLQ^Ze2DU1i@P*VaK#ah%(fM!BOd1&K%ZGqy*nJW}g6nd771I^RF4 zj?TaRWOri#n7NJkJeofdCb09lyn*cLY0M4ACMHr0a=dT4{YCwGkFNVJT~@7Ir8dFz37j3IA0uwC2onV~0|z9Te69PD z#hryu8zTt#`p9SY7ako%9$ODKOg6w9-T;o_hK)>sN3y98MEH*O9&d?^T!6MM9h60f zXED(`>1lTbkB_V059M)vJ~-O_X%5f4pB08G(Z8R6TBc@S<840bh7{$EgRZ55h?4`x z4A>!&j7W_AxW2n9yryY`pd{bd@auG*{sKhwVA5faWh6tb16-TkyWc@h}*9v1+Jo%D3%* zDw}u3Ic;Ce(!Q&7K2Ok;i)ni@kC-}r=^YPzcJ)DYt-R~**!`0ry>{PC6r`65Uc%|W zBx0?XV3tgj%~HEpy0%BB{o-t|93L)e=(t)ASFhAn|L(=pcF4RB3ZnpcHLsqwzfsRA z58?~V@M1IrW;J1>pTiF?mdbfe>xTze{@WneC+OnDEW{4LF<0JB4(P|{Q6O*eP3NtT5QiqU05l8RY|&|{^HS&7tgGJ6a$D`2B2SZ-YaCHxLHwY$qOTk5PX2fSd^{p?_ezJ?In-$0Kq2$7%uT_Qgg$jB9&mib^0;Pki5;nag! zgXIZ#Gy&UA{Oi#N_;nEiEkc6@0ZEQdrDe3URnY81j}jVohPdy<9loJevS}#4Xn5c$ z@(~|H2jD#7hE>TEl>-SqF&agUjHlN7@20yh52Gg9fO4lGSGk2aVZ^NvMR8bSoM{UA z)wh@{hPvYscVtS3@~)jTe$=YsDk=1N{88C849yj*5juG>l4C-5u$$Km$yp)zM6>FWEM==eM-0$ztr@zV&v=lU6(S7RJ1+zXvb44 zSW;G#w_=!gcyTn>y+l2_m4)HmL}~@OW4?&-ro4i)LUkN&*hI`}lOH-I1?4Mf zH2BEMp6F#Pr%zUdZ)33(434h0BD>tj*+0KNZH->kdDs{hQ3|$O1ppxg8+)EWO?}9V za>k*eQw%Zs+@4#AFsCzFh#>xQb!yKBN!CcOmI8J4&19pLdg&t0_GMH(*2r}gS{W6U zsRdRF@6|@a5=WWp=|%L#B0}*TZe;uS7g}JiRa7j{a1ggx0&S=*W$E*>2Jlg?=1I-Y z;Vte>QBzq73gORZ_2g_98#pDyv-DOohm8ymg2T~8`G-3`#u>{E;aR%X=F;fPg3A}Z zJ(WC*vxb$AmWY!G&gnnN%EnIPjm?n(fx4BuJhbH1HtA8$??kPVdDYiP6t=t}JLfs;8rbMgpT)^}*P2Y87?q$SU`3mH6#!Q@PE%hFIluOMrxz_bBpD+aB7M z`#OxCqq?^rC5gsx5=`7!e6PcUX@!NV{9LpZfJwY!4Mnn+&pf$*}+u}KUXH|6u36+IZ*ATH`Z_9@*pZSmb{JT z<|*?-E_EMAri|fMAD4zY)(zI=+OjPpbiRJI+c+msj8KeD_FC_2&$RXr5FxL;DIlDFNs0Hs!(p^~fyaWTK1-t_-ER#8wCw2(i%Z(9tAUql)Pp zoxnOpDr38%l4q!wx|XaqD0g9AaLYS<3CdXqE`$u($$w2X@S+3x)vlLxh@AR`QJA4I z6RbS&4z2o`3Fj@d5+dsG4opd*0}k>#j_A}P&nj}I=>7>u){bTl%;%6=oO3v8rYcD5 zCDCo`TLl5%TJ<-6Wb9hTmS@g z&pgjVc;gVAJt40|SA1nUSm9aVuy^a1Dm*q$AAvOO-JY7%D$-RM>mEM!(m6e_d((|~ zT!DXo8nvqMoHm=?udRG-i>Mo6P!Oi2EjvukQ>6CW9p=fOC~TZ#-UA~NGo<+YrPfS)g8~{l=E@M!9ckjqRgemlvU@BSq)=4cxwV$nKM z!h}xe{44!LWJ7t(jEFMl^FtG*m9489@!4KVdDBz4dj^^R!&iL%T<}l<+_E*kLNBl{ zV~oMCl~73`|1o9Ggo|RsJ$eoNwo+|{=?c2thVtH{Z!m|Olk%QP9~0c4=O(V_&NlHL zUu%N4)h(>9eO`wtC&WBjXPr^ADRxVm8z@=vi*CjjarG%xpcp>l@M7D5yelwr@$)F( zi_(pZ5{-L9v|If~-QyQR6@$AQk(mbh%_z6#;h~fM1AF`K;f2Ma4ji7T&f5MRj}C_I zlo~a&mX!M+fJY-Za}O7k?^h7bHF5ekYWzwvMw|D}IB)K7)VjT-cqehq&gnqcgN>>5 zS!GMgs+^;%4@=$`K*9%(B6s~b()$f|@qrAS+76T)VXa$5Q%k&Ba+4({Mz(5R%@D(O zHiBM5Sch_4YQm_HJ3@Yk99MsZsY=#SvE#;k28ICg4mRgSNJ=f{*#8>`V9ZR?*8!>m z7UBw|Esbq~E8Al0ZKtreE35xPfFGq*8yDQNC)>nOVVHGjw3nh@@+D^;=Li`cz|c<> z-4Sx`)LHa7Ju826t2|3wZd-Nr$O<={2w7vvcu0WioP6y}Zte`Qz!|CuMn)Ut%C+Y0 z-5?*jCHG5A0Eo{=Wx)>H=FM@SGar=XHjN<^GJ!Ehaa6$6G?UluEDm{c2b1$Jd2>U0 zp?-1jqs_UEBB(x(`SU}KV)S*l7LIaKwVIBO!6;L|9p~@`h_MBAnpgH`p4X#59Vy)X z0Pe|EaebG(6Ui)8?U>mPZlRkpQ-C$P>-*0hWRsH%e+t36m!0asz(XO_&(wZ5{gKfk4OUFGA=STS=J z^3+`XItvP6hb2NT-pX|#mjLZ*!UP~9fZsUYMzgJPXg6^H_aa6)-djMtUf79xKBMLy zLa_v!?JAmh@PUrK%)f*VFJyf?rq9~%2U!~gzEuIU!% z+%l;DnW5`%T!e9D-yF)3T&c&*+uHYKV`gt}+p$FRci6*Cqzfk#-EVn<6N>zGe-*A+ zVmwzccAGe`Spdu;0G2FqC*tx-2QaUgB~O{f&zh|ys@1+Yj^LKno-cP(ms!_tRr~OD z-te}cPAYL9&0UhVrZ#ASG`bK)sS=YL zw7lj*ZS{7}TnA)ZfZ9znpn{dn8hQA)q>+*d@~RxO#Sy)lfd-N>Vv@S5WUNsLyl9u} zE`pAenl7c3sg%lAbv0@BHzkAgQ7X*jIqu${uyf&O4+?Vqe%xyNbHUkQ0&g*8_g?;W zQMtO|9ZSBZr!wRTovXXm5Yh9sd5_MBe1!aFAs{b>52y1u+07T2La!_VL@lUXygAvK zGa_Unsk zoVwUCd|jt{KB5ECcMI12^P(-q@mNP;4+9m<%!}Rn4|(Bi;;Xq_Tkem@kIfIudA`MC9_4(P)=q+W!8* z{#yxg)UpQl*B$tcDcMtH5rBt@)b*)qyJOUh1Z<}r{E8zScS?=VD8$ef(j{yw7?OZ8 zbR7mw7R*_)X1%nz(HKvl!ch>;b1XOp5Ga*PZaHI>3EDx0hR6wOHor3M0|v&mEiW{v zr%83X2Dm5ofn$EvT4h$JWLN(Um{Fel~Nmo6~Vbl<%AgKbi?Ws(kLf9eZ$x!yX)@nGdzJ z%(G5}o@BT|-MYj23?05m+@2_hVa9&!`3s|wBepj>@R(`7|6;Q82Zg(dtqWITp~;(HZ^ZttKQ{Cvhzc$<0KTVG z0YdXqZYTpoN|G$V+bT^g&952F)KkpCSw-0DpH=^a+@pNfE_qfQz&)cBeLe3gtAm#7 zz^m`5g4j?SpCy{0TSgnJN5j^En8r{9Nf!ZxJfP)mvyH&CLyX8Q1cmio+tN9>8ods9 zT*|h}q$u%o=Qo<8sCj7VvkBz*o@`ld99&k!@@a;6wX18a?a@9utr9%u6M* z)cG*y%9$ue!QMTS3rMq}m0W*n5d{-$w!I@ursC?pKL1B~fy_MfO!y&YZZBFTDwey? zGcT?g8YQYYV+(>j=Ysl5ZAU=^ki5f{XR9}<>W+6Ft2p~s+UNTP$oH|iZf&Y9&W2DHR^8434g}wj*QX*X!f{;S@hB4x(aHewVzJsH`Pom)4CR$W+In zu6-Jvu>ma`o8k%23iy~h`D76ej3uWRK}J*=j&vGY&^ZHkeActz{_+`-xj39X=OI`o5ZTaK{KY@dh!$DhY z?OloBz$ion1u=C3x;K+c7J$^-Ympe{#u$`gTe~Agd=7z>`2jTM=czL}bY{>t(cH`B zJAH|$KECDt6Xgb*73H=UY0b7w@Iyk=LzLUXGZ? zoY`vxHJ4%<3br%=KqRl~uS2ybo@ys?|5mBBO{)FS9{Zh+z2L~HsAoa_&l&vEiZg=F z2e7y3ncH|wrRdV{2e0dd>kyOV)ICjA0o8>Hd>_Wmx79X#UITTRx%+G*x>dFqzYeZs z*p*)_K%(w#AxJUMt7r;9nD!#pmJXz`>4L$iX08LXbc-$-5p?V94sdmqXM8~QjyX`0 zmYz-aLXQAn-lq;n(*5bbUdxyykdBXR9L>8GFsj$KT?0;%FY1+7k6B zS(jH7v1tpE)ymawo?S198S53AZdJG`V2FX^m~!6sx569$<0dJ?QyK76jQs9aQZN8^ zjMy2BO+B`ro52J$6;b8xalufY__gp-XuIK!K;MQK?ZAu zhuXBDSAjPTw!SyNLNKq2{|Zcgr|KZ&&j<%R!|r0~#a8xUJ9}^vRSw1ho!o4T3x1Xj@K*+W>fUg*| zcQUg@6A#@s_^Z9X)I(c^$AwOUj7Z#Ydxa2OLd`RPYBqq*DH{C9j#%Ute@oJ1nv<6# zTK==QE|+NIE`|Q)nL8Y3a^x#OALO>M0rcD8`caAi%VTk1o6;`IJE3bS+}GNs8U^gr*-&g{7^ zx2;v~<<^J<8F+I8GP(Cy%s^IJwwzfTFNFzVcxM=}nX0-Ul9-E(gY@lJ4-DiL`H~##2U!pJt&T#&4M({f3 z;C~Mf`D^O=z{d13-s|sN{;~P{>TKaXj1C>drC!PJzrcsct2$dD+_MNe^fL1u{M_Cg z`5jzV_BDYT?wq4L$89CQggQT?fK2I>v7zy@z@>!BrDfH>d5uibWB)W57i>CugG#vZm8Gjr>_3* zzQXHF#p`J2V?Gh(L6+|^7lRy2f#BoXpG{q-Q}APqZ02?Q*EQSKlo~2dx+dNF^Vbsj z)9~QaV;^t%9GqG|TI2+BQP^(1p3>vLWu@zDq4S66eMNe|7kkiFtMlVDt6$~bC3IDcQ7+_!bwnBa$1g$RL`z239F zDQa{T9a5kzzTQB%etzIk`0UI52k%L4x;?GQx87AE4gk{K#kK$p!(qhJZ_P%28L&@{Sf%3rxJmNB`28L*=9Ak8a; zZe2>mMMc~q;;sv4w#h%zmZ5e=?h4G;u1K6inH8pnxVE|E+}Z(CH)oQRkD0XfgEU(L z1<-Wjn6IfnaSZE`-jfpH|BRvBVsDMOd9$Rb(=hS!+8vqt3GUd4l$iHm3#*nN_*dCuR-bN4^}s4P5bDr4cr0H6141WjCrSSU*hQrYkDgw<4VHfpxw zg!Lgo>a^iQ9gvUxLRp}vEz=h0ney^!YQv9|BmeuLclxTRhh$!`0h6z+JgooQx$Ov4 z)vCO9SOt>Pk-(U*=J_(zt7rNZLhWhV_(mYs4?x%~IGwpl$YJ;$fgMlfo>d>%pa-fZ zAsPnQXsdgQsy2k7lv5! zHYmm+DPLSweFoehnw=S4e#V8$Rber(99+3M}j#Ws^qfqBdP+*O!MJi+f02LI{TKBkhyUWp&?C6bO)yxcl{mKVjR`g;2quu8`ZH5{GxZmKH z!=^-Nova|7L}y8@%+E)XYmwX?GqZS)V+h3AmRSrGUYld+I2vTEOeYK4D|O zCjY2_UT>mR8PyZ}XZe!zmGjhYX} zm>JF!x-_VV*5QIb&_qw3Uzp#V%Dcv_ya_hC^@Tv;VXxe$-$~p1li=>fTB9>TJicO! z5OX8NfRLW#9}A&@O4^<{2SA>`xVKrQbuHDY{KgL_f=Gv=#_9D$W*phAGQ7L=Ym`;~ zI+36hJ{6={h%ZR&XxbiP2(IEv_StVF6 zo{iR=Bmjk73kYV?W}kLJqh0@E2OAi^{=AXMoY^m+e* zTwJ4|cQ(x9>~#jxX8ol`L2|(XmLt}@;#rcDs9-h+(P=NZpc-|x-s8UttZ=>0tUs_Y z$=I6wgqvZTe`-3_On*bk*C^xYLR*2B-%oh4c@^^bL z`zVD=Z-)!wgl)Smuh9N?a+t3=QJJANA;G4V?PFv)@od{K$e*_M*ZAh`VYtYKn+XBX zkRR^){Y;Q5M4~dnaZ}Sr*PV!*ywte+8DRE)9b~B%ckh5g73lt)ttcg>`!?oat;jF$ zgJ+z}6+!X{e#_zFO$2bVrrOpq+Ob|lSzg!Ih1btpuxgR%uJNT$gy|RBG?8?iB1V@s z!Kb=3c^11u{@p1SIs-KiP2R$%JZpsu13{Sg+>IJJZnWG3YBQfiW-+lBnMG5``X2{wB+$X8bdA@o znOr$PdOrQPgrEX=_SjwYnSY4}(vk7>%BYx~=m)y3e2!|2HQ}7C1z7T694oV zpl}-(dIbN5^gqu-$%up3K_k*(O~zPK+%_$P35QD8f2?^HgKGNRZo`P6WO28B8N<>Z zsXD+@T<02fj^SGflBOpqOQ7!WART&-6_LLi?`oR@ww-jd1?*5_dRxQKJ=x;sV4P*! zS%uaW3Xqash+#oTVCY?s6@RVD)S{*#UKNK9`l2r@PG~IQ> z#65cp2kgA7a!fR0%wj|=@U$l-%e}m+Kj%D}5M^cH3VrJNd{_I<0zRioIxu;!WRJBq zgYFTad57ZlY_KXhxTg2*<;6iglEAMEh^O)hEYLf=o`V{1e$do)1?%FS&5> z60bRt2ifwjQY8c7%-pW?qGCCq`eWIG1psSat`fmlA?5mh+(XRgkpZBPKS)hDGyum$ zax-++-*s4nf@OTml#wsL=tkCjGF-4P+5<2_AeRjT&|Hl@0+>ERvAVd8oVmN2f#LAp zt4RLM;8_`q6=?#U6%cfufwAxD_hzuu_HJ;wWOtxV^<^U_SgDS_(c5u3HX8uR4()JI z>+9W%nJCmji#xgDCMj;#;;>380XF3cBs^hIs7Kb(5C3+5t>hc5OkNy_R&b|U(*^uZ zF<%P_>hj!~EIA&{SE&<|$P5!!hGB`EQT+B)GJ|HXLtE$TME9Lsay8Haf1VI*0rXgr z-Rxz8Z_zzNe?ZpOyqO7sV}!S33W)a|eD*pZ%U!eyNE>vL?Ou?HbbmFen=##0-@Tj0 zWMCkBNSXZibt6u!FxC6#oa;dL>3p3@iCRB9#r=6Tv+Ux0*k&fsc%^@Z8ti+2dt%2k zef=U(f*{#%V*ijfCE>YN_aL}KP_u$qcjj8S12=wIgN@Q=b*Cu!wr<`3_Q(|Hv^#Uv zNa|OK>DyE3|E?A_Cv%%Fh@03$O~bR`w-ex9>Klt?aMt}e>xQO-0ASQ5E4ns@UElbU{jNqREF*=50Js;6^CV3GL#i;px(TgGKIDzAr z5YcM-h5dA5YUbt)OH(^^RS~T0H>{ML51-+F6{+8Lmr(7MRlWJfhjz6;LwR)LDZ8{$ z9G+rhg$p6j(z(ZB0(LNYck=KXkS6t+{#gENPX~IB$NmZDVT5BDaJwz$#?g6Zf4F9w_Cb=HF;&-&<7O(H=6gSP zSbt)cGHuQToqpJz9n2fPt)pP*`t%rl&&YmJzd+00)wxleY0NMt1MRB$}h=O^3L{=eUF<7y&>#E^s%_7z)mW6h0ECUE?@rUlxWCMLNyqqQ5@mqh>$$rD zzMmx?4^k@nS^Xd?KjN1wfG8mPfGRCE@J<}eudh>%n(_Hnpziy9`(~V&0{ugs-%>6n`4#!gYTdiVR+j}HZkLeD; zruZcLXZi>FKt{#ex8eS*6*ZM{$t38l0ib+YH_duGQD0&_>>9I2nHmUyR%d93tyolu zkMg-TQ@eDFLyRK$U-r0etQFyI-hZap7V+`EsrxTJXY)byxjG zt~Ix(qOi$~depgAgkd{ z;t0|Ep7-HyhEK>_TIdU|{`wp@W8!5U?WJY_NRjv+Ux&3L9{MCbR9x*{It zHKm1;gCQP^*40~N$BunUlzJvkWZvDj%(!%Q*7obSm+UvAh{Oc5_57pjiE`Vm!5vZj z<5V6x4resX^J8^a9B0=N^}x4%W3SiZ6M0tJ<*pnbxy6g5rVFM&%uwFAjRLquS79Kd zLL9j1MP3o#^tP6FXBaMn>?imME3T<59xRNj1He)1=yLi!fdla6>r7wrKCL<~LI&7c z#liXVh&MSJiTe3>`ux!zz?<)yCD(}T%-+o_#4Z3L3}7*-8t1Ksuexvp$b(&wS7b5} z)}zf6eZ3qQ_Hrn$Yc%d{Nj^Uh3IW?P`Fv`?L1+Orvh9xjWCH9ARP)AFpy=jzu3@JTzA3( zDEN1vQ5aBgf_~4oyL*JWYZvqE86PBlcsA!{;j85BANnZs8Kdg3z&bi}LJaVTgH`j0 z(B;@Rt~zFO`47AilKHqz+{{)n`)`Vr-o0VVVi*Rxt3er*ffg3rMDZ|hXY4UtQD(#i zPb{&!;Oh4Y9}^fd>Ur{2+m*~p&aJe12CZ?MLgn?>pMd17gXFUZoy5A4RVI3ik_Qi$ zf6W{`<{|Mo+-KU@bBz7$Wr_if+XFuTL}kjY;iRF9d(VZgP35=H@{(Ip1D{rW$5!%N zq4mf6k$MMdy-S^crnRdxQ(#sHR^f8wTAr%F-Q>NqodIs-Z{F9Gf2I?E7MQ?Y23T9Z z6+;yS*3O9U*!xxwwp}a}oT3HaU-pzz1I}s(RS;))M!vOte%tdlqBz6K;jHVa$K67| z*JLVCI}WFB<7QxP{`(>StOjp8q$85VKk+wpYMU_s)&g4W#VRVnOj#Bv37B0jT+_7YKzKDqb2K%Mwo?fFen z05I~op!V!C4De%bkzjI!f^Y@3qPzD4Pgpj(ngQIh6FEXuLT2aQb2HNExSstRbDa?f+K+!Y$=_n!Y@gioW%I3A31`5;PT%2X-+Nxk+^(H+ zvn6kh{pUPdbJ}*|hUvePmU6_@H=?QEuvk;Muf|2x_n9?nM9#NgJBNgA&8wRKNM-}#-OWt`K``>M1D8Z45+PpIdOCR#&=`0H zi`O)_KWgCylUFCTt?2I;;+2Aad+!p91y!DOZbT{Ay|CKt@O+tLopzbo-Ur9OlxFT% zt~d~8Ogk6d=blU4e8er2wq3ByHt_}2CdFLA8w7==NKbz7r`*(jmDt4Y9#l}!t(g$d z)Me@ip14D5YTxg>QE3xY<9#dGkBBlwTYB$m&-BPtjZdmWsMj*=49%HLye|j z=JD#ns)nqvsVqqig<@)Ac@VH#<_XRgY*Eq>Hf4f9OBS{>&paw`a_4Q$gmeSbi& zCXG(S_u@RX7u3(yWM*dZSp7$^nA&##xJt1dO zVZ(`C69kg^PzvCwoI1<2;P_bM*z>I$yPk5;@26=h4>HeQvqGau7-IfgdZCH^1xu&?SrHLOH@NOrc|VN>B(ZrhYN|1QgriT zZId1>8((#ZJ$Gc}wN#xmCi1*=vU~JB3H2)2?R*H)FQX>q8?B@a7dRa2p}503)&n0P zj7>Vim!@5&+RD%F6d2E3#iepfywo*j80L(Lz$YJ6($M6#;yv0x3C6I_0P@uNyD_-Z z?8cw5s<>`oYv=!tkHae1n!V3rEQ>yTd&&BYca;P(7W6Kz4uUJY7)P_`2-Mof%6aw@2$B6%0T(Dhhec~q zRsr7mGTt7Y7%eS}ZV%O20in&EbO8X8&kf1_|M|>=#y)&Cr0~RqYsT}bJ{v=}@&1Te z0+qAPH{2CUuagmU#S%}xSg{q|YaS#W_MV3->A8y$f$6h7Q)BotKMpFY$qkmxMz}lE z5T}1dVM?jczV)Xj2EuL~V>WObsU93#%zskHReczjWSsJPGaIX!p-OZEn+dswv$BF% zzkRBfJ+G6Fj9;~aX4xm`k8?sT(1#~9RZgyrrM{QgeB$uWP4Dt@I#gn_(Ohzba#!e) zI_yT-QFet~ErYmxqFwe$ymGi2Cw-f3T@_DWJKvMyX+6)DKRN75`N73~)vnY1pR<5c zxuZJU1>!IuYCKZ(;C=3}1bx!pRpU=%Ag(%a#HRRbbyEV=X0inB+E}afqtS!bxr#m2 zHbgr%&Oui5J_cy!DogD#k&-9@UwVbW~+W>@C&i@ELtEJfWfKb<7 z28473MkYz%U}Hs(IGIuh+Cs2rl|=a**L&<>G%7Y*>=V{qdQfrWGohQ5ZhgE2owykz ze@MS}4!Jop&U7WcNr1Tb_m#14&k}qrc#4La#i&~VW8U(d`X&x$G;zSBHZD(}pqs06 z24Z$h=Blo}>H)izPx2Q6vBb|1vUSXjRDGC5BQ-p5(IW3y7=2eqZLj!?#cfB|UR0a; zbJvtPl(`!0Swe-?ySSd%hltj1h37DdjkUP=No;bBmAofM?*6W?*cWxaHcf9q58kC> z9WQd#KZymDc5g4OF}8ea^}d6zv-{2VkH4$jvX3;*EZ)+4t?*3sobuza8#48i*n|~! z(r6!+$isE8C`X$ZR`i}_o$^cO;z<$jr$3sD)v~W-6j2V)qbm^ChU&`d&fq= zp1Ji66vKM`?QXwoWx2aaZfDl65siFqK3cz5t5U2A#<|-da+!5BgtHZ}{QhgJXjVHn z8-P7@8}Fob%*gwDIo`Tmf@K2XZ-TD)rT`%OLn?8DjeSniaCl<1;`PbK470Wur9^-T z;zADHX?C-}3l_6ebJUMe%E`K(t&_KVbmznQu}}S`!&*17VZ_f1kmwZ<_V@sp3gE%_ z{!>m$HJ3cp-;Gwtjw;ec31ZaO+wwUQt#HT&O1YYq6Co_$thl+p!8zo6@-<@}Bm3M0 z2d=)&*Hqmf_f1$W!0t#Mi=PHQcaEdo{wF<_^0acw^3Z7Eu>pSBT)gXv=U?BhR4l8u zi?6?_y@{x#jXn9dpx^3up!^|ekHTpuxn8|52g9T@C{DqX0lEBVpuI<{^qxOKn#W>e z>7CVh7wE9v2N*qKHUuw*V*7QjIgA68&#!QF3?*Q+e3V*uWxHAnAjdD#z*hzyuaMs4 z!7mKj=L}%(>tXj^e7LPM`gewx@^76jYy?F%J~H0@GkR-N2jy(3hN}4}dS4poY0D<> zY^wNp>$H4O6gH#`R%w`}Uw<|*R}9!Vz-i8}hT@VRSp|6zJ~4Mj3Sz}j#T8_Cl`cy;HXPA~ONMo;MUAA)~ps(R*rDAgH?vme51_+pfTsvM0c{3jLWyP-i& zp)Y-{HDgPuNIm=iO*OXB)p7bc?A^SmQ1N+%YzErMT&b;5DK9XahtAzrOFEeILp8Jt zsMkeRez@YP)baPu+p!kgVYL%U+CJNAD6ymwAc;%6_g}Q{@S(x^?asG!6M}LQJ{_b) zNtCV%Rem3IZWKId#CA>4i0}8QeVRdyNQfge6f4Gd_S(aAgJWw=UMsYiAD^Fa+|->J zByUp>+|oQYziO7ZFe@Sd*qB*hnZoy(c54qG4XOo41&p+UaBI|EtXjp~{RP=W-~=u* zxY4KM!-+@HzJzG9x(}v%WqP9SA+D+RUHY_sJaL5m=}UCLqq?~DI-eiC%m2C8!#wIE zW;V5;U!s6P5u1GIg5nSVpdbD?8c%T`aTOVGE!}@n9i*)J&u|<-$C3(~Z-MFKb&7|K z+@lKLFWxLo(9)!5Rf2X2nb_WDf>hY@PNYt~em$6ngU?XJKysZZlLLpR6uzp|Yz_bC z{bHiFGl*3x=ASui^o5G(6u+unL_}k5)4@e`1<)EU${nb31gMrGLB>5ptN|=RGBce1 zU!_cK|AWB(jR5eb{&k5CF6NOwWdP}O=G|N%m!AjQ6_CEsbwR8Q( zv{k#CH|)ADs>X|9RBo^vC4Dd*1z)r=-@FxFMM{vK2ka(5`2@41MnM=2Tu4 zp|$*N=S3Pz48=~MhP}V-)tQ-Ekw${~f*)2cu<*z*?}&ER!q|-5Pmw37h2K92x6y+K zMU9(E&=s{|v!4h`v(MW2Ya{^tIPiL$UtR5p?;~?`kGVaIR+6^#Usr)0@`$46rq@;M zjV(SUj!i>m1pG-qRXJC;!eP#Tz`T4yilY4!!A5^-P!SuSnf6lk5#e=tD?atXK6I?( zh7b}2z-_|5Bg0g}07Nt3XX^mpcyX<4K+94;#PZ zSF6)Hm&&`Q;5l<%pZykw8|=)YtQ;zjqG6&K&Mp$CFDB(v?E5cyO3@6ZDB1&Af#aW# zK=)F0G>tMQQCt;49*(3M4?W4;#&|bFVLj1(d%!IMr$P%(|Ba;w-hK3`-jRwEmh zmNl5@%^RP%7Sk~j%b!!eHPfHpyVTLUB&K5J7fgGeq^-13B6xEB0P}scGv+jl*ReRQ zg}?|PqF)FZ9>(Z0nu5gWVF1L3g->G9YT$Cj=a$O<7^-Ijv97^-H`d@_uW9NyVEbZe zhHh}uw?e84Qlc0?Zu@3PSyMzvA3=tTz>ZcoxL>z50Zd!6NjVnA7aNQ2h<~|1g$NH? z4|Q9A(~RC+3H~#SbqC@PVHE#}k>o{{dM+?j_iqASwR~gMS((1aMhsE{Dyo|(zZ)fF zi%fc>Fr7jY3~=wx!8ao_Dw!WuUP6dm-;;|%+cwi!$C>jrGbnvp%1JNO=qH7%R3$Re z^$kr)UF@n?R@MuC5Iw`#$}SgciT-ofeoa$+$5TybX!w`?_uuU&3AfH?_a2)UJ@ni3 zb=*?dXOotAD#y$8+T-=k-by0ot2as%e1cojMK)LHJFie%XPd*M!p!h?8!Z~8mKDa~ z85Fdc(!G4zxVU$P4*jvHvvR6prt+E1Fw@$9F7#5Ow;$3FaPAko|t_tzjj|s-h1~L04iSw-LAcpC7h+Kf%^Fq#_rVt{*yK%=K3E zvUmL%RiH&)<;kBY#T)y$8j%`F*lVyavhYiiaCyN=k2%vHd{n3SuQ6M>m}cVGJ<$uoH=0=m zqattVQ(hkiE^$dIK*jkRs*h--&Devn1Y)a&wgjkrQ&gL!ZnXo>9{~6-$U>NeM=>CTTHY>DUzXSLUM5dcKN@oOsCBa z=RtG|W1{ydL~t^9G-#|y_Ug4hoZp~bjIpu4-vzqB9%e%2umm zs5(m&G$qP)m8!QE(v6LYb@qf>R_^!=p)II8Ukn#;?e6k$xCNl5eL(t$6EcS1KsrZC ze0xSEY2_ccqpCEJzMNXMp7 zcV&O6@P2NKU@MajhK?pE-{B!!SP^L_A~w%7bL)?=Z@}_@+Hb?Ot;5&LO4e(XO?}*= zJ>Q?aVHb_3u%QGVKI-TP}tmb!R^TQelc80DoB$I!53q^;zn7P^Q=zDW1&n(_bNicw!f zz|)N^vpG{tgs8!X(Etw_4qh^sEGeq*H|MbkbXkK-oX(~}+VnQKrIa=ekkbwcEiG$O zAD|}xQ;x3~|{iQ*q7CHOu?>ZAQXhW0->YChJK#SIbelSm`` z{!FAfYDF@$!chP8krEkuD6&Hd!)v-CY05~+*3w%zQ`#IL#!XSLZ4rU*R;pThsY6{~grWqd9^!M1YRe2<6AvD2*dc>*8kqTWgRNT!=Pm$sBO@V157dE~o zU+o@(tu`#&M51)M>redMO)!)Yjz7Z`j{huHR;kKX`H3`j+z|YAXJH!ehCgS4qCj-Qrl)O}a9c>pT z%WBW&9P(~sqtf@b)3BP8-kXo#x36S!h2IPfgA_TfMLRi$AzfoM;*vxi&QoJ%9h!_$@LoCi{+fE>pRV^;HQvp{Y0(H#M2$s;r;seEy;zlf zry0Xjg-N>W*3-)z8`{sHd+#H&O?}SQACo^gEPoI;&?SxTUOIZe{oITKk{5vebRs`G zrE5l+>x+TBy}<_77J)m}bNyHnyW?79mu9UG1T&S5v0{ve+4nW#4fUIgfhqCZ&@`Ck1i2FxRH=H#9$;?~L->TR-y%b5w0_FJ|9! zZ^~qbM9#Z$Ql8eiE#MvnQv(-`=2qb#2{+75ZrJZKaNJJNmi%iS5^R%gG^FqFq!vqB zjI8FO&{}UtcfWmcI?plc;$^x;Yst;|*~qb?u5mm?SvuJgrcSL2Eec7!N z=Z~1k3)V|hTL52K>ZIhwDak3Z=}SHD8xtFh+`dAJbISJD_1limQ|w$PDu!o6Z$#2k zUyRH=wN%o0boAZu>^Z&XOYL#%OMzeAuM=!e#Vjva#TF8q($0WU>c*8fW>PZ8QrCz> z=Vonv8=J0p?%G4t5!I5LwdFQh*X#3eg;2yZD!Zj|+qJH11=ikW+-*%{0iH8#H z?}_pcm(O(Q_|*9-Qyj0ccs{M$ck3Ozmg#lgW#nh(=dVND*N-0hJztjZ-s-J&ChgaX zW7tDr&`|lcFvs_#qc;D(xc2MW%7c>&xn8DajZ?2uFyx-%+)_(+~!G& zyBZ%uI?xj6Rkhoas;3;`Y@vIpc0#uK&zc&PuOH!k(}RLC#%03eS`A#)Q@x{dw9~Z( zNZS_!<0#vQ!8@MSCs}Ks^WK%X=I5cZr(;zAYRx&!?j+$0rjljBe1VUyi1^w4z~GL| z?T>QqzIBdYoER~=oYuY9xSu?V^$)yx&d<_AG3tfW(!@AwcloSB>8=miiqCXMoxg@! zkNOGZw|x9oJQDHk+qnFeO#FLg*2dbHuTNK9@3}(r8h6cMQVms-tLNLLSzyGMw!5pL zPX+lHIR|U{8C(g$LkW4Qwf@Fr@N#>S(2`Q#t|4N+BlfW)~`xb8j7I=nKQv$2y)zi|7nRlopOpgx-jBq^CEmT7 zoOsaTv@2Pu&QLTq-Dk@rJOAHcss8f>AKH>+b2`d5`cC)S`iU*CIygrzvuE#ipRI^b z6YZWq`P5;4wJ_O3lM)@bb88IGtb5WolJ^m{cTH5(i=M^oYvhZIsTX!_&Z!O#3?WG` zGYx~;Xx}i9QP@q$&ko*JMeBaO;{6ia;eN&9P8nV-&e0Wn=)9(Tsmud2E#3DKpGf`Zp6u_t>06=mdf+<*%7-XWt z@QsiXQ287=!xu9($7Guh<8SF`Zyxi2c_ay5u5bP93pRO(a4CVV=e*hq+gZxT=eK2; zY@&_F=n5iaIlwmsq%r{KnjH@;E(Kh{>E!eriu|6jJ({6eg?rIB;L)1vJV*YxF}Ig& z`yvk%qlJQ`DKa19nla2M)hpbLdn+Zkrk9$X-l+KOh@!#IeH4?H>|Kp5*AcN>KBwob z_rX8k>{hu2)u2keY=#L0f7#sCn2&OHjd%p6PD}lp*lWwTo97=5qq4aV&*O3`v8TowT^wrm81-^ z;en~0^Iiw;6la`D6hT6znJ&d)AbGA7zVlA0T#6A-jTr2!>etOUg(7dfdzl9&$HhIQ z67YgGHBD9TY`N?9frxaVh4Pji7t`&tKOlLz;W$lNhKy`@WL3*^7Y(XxdkN9Pyn8fP z07X|e;x&gr@*VA1!O3xo`)YdB(ja2z$?+}EiA=Y9@xYzUddjV35GNb?02=`DlJm-Q z6KzKC?)8RM^lj8$TonFZV_!k?Crn$g19nRRyanC4upyd1OTX?}(8en`_h}+jJ14{s zvYyY8nnkQnVw_n&0FlpjIk~f+kn+AGuUcoQ4W@V*cLqLlm1)^O;d*VCt%kPOwVB)t z%5#3isZg91s7-V7UB)Ds)JZpuG{h3?kK?CPzv{Dp$9ZVMH>weAa(9xBeJ{z-B2#}V zHeuA+-Qem2kKLVoR(M)QqCXd+Ed(G;;xdgM#pQ?DYhuKea*p3t@{U&4+PU$(l+FN= z%s){vX@8)`g^VxNh_$<;qx}{&j2w)rE@BwPA(FpL#2p#x>fix zxmaUrIM%k3>z?vfkQsuEqwAQ1Q=6nfyKHar=j&An*|odh69(LV)$_7#gP&Rfuoo@^ zyLG~6I+b!V#e!^pen&hmq;XbK;?plUos>-sq(KehJk+yGvV6mUI6VEn{p3BJ^lt3& z9nG;Cjlm77#2%!7+jvEt^r{Kz#&*p`g7b}D?#AL49t!R8Z{?CUa`K^q&u+t{l9HV| zaIdhVTdrG%{eHC-*xk(4NzHNY;w@w>{Zp6XNmDN4PRN!AbMUj>D+3xv~aSgd#Y#8 zilyAv|12vtE`~>DTFBwQGoaKJ-a;FMZ#ib0X%QTXC{a;Si3mn&53uuESy0u8(R`hJ z9@<;nd-EDS{m0$CIjvzpSW$7NZsjal8t1CQ1r}kd=QNdRhh{=|FT5mg`D#T!ijEsq zGaonVhi;p(ke_9!6DK3el2IQ^5IRicMooyu|6ZV|KOF^F)n z)}dN~ePIlhA>C1M!X@1c5wfWw?=W}=NF8L#+Xuog7c(Zthyf)bQ4@)mIU!zL;wL6* zkR_kNlph74KA49_-&WcvGe31d>(tFBN;l+A{V+#;#33?QPJO3i{?N^S0Z9Ej%`)%_ z?nL>tVnnVEI!+3Y6ZnM))ju+!^*N07HVs;L&tg998a__Vx!W#!i-6_CZ>HS;2LUCv?~@>2kRbIWAG2io3rfZdRhnRv~+IJaq)d$XK*vTSx{@eSfjh zcOmj0s{CuF(k@PZ=g$28e^{2yY>$6`QtG4>yG*NNhS*WL-WN+d4cY)D!DcQK+s zs2M^ zo2DLgL_TV-mkRKl6i7ee=v%6Jbxy@d&Ot3lB z|K6_j|3zEs^?#s+>;nW1$ebdWI-3sRQ?~*FaM7={bSCF?jRE@kAG4@GD)SLrPTo82 z+P`nL7u1xX^JD&kO@qUSE7@6IDOw^!Owx{J1vfJtC3K0)Lx-4`=|Ork1{-=aK^4k) z%qq@8N8#T&i=JP`HFpn(_gS<{EN%)p52%=S0K%~tZOS|KeFgi^%B94>{DXn5vUqu& zVAt7Z>^C~*BksoIpQh)KjuOv= zQ7zi{uAt`tnB|x&cHyPP=@C!&AFhBPe8k`w7HspBEjqdw_jFgQ)Fv)nV}~K^T5x%) zZ1X2-OtGEr2kPG6zSy2+Lbwvp=XQoffwn~|TYW5WdJ zre@{sy0`y4Azd>B{gmE33}DM@cK&H z5%beS)HZJ>6gq^CY7=*g<M)7w5a23K9>)d59d#^4H*=A4Gbp~H9$F@#MA1}Od-

dGXLvI=V0y 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±UbUC>@Jw*T)vwBcsTdXnFC<2cdW-U&8(%z@ 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

G>CICjdLMZJB4mXG)Bs-TZ{W1|O_{1N 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 xxxh{n+5+ITuEVXRs2 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

BJQim%dY-ZMy29TD8e;#$%6v**2ivlYGyK9 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 z�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�-Q0$7a*pP-85cU|N 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

<+6)>T2L6$&y&QTVPMr)wh~)> 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

O+ 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

;Gs&v3ErT+UQqPo?p8luN)m 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

?lk%Te#pJ&#U2XmvyPg$U&b% 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/ub37*pv%1Tc1|M8t(S4 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%0ANnIx@vPyE;q7M7`xwr8 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 S61~Cr2UT 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�LxGnc`fUzBKF6=#-IJa7SKv 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⌤VLK)< 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

Qknt@;7Faef4QLNTlTD>d3Usq9wi! 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�{&l4xu#2q}YHfC~F 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

q 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 ?ZcqIj{|K*vzU_E_K5)a0I0J zy{HGl)1yK(|30%@cwtw^6CHg3Xkmba*TG?#A#as+l$nZc!-%yahocO%=A_qeu9+dN zU_0t*)@!YNoj(YTzM1zUlqbn+1FR7p^{;0GTHv^qucrMsk$fWuS{N|bRFBF^(<+ygmU_8yhy`3@trjOjH<@mf zB|xSWXda~pd@`&`r)z66H0s&fDg44}`W6?D8E?8#atMMjD93TyP(YAB?iLw9EBJud zlAnL)v&w|X*|(mr+Y3ktq~A(IP4y0NO+hV?UgtP3fexJc2**>V09wy>q&~5-;#t(v zFf@8x2Mc!mJj!tQYV2!Eb=1vBX+&KDv!2%6U${67fZ%)s7CxcmC%{e8c_raOOw!}8 zJZLk5>Nkc z-jKJBwV2Ug%SbvGrjPZrTNu*a$kFbsCPap1Yft)!eIvvP1|{w5l?rTCgq1 z1D-AWF7ufqC5V?Yz6KZfU8?&tD>8uyRjmR*$Z=RdIw3ue96eB({>UgJ?~IZNjsu;0 zv3GcgdtQ0mDtCJdD<-(0P>0n9>;8)9D+k#X*5?ap;Nt_o-(qgQp%-K%*zYqgE*wTt9f)c}*0S$F+> zNyhXOirqko0*E99?eJH{iw_d;(aoaMgFFTWGGNKedzBARU9KX0jXh6|Y>@72u9ASV z>DUmT{p-(;fArqOs^K?NwwvesZ=p14Wdn^PW_kB%m}I?ZgpKi)E&|+ zC=^F3NW4w_;l+2fef{I#TRbsu|1Ju=vUv2*Yn|U9MVbMo-Zv~k`eOpwkjm;qPj0Go zAeQwM-_#5=UPzc5*8AG7&`+_B<`hN+dBD zg7rskmnhc{%q@uKn_qr1qzDIJt;hE_u&JU!3Rh;-7Op`8z2-CC07}tB)zL`&+hjpV z=E&o7!J&Ojx6Q9BdZ$JfM%pbR=nJ=LYG>%`wRE>SO=83#@iKqjY4w+Nm_s!9z?f<= z0ElJ(qYj{xQ%VcjT{_8hT^cPkgK_xXC&^td-iEi8#Q%#-x60RT;fz7`Laj1jss!#< z@qqm_@6`tX#0ov@J00VfxTRRBSq5;SL{_5ve^!m2o =(Gd%8>$KDi%?=U#n5Asf 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

ZcDPm!xX$%=4f-ZT!RQ*LJ3{Q4cKJa3n?$?{| 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

(J9!b2pr^+@Q7nx; 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 F0KbrgvIq<&&l$ZbNdgO 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

3|a&c?709Z)it> z(pspPLODsQDkQ`sevSe)Fil`Ck20dwFy*L(E=o{%suP{Ykr=!b#x6EQjG`tJTf403 zhhDQSn(7G`xR4=nVT_F?P6avJOrWFx;HXC(2ddpDPP-}n4dW~)-rB&%rJCcF6H`Jig=RP#`F-U}9^W!_&z0 zGXD}1^e}Z^RHaBBGGfZ@F{m97Vr7LSB;pMv;bF7{w#-u?e|EBr%w%q<<4L$n+@L;Py~h0T&pGlQ`&radPlU4lt7m$>c(} zMA4osAfgMkTq($yrYa4kXPzuG8NS5!&6H7FTdE>k%xGsbamGxTz9Of-q2|x0>TYXR z2u6@M%yZ+ zDfP{y)d=)5W)4@$ShD~pyltPQv1b`n!X;6YxZ4G)KD~)pk6_DB@vn8Ys zl<8gk`8R`)Q0oTs`t7`?Pm_$o6Pw|TB|b^QNAf^Ee`P2V?FiU0BmvieHUH>gM}Emm z=1uK|k!U`*Oc=S8dB1bbPecoG{6Wrr%OE4DW}aPlL*}K)$P)?m&wR)cZUA@syyph^ z#`gt6+a|3zWi9mtlM%J7rWY1K%MWEPSn?zn;^!r8XEM1Vk;oU~>nYFIiOcGIi>SMR zCMzk2?NaKJa)RRRv|T3E-%gbQSk+w2xB=tOh2F+qv8vKVhpDW3LpjQ+7dezO4m6I# zbkt=B&h`Q<9EF6uPAgi-h0Fv$1P=EW2`hfaDtPDL@`Hn%;7PW^X*g(0k|NY9sYx&_ z!z9MD%0ph}OsH<@KEB&gv3BD20>^9TvDP$2LFOYjsSkhN(bQw z#@gow6NH!Mg$7jyK)T>V3Mf8kMrDLVu+F7MCgK8UV*{L^TTCy?Ho%cGOK8+d0sD-J zcyCRdOH$y%oV2X99OW&L>uucTFks7`FyK1;>Er-~J09aY5`$1GV=nxJOn~SDeDCHI z#WTR7OpfTe*rWrrj5cbZqdubs4vlbFL(pJ_yvBfVtc(3rs=U~5j$Gw(La{e0r~f)< zjz}XAv*R(`qR!SrRm9A9j3S(NY7N)MXgEk}IDjnH=}LGe)Uu>3B2crYEZ`vN*Ye7# zghY9yDg-m{BmhROUhqLg%o^cJ29?kvN=XOhXCz=mer_oVsQ(A#%Ol3l%Cs<$uXwO@kN&#X|ldcfo5T|DZ&g#?x1Mj2DL`w`G zYF*kSY0zYwuf>=t4^VC=DnVZ4}eXy)*|J^lv$$>xv9zFGfe9E~N=ZV}(>po%RDM zo@70)qJ^SjU3zCLa7U*oAX++LzgQr5fPw?0B0Lr;Ev^#bSb*2Qr5#CWAny%6MuG+{ zrXm~$LlQ1o7x#+a!Vx6oN+-i?6~F$6{h1?f-2c6<`AxqLDD7e&!9YxXnaN zLLiBZB%Uys7G}oIW!%gyuRQDCHh^0$p!G1~AKHToy5%1Y^5%L6HF$?LAxOa%&@CO( zQ;Y^b7V(D?s&3TAYZfKyxMLjt>7VedZcuG83Z-o_Bg@7_5&PzZo&;}DMJg`3|A>xyOp?8AaM zC=Ac8ce15-jz$BuWh;?lDgsG7uq1bejRSv&7oA5vDrsJ(r!~2d*BXE&kO$df0zp{e zAH-)G+3MLKM0~ynMfYw*4#-|A!b6zRuxw%>(EpLcIzTk2U;`?mA$$bKYAi(B=axc~ zCWfirD&~Dg-P&*vSEe0!repsyxdqdq$nQ2{jYUC@KKq(y~gbq`@?>N(X3MD8j2`G$Hb3V)SLN zw#Q%`q(KG`eEj9DRFoVh%Lo~w3&K?;vi}Wa@M<8#Wj?gj!fvhud|(62YLXOU8X|#2 zSRqIk=E1P-MmEMGbWD0SW(o)BLm+7*?9!8>v>@*dHJD2MPUYncC;hZ@T?XzEDK__X z(P)HIIJg6;_B83BHxZk#^DNfOoS2o5P0QVbuG1>%MF2(?Ve#Nyxr zY5=D#)+A2j!gL5Kh}8BK>#1+Tk5Pw0FepGqU$HROLSL^E3W&5=CP* za85iQjpiWj=O%5@LW;baqd3q=z0MQ=`tMUS>dVGN4c`z}0WK?(hJ&usZB64M|MzHe zMgt2VWl7CE&Qd5iC?goGVljZTHY;7eC6b&>M)R_ROM-6^uR>t3dlF_tN*I3zh#@NX z0lJ`ofHX3lYR8PHva)vrZ2v@Hqi5cD+nK7b{DJOj3wVH~>4RM9ju%dux{^ahJ5%#JT3wP-rV_3TlZKsVZoXOVkN! z1nxdw);`qlOMC71S@j~;HpIGc4}t`6lgrCG{$KIt)*!WY(LWDd3>!s;>A7YF4x`z zl#Y-a`K18@hF}g4AKFUr3Z~rV#WOW#3iT>vDoc}EFIl(rHC@l$@XBR0M&1&0eiTGO zKI8^=q$H-{$OQH^&;KKMCeyQ+N|~N1cvPjFYoLmsBq~>k_P{~`ArjWI6O3zaR7%LS zj$(z(qE_8PhS1QpNJ|ab=4VETc+-*s;(1Dbrb%XMJusq6VYgex3Ez%DeP0D{x{Edp z=QdIbR&EY)K3dXvBU5+7RlsYJCkGXa5;>?qHi)A>E9VNqv{d$@bsn$+)@dzB`AViX zE8lP{Xkd8_DNnEjJUnY!utaDE*ghCcBgO-Gf(K`&q%u3HUdW>-2!bV8f+w66#r$P^ z4$s8AC-ByaLww|J%atLf0o-;;0V1n@CX?4%)0VeXB(@Z~?x&hWBC_P_2HhtQ{z1pU z1tA`0-XPDeIR8dR7{X>Oa6HKQ3L%Jf|06a5a$z?BD=*N>sCZ3I$Y{9OfEjXWkV4B; z%Zs*YB1y;rYVBeb5mC<2DbB=;SSz*KMZv5}vmUUX+cGJ9$6*syC)rsw5+}NDQd7Q^ zxNRCYq_U$SC(;gi=vta5&C@qnI#kDC1=^@LCg&9ANI3Y$X2ndr--ayShSc~d;fg|8 z*tROff{7v{pt8d%F8MxCb4)!MDAuDqIEy`mVs1Z)JU~#xL~kIROv38rM+hWl4ki*( zDMF&nT2<-UP*_G*MkfMGCw}al-%;+EtaZ2ahPSi^vY9Z0)MfrE^M=$icQErx0%SB# znuq5ZqyL%0ZlD2B^B`rt0UXw@^N zHqmlAC%u{@=@xmVkRucg=Zd*Q1E}IFiiQRh@fXo{O}gZcMtLe+2X>ZaT9{=)F>tiJ zl0VX}1gUz$>Iam9>E7~Sdc07RlqV<5DynUUno*1#JivTd@LE@C@Ip*{P*faA?_1|} zah3T+NIiJwu62c!en4~EV0=kq97a%re)<}wd3Zh&tZuRTTvp{A=1iYJ4|4kOkQZfLQYZA1ft-AX-tv~VF-%UFiH|tRlM-# zD!NL$-Lpho%>HqsYvmGo1ByqwQKu5R8}(Oa%KVVb9@wx$~=K`9wS#jp8>9UmCN?@ur z+PWt&yQk_&pETzSdGl-aV{@y6cgua?~i z9W!}atXL0xTRb_u2wD)dG1z8%<>>;=DpEoc~Ku z%!!`jQ~({cX&mH;xZY5MyMg69BSsWd8NXLg%OC^&NAh((JOEK0OU zrXs})8#-S2SEJ)UG8QYcxG+H_3nwfnbZF9qg#(&29q7bBbHW4`O>RzbApd}<&6^k| zG%2dHDN&sQq82qK)hSLEJ{_=_*#rYt2N*ge<I378BEGI=Lp7+rt{ zhSCCP7&A6nwP^tbFbG&Znl*&H)|5&8>D!T${n+;Bld{uOi( zc;NlRB8vkaaRZD9nOKmCdQrjFNPvyRmtbn0xB*2yZh)6;EZ8)Z17zF;R!%_O6x0O0 zQ7M#_G%&?fQ@uUa+m=B!@l#elC7F~@6Nr^&2NNtn0aj_PrPdKA)o_De8?aDA4HtA2 z5=DGn@YzXp5=JLrgdrLjV;LY87+{Mn3K?RQNrspv8B{h|2AVC_sb!UELQ-CwEu~rk z1vp>~4Xm!3+H6Icb{Yn!qKW|r9AL132Djp|03NyGT7XP!QUqd3$Tr6vhug3~y}Apb%H5i}q}2yUAmh66hE z;(sY76fc7785A#r4jtjd5gQ!T;Dzf!r;tMfEwn9*{DQZlL=RcZ9dr~9NvDw=97YCQ zge93|O+Zm(@@F(DNrqWtvYBL>N$P}^07G@TrIT7VL6xdf19 zMV3!Tm|Q`Uf`0b$XH%Qp!C-w_O-2TT{q@ghRFHAlo`fAnQre8EJtZ%2dQyXol##TVExK@s9nrkjl*0mE=P?mNV z9l_Re%PLsiv&^OLL~#mGkkGaX377}x&!IR?1MrcHPyc`Y9Yde*_UVV8eo2t?-^2|$ zgdo8S`Bz}PEdKMay91HupN0itc;R#Iibq}Z$TJL}K`H7BJ&Wq8r|&=tJfHx2<0;(X z22|WIB1mtU81)5js&%qwJjsicMB^Da9R(*%n$l=wLlhb;G z(2N!|0pLVTq6y1rWEK`Su>~|UuyCqb6(IyLjx4RFa9l`D;-I)H(kxH_mb(qjjv$kY z`~(AJ?0}3slCSO{O92#$j{6Kk0rxTM0oQuK9RFKqN7xC-1L>i_bjA}e^^mJv@)=is zB0-=0kV{j&-qy7ALoTfA@zC@ic}On*uAcOm_*3<)VGlLaVUG` z$(OzwLIs6b=OG8%z>ZcjEhDWYYAXZgR)7MOtSlubXhPFbh}jjKOt35=G)N1VOhX!dUUd44y6MHoInf7Km*YH2o-3&4sRRcb=A96@ux%%$jNYX7Y`a;`jNG!Gn?<-Tv3GCJi!U;Gq;J4x>6 zkmOpVeja&@Bq%Zu)96nZE|dr9rRZJ`c^E^W<5Ub)Qbyt-&;}$nRf|^Ed zbP`#fF=Yaiby8E7k~5;+gf)rn%>+05K(J70G$U0e%ES~Up|mVbEX2#$MiLUh7%47@ z0$idR<2HjHlx+oN%-nvnV zE*?5S9PqYpbSl0+dvKSWTYVZh@YS_+h0l2nML)}pA02p5hn#u zCKB;7p8{1wMKw<-W@&L7%^*j2qs`civ@FEhZc@VHQu2nbI?bU_MwpYF_Bn@)m6l`s zxWl@H6etOFcvf4TH%a9|IR6j&g-?6rgOB?p)js_hSBfP0EQJ(ULEKRhAPYAg7BAPg*v#*Q-%+?Tew)8eY=-Oq%LNle# z_95_S`VBxp*kJJ#j@oV*6?@gxg=WUzEW4!SW?a!Ps2;$|*qj_1!FbXKuNkDcUonw= z>E<;qCEkvga;v;2R1r;m)Qa;|=ZrPq-(A7z&xj>mbMp$_%@9?%s7=om3J}o~(Fk~8+kg473rm}w*r7| zH$J|ZB^*hiT8D&$^qGqRo^b%j{5{){$ zk7!Z(y(e`;z`IKQ~m3^rm7F#|5c|f)zoaO#%+0wEz0s!FlJMx z9b0Bkn_1sM>98e*fAsWs@B1)j)ourAJH69Tm+0p1N9|Ar`8M7*sKa z8t52d#6PP9UJ;Lr)TXD}KjUg*(P?rNNF-283!3KAsRe6x_&5O+XKBM{w9;0x>lMY$ zO&@hW>b1!capvm0?N8sE-b|47F#VIONo7a~G9iLIOMx)_r!P9|Wq8qQ%bBlo_r97* zd@<)ZkUkK?M7|KV2;tJAR`=6=TUUcxcQvPMf#Re7#vP{DTI|n9#Sm;Y{l*Qq9sF#{ zuwNypZBcHHV*YqY6lw)xCl=+^!SMKOU|C^(5EI$a$*}7XJ3Gb$=lEYfbiDMwzVad@ ztxxqRLsj~iO~x=t!9+dFNT0Q=R!BUQ`1K^n$fQCqP}^5Mx!NRy?OLWLZD}om*sh59 zVi>g;5!37uJSL$lRU>@-^H(o=(FuIu)#Lqs|2XJamj4rEG^`;gY=hMm3c%{m)|dO9;Y;zt zb_1}JTVTI67^`_X3<-0pXW*iS?kJ6fiD4=!x5AXYMEw#Nk(pzlj2RvabJBfMDU(3Y z8Z*vjmmk-IUT;q+>OV;_R99BD6^hE!I%9HNkyZx6b-c{8(yOY9koeMNYR;A$vnJGM zW0uE3hrg(-RTV_{g}d>y8_2*}I`AD6BT|YHVmJut6$YU*OenzXRERAFXuGWmXPB2` z6&7!2r{7}g#B?@~4S4mX9}^^qf#J9mUx#Z*LeK>yKieX367 z(c#pFnV)4smBz$a;|A+49vv(+WbCq{RzqmaWtTITa!Z>f>+p^7VyT+zjeI^0P$dZj z0ti1L0uaC+Knt+{?ErUxxp9nxEN4QFkIo5K4H2Xu4l5Gr?H27z$B4kiF~|fzdMVrA zTw6ZJUD1Q8$#hX?xhe-ZlPUw$js$wL+|_x0`Z38a{R;F2gj=M)hn-g02Q3*vpNoONjHQ zRF|AewpH?_swv5dJ6q`$gc!#-Xr6RZ%Jnu#r)l#%)T07@Lk^l8v{cFV(5=mnGSyKm z;-xFd;53vKvONrs1XGx9M)3iRNKc0X4`qKBa(O|9fwpp5BqJ*|BGt<*#9lMs-@@D8 z*hEjA7it^jZE2(^UKC)K=B(CIo_x&LD$+&AObr(k5||ohV{W8X;-i}EVJMD*vV9Co z{IxRaW=9+}G~~oCwx3Ce3}t(ovD``ZWh^c;CeG88=|T#oSvN-L6b4dl50IP;yqfI(_E96xh?ngte`_0UnJXQ<7)t_!?wD?`l^N>eZKOZp%H9eCPIs^eD&b7|)KzlIs_Wyj-o1#RqlQWuyhTXU2Iu8LIR) zW%X61ds~v2A(my4ZcbE_(;@a{G1jf62`7&p$w-blUmV+TI(_m+7t>xb-qlD&0(tvt zyZb)b=WgA9p~b^QsWy?480wNA=~xnU;J-UVg{Ka8Rc5EeMOJ6{)ECAFA5cEz=TOFu z;KVxQXM}W@C-V|Rviyug>0iDszsM9Xpg_r4uTLXN4R{ zR#qDus=w4+WUqkBV$cfX54P|E*A^$3u69LX!KF#T^=XHH{BQfHo^BI6oo!|M{@L38 zCV=e$&NXVLxrbWY75uCDsW^c|W>Km+KH*VZ@+^l;kdS4k{t7E|! zjC~bXp;gjvoIk%gFE&%U{pEtc_k%|rmXF$IQ2pcnXDi>f1u7bwz()Ebtuw>x4^?O? z)ZiY|cV*g#aHV}QUMvD6ndA)`--*x-^tC9Eh()Vu^ofPYs ze$YYRJFR_S`uF`C-gh6!|GxP_{8Pj&hcNwV@V>PEBy*bv=04`Q%RZYKZEChFBz3`? z_oS`1xD^B&+7M0@`j^S(+UCclzH*hJf8T7Zea>szI|Q)Ygk-!emK;O7=S|q-v~5eX zp_rUzHq zi)stKxqr58VDb%L!C;NSX78E=f7)G1X57{tNyy8T1&ZL`W?GTH8Dz5v_mf_%7ko0U zT6LjxXtqsBszTUhwNlK<{p)UIg{$>TuhUBURht#(Z7|;MrmK_+FXFk$y){weuZElK z2yeSFkhn(W(tXA#4^q>c-8WRArRlX<5U4DEWn5(%X)|jyF*7vQT5{4v$rk@W#?-T{*AwTt99h-+}MTf zEA@vjX#Ig}UNCPUnP1;&=1Jdsq)!rgpv`hi*Jsp(K{nghDR3_N1xrlV@hwFdHl2I! zWN`z1AZurc{q-bR+yhn(G9xH!*o=G_&Fqr3u+28z>a~NoCG>Gia5ZdTuJ{#F^NLh- z3=ip66ZXwmr8~ynP#1)8GgQkeRxCx8$%(p0f=`G1Xd;7+XW4DgyrpJyma&b17QVDo@l-Mx&v5{Jn;) z^^Ec4R_u*aSs>rh{1&H=fm)y<%6?U@lAhPs5!Ht51k zo22*Yx^`LPu8h`5e>bv`mOz?{+AQd&58FK5frfozIVkKEv4wEglS0f{R3V9^M%NLb zout1=W&>r4+B>R8FFgo5VSmn^`MEH3u;k>HqQSY^#Y6u&0yEKrgX*Vq&9!6JmM1Yr zecr`35qW?dHN&BvM1oydd|oqa2i@lfOw`8*G`Ex~V(%9db3mfS?q01(qe%y^O`7m@ zZ~y0-LzFz5l5BuUj^@qLeWxW`!vX*j=d)59Jgu+UpSiBaIPBJK@U;HJ1n74c9-<4N zAh&n87MN_@p45y;Ln->|`7VOU7(h`GR9K|dO+U2g00IpgK z^-ve5UnAwxoa#md+_M+cb_RQUvjqEf>Won-i2!4FkuXT=BRkj_M`mm<*QQ2nw|o|%4YU1mNy=X0FfE!&FX z<;ZM7jWm?XtuRCczrz@}$<|4r_Z}Nj47;wrpqV}ZS0D<8qrTcI@VcN8UDlEd$zh_k zI3Zy&#!l>A=`k7*^gwUjvc)k-W({B;6?w_>?15ah6DsGfFAN=Gn3aIbc}?HQ6pEco zL$+}x*e>UPakydWf2yK7=X(UHfLwV#B{e8q0h~~KUGJt41Q`m@v~Dt6 zy_XMkU!5l9`?*^EnLWgsv!gO9xl$M35F!=NDhv~l37J%h=<^{?#$mj}J^)rgyMp4w0C;^Xc zuyW04&Z$T*cp!{}9l?Mt5>_FuO;dKYNhozlSnHFy35VIznkhA?uoXF0lQD%7$$Ayk z-Ygl*GS^*+-0U!Cpiqa%az$Y+BH;Qd`t#=c_FqZjKF{urq0^Cs!yil+lV6k#d=@D= zS_SUKEvEClV9>nrJ)wU;rLx8u2K4p&Md?Fd1MlIKztKJ?Y4dwtKrQBk?TX`0N7Po) zfJUg|W}y-ZnIhKq4eXR-Dbr@rR_zg*dOk*GO%JR@5NcbB_OLy^DICAJ-2VuE5&hrH z6l+1zP-`sns{AhSy>v5Dcd1D*5?xXEY!a}@Jq=6{eJav$7bH$gf}Qk z#=eu%K)2+%bj~0j!{qn*I)qi1jofrE*8#AJ952n$5!oLHlB9$jYgv}GeTy}tdkc3e zwR`pc3cfw-yPk1c5pn1p)jM7MK*%HI@ld~DNBqm8gr{7@5>*d)`33yn-%W(dtr#G} zk*-j~!rkA4QE$AJJRersa+NUTEFj*xITvYT_ zZ!6;)|B1|Z9XR)>uE^@^Bj$pEPc!Op?0by#IssyYN+1XdtI5HxINEhu6vwZ3lod&H zW~jFa4v*F7+jG%8O2VzuL-A$D2p&dw`~zwXCYsa(!=O$%QO z+%4GiQUufVfs< znRZTc*d;u@G8DA94n9l~8VU^$rcqqip|a6*=|iB*Ci;#iE4LX(I4bT7n-@3u7ghKj{X!^#n!D@LADmh|^aaz#GvjTjr zDP9AuKqv)u@fp01Oe+qfWRhqG!myi+P0KSiL5jvyM{;<2ctxg^z7dK*e6ojuCh$R0 zU-Z2oWbGz-(@{in8)2hY;JCN4*N*>;Xz)#aYq*IHejQRlm)b2)Euus!?Rb!X zSW}_WJEf?ezB27JJ?XDhH775qr>y54o3TF96h;pdA-lb;yS(>8_t{pO6E;K{9r9pN zXq6p(1b~R{^jEAP+wi%d4M1@=e!$02PfTHidF%ufuS+pxn~=Lk{gbx<(xnbTt-z#L z>6f+=aPXb+$$c%wLRd!S%8ZYno#ViwPVi}^j3}^p zQ!Z}CU%H%t{$g0D464u3C-BYcmSG|Kg)<#yas5myR$@FS&>y4PhlKb#Ez^+nnfcEHP)-{xai)fkNwz&-N7FG z+;(cqK$A{~{AvsJ#vEXikN`W0AD&j&hlde+SO|ha*>yzJMuFOCaMyLP-#S>htmG$i zKRE$0I10JnEA(~<&i!19d4wB)2+D6GC9mn&ccxO(sp9}$JD+kJ2bs=fJ;ytdq!n51 z$#S~MQSXN~cPUZcQ14H@Lede)M=U)HuB|yB#AVV}D^sp)9VNT&y#_SKZYWBx%fDU3 z+Y^24`sLGVxZG`S^p@(y=zxM&lrS2a2PR~tI-UB;+fA;XdIfY+Gz#1_s+u}e4TaR2 zI4!0bl?oV#EJIKjSZ1to%WK8+ud9zK>9ede^Vl^D4LJ+t=imDwCv3Y@o7@z9VKefGL@%T-{Zs)0MW{uj(Nq?^ed!Kss=A{t;(A#(0ZH4|{l_Vn1bhAFcn^?-?x6LYhGfhmWi1Dz zggo_Jjcp8+kV|a6?RML!?;?AAAp{Ln*3_q07hSEQmQX7Hw%I{?U*0riUzBcmhI#zI zZ6{kr@`+>l{t=AKy%obCMW5sx<#=V)6j9L372o3fMrG|Mvwot-2K;ia2}p-#M#e#< z@s1~#^Ll)Zd?yG2ETG@rcs+&lsV@YeJg<__d}a_|@Z=Z4g;}!SQj5>=fmH5~7-T z`eN_dC1(V?v}6!}?!C|m8$B{Oikv=vPQ-_$^AIhTN&`Et@<1T2x5bfkfc;>Y81|8dJP^wa>M7ho{BCS zn6bjhQ;u!R1+uquTkaU+_(Z}IBw8br(-wPPz>T$%m+04;l{(~?vD4p7uDAaddIu10q6rc9A(lN+ z6)h4p1P#H5X+3Sxz9X$(PeDRs&)tqc_k9+5R(52bH1chBc77Z2{Vn(sIxD9Sq@!wG zFov7?79CBIdfy^dvmK!`EZsTgQ3p(0i@&S0B`sBv2K*`;lq+-Md?+azV72g zE}MWMBZw(q9=0e~DCWTZVR%XmczzPl2OIyM>?2Qt5R;gmVnp_~(c|Wh!xf5(;}e$S zikPQ~Wl=*C;c*A44Dg4V1Sx{)x?El>IrnuP!=k==@qNly8b#xH$P_e+I~HyTSgT9q5Y;~Ch$ z(WfrI3jsgGLLS_2ukYE}X*fp_aSyYktn)6vF`KwAFi$~R14b*i zbH8(0GWDKxE25&w>t^4r|K?@pMxyTG5e#K7KkpWIL4oZkxobCV<(6B@wxOBXz=ZwX zj)oQjpbbevXZVN){*wjN{xwfFEv-7TVjnxqVEYW58ub@zAs^VA^H;tZcYadg+nCRf zF#tEo5Ruqk5HpltLp=I5H&4k}vN4W{^CJ;!EIt_SyODG9nHPN3b~R9~GxdDF)LPlM4}fn>Mk&!2hMO$v*UQ&u#H7(TIEXgJs$J+f*1^r&Js8B@D0 z@Q!r9G=L^MWIjItdX%J2qlkn{hF<=q@kzDkuW`?lVeAg&`ZG1n9YYZcP3UX0W*E^W z6-AT7m1JsPn@K_4Ce0DIX9u?t9j>r*A70)$u$mh**D(+A=oR{(y=WE%VFXQhIgU$0 z0~<<(O8C~wlVvSzslh(!*3UWzt|D63?<(y`pL-#zxVJ`i+|k|ME*G&up;}4*yW1RK zjJQ53tEXfWl&eSMzinJV@dx^p0Sj8vLS=w{N-s?E2`FJ(@b1MYKBGc6S;U)7PtNqh zJ{FOk*T_6EU|o$oueAnC6rKz=Y+b<%6O{z1%I&E>#+!D1c?IgD{>9&z`houC5{k!d zo)d_Up<>296?yn2qsIiJkOM|1H}M{d4hcAZXe`tA4?$#{Y!{*b{SQc;!cPoPI25{k z{5$u{3)O3A(0??S%gZiWc!jF%6H_|^E5=ADX1G@Z`q0E*WX!SF&X2M#kXe5CeYC{- zb@TT_`T-6o8$u$#CLqlTukwTDLO;H2H-B||d*|$jm$VT06ZNy#5M}T~;z{y@lqT!d!lut4 z8HH=FcYdMH%Du_HC3T}TO?OMThb~id`pxwwgy%6*&rO@T1~24q_&_M)0sr#v0&j=a zibph%?1ZiJq;1qIy%HAUX|G&&76e0Ec>4r?k-yKFgZt&*4}E-+Kzbt9FONyVYfRwt zQcq4^O1DX37UYwqmO$?hljQ-bH@yrlMkxFkLj<55Jif*RRpj+_X0n8gePoQpmWYy~ z`rcq;*B&RH!+5}Pe<#o%z+V&h^FZCbiTuP>!a>CV7ex>T54tw$Jkp80v<$sg9atS< zN|!aGQ+?Z<_*2y&3MnYF1;a&)Fux-K&2CQ=4D?w5J#b{bH6AK8{>A5_zj1v37v|hR zT-AWN+Y)V-RBuvxg?X&$cf#nx$reqz%g!GAP8H z(6JUb4+&%qCZLQSA%Do*)c+-J(px?HHtcBBgKxgR)WOMr)WT{l?aQIL$+IBa^n|t0%h?0k z>*(2n4!eZYIkC--?w4K83}=6ScJpCf@XcpCaYxntDr7N+F*EF-z(Rt$thtSm?WsbT zO#|6_iY?3It8iqmtWvWj{Bhg2196OQl)1END+<~YN)QNMiwBLVSpGq0i`*l%3ZWb) z@lz@!{Gu(2R0_Hr&>X;zQvp?2 zUJ_maor2+V4Yr$_sn9I6a)Ck?AZ#8zk0hK4T)=XX;x*Eq5d5vCWsw89pIgy%TlKb5 z1$c&inN=iyrzvLd!VIx68(E-Ihx>4WS12Ai)}`1@E{_r@<&bsc)OyD{DjHs0d#ijV z?z&TZ3d4V>y)j%vP%V=OckZ%%^+HLIULD??d((>tQ z$X+T+^3<_e*P0`o_xEt}#+z)xh?V_;TEA!4{%8C+_&{`36oZ{un|I{vWZk_L@};ma zq~+=1ggi}PK|;AE+w@h#W|O$-g##Pctwz;8iMr2hZi#4;%U+0T&L7_#2`;C%2^mic zfwQ&iXi7zg_8M^E)?N*x{{e%yC+gyUxEfAO6AV{5R)Xmkt(c+z&@YitI1pVWv3{2> zs}P@dkS7`zsAq?&LH?d-v!2H|3eG(H94mIzT%Q9&&wrlyA$zFTLPYLPA9s%?L=q68 z#I$0}7m|JpLaBfkclCS^QSK=$&6n@A;X+5ViNef5X{%ek6*bg_w_h7l&(2zxMN{ZA zqFd}4Vl$5wgFU0Wa(=qT(ztI>qtIL^wnDtfN%h20=TVjN+HmQD_GbRES?`1xyFIbY zG*M@tCbN8{N@v$k&8I?+?{YO&ysNwe;eHbvd5*XE;sXs){!3;EzkVa@=wWMkf0r}* z(wM{^`2$yAOW(KdfpYga|9x-`ed4yn1Znz1OUUmp55sYrGDa{PoY7>CQ9?4w=mS5j zhjC9cp|$VfaI_R9r#O)@R^Z1xl3Ra`FyVDYR?Q1zI^{Y(^ekW`#)RAh zTV##^s2Tr8^Mhwt#SP?xvd0$Dav5eQ6c%zQK=YK?&1((zkFIzGZWdNu(ct@9rciDp@8A)d+n~o zylKgBTCv$@_k*vQW1rw`xUm`t`xr1~YF)-nl%32FWKZo7xi<>~yA^ROEAU^V0_x0t zA0MsC)gM$*-6e98Pk#RUjUaY+$~KEKHf>HBD|IS0k=e~|6z~?J z>}E_Ge4lW!vvmhwooAD=k>?~y6c=oq?c+6c2Ra8YXrCEa!P*(b=EE#Ii$-y2w#GL0 z3&{@p?JuX%cYWV2C11X%tnxIw6-N4DTOL1Nd`Y57R!vk~7r?g5eFT&4T2G4td7hj6 z-D&rVSgS>|#TRt`1gj7pE%cZIs7`o!nxUG`D^EQ|FGDnfVFwHj4 z^K%XnBSJcF#st--^b9--mMVC5cLvF^&b9Oc4@#l70cwxx?I^8tYn_Wc4z$K<^cCyd zwumX)7KN1-)XoC5Lfr1#?T=VTvBRi1_r%rl1On34yiD*qvnJjP{NNb+u2@g)*U8g* z@Sp@xkKlyH_=Os~tv1(7cYofos$0%?cia>$EL|jm-pfsQ(}gMZn==qicf`3 z$Tb>6ydtYtmhXLt3gJ5}7C5Gu#*}cT@f^v}{4{ZA=QT&)8)#Z0tn_Q5{Eze}&$D6> zq@^bssR73F#myG>aUvHEspJ1?#UeJE0#maeE2;g|7XYMC;h-&Gd#SsWSUCIboL9$# zOAFwx za?Y_I#;l4+q{1`F=SA}7Bk-+;TH5zNGcpr)6x!x~tT7rQ}n>amD zTp5)uqve@)x3{_B^@{kZGG=y2NvPcIl<=j}Ta)d&K%QONYt)Okr(eG8h0SW`S=PM_ ztb`}O>p?XSxF?kmLr*IGVpL0B%iC|Hf7w%p&3<&X>Ma5Lk1D=!IPloqOh;QR5|1b7 z2qNnOIF?dC%vR``hKk2ud#3U7Tv6L|Pq$d)*N_(?1SFu}gLE|w`l;I;`|t2YH5`8S z;a_>k^@Be|%+~=9pBH3)e+RDvvGm@{sWX>(c#PD#A9-R4OY<>hCtGW@kUx;A-Ink1zmQF34^w zQa4_sGp@NSTq7V)k{?=RBv!LGvyRHa6z52BPmLZ>Oev0-#f^9p?{7nb7`Q9r5VfQm zn`B`%$H7g;>6hd|I~U=E83<~(hqMZOSDHOOH+VKo(X?iA8?UGzo^A9_3b_mZ&SyPX z7ST|*3+6-rz7dh7Ll!wkA^t{tjyKWk6%VDv*2->M3aJkN6ajozzIML-^`;2)5ytZ! zdR<=`xCn?5V=H0=f$AJ=Z^_#fmL93}POJ{3uRbDP8O3iO{@mVutu6$_4v4UVq6N^U zR@)$5eZ9-+5p8YEiZviGp@4n3Ta1n#wfqTG9d3`_&4wnxa%nJb9?x|f{LV=bb)ob0 zo36rTY``0E@$b>SJWl>uvXIyb@w2S12F93w?YXmRf_QR$`x~J6YM~Au68;8!A<3yR zl_y@xsVG-h_8M2Xp;zBF+q683kKjn$&34PXes=AA$~CJdf5{dNXy1D(E1#e@e@5p| zKgqTG`7X~u1~)8&;;|1DGl~z4+=yc47CpkDtKK&*^(?l_JSy?EMPs_`QE2wsi3V*nTdxZe z^y>-$Q@W?&H?)qh1ZFhISeLIBut+#`a9;W68`8-=qU2qmHXZ8t1rQ0>joA>R$C!u= z+reVt%eA+i$t>8G+|OSe3!vDk<2?3m_7APf>{Xttes1MiL~HWuVU6 zz~=W(L`{0VAzQ(m-e?F6U$1ss#gp-8ZtxMcJrFxwE<=sbxuo9*Uc!reod0#E=TD7d zRL!%P{a?p@v5}@}1!|oi~Q*=_~ZuaXes|7 zna^PI56<|WWhjVjKHN1mt%?_l1qfia#VWV>!ECSv421YY`(GI+J_)>M!gs=Zj$^gy z1VfS<3QzMEHAV?#-D_QK-BcPujWY>aQDD zm;9Kc25R1@yjqVn{*8#$2`f+V5($+JtksnhrYafQ2C^m&c-p%N-}t}_W_W&mZ>~H# zIXVKF7#Vb%T&^zzx|%;;e82oB{Bc)v$M!ufB*RrZP{sN7Bt^vcjeDXb*! zf{xpicZfxx<2`PuH&3UzQO~d9LEO~{f3biZwD@X82Y{W~;IsGh3pD;O=Q-x=A-)6K zrSpg!_88GIhb8bfdHC-j*=1J#JT$XdfM&|Ko$)*FKXtwc@lVoHb4epp@*a7qE^0{zRD7zY=5c9ppI2C3 zeoa#&WRW&NDQwC4DY#jqIjl5k)9TU_#5(M_d=ZtsZ~*vj7;^c4YYWS@X4;SK9C>!z zo)#%03Cpe5FG~Bzt=Je@Ko_Og6Ye|X?7st)OY9JKp`wat3;^*;got|J9ApVAFEf@=LKHS@(U<;Bo_N-RtVdbecfaVIJ=!qqMvb zzs@24zF zVoSoP=K03E-6w_dA9;4`4I{|ME?;{Tqrv)_gYyF;KV)yJPP0Z%hy>hkCBd?32&1Nj zyR=kU8|AjP>08>y7Lbg(MCL(kzK&qcQ&*_iqnBcjP^t!moIvK)_pV}XFcspPa$}rW z6#YOawqV&b-sxAsv4P75bl}Zk?UiN$i(!Yu zy*`JBdAL=vjdr`XW;MBjjw_p61`Riae7k}3a%Whg)C0ViIIM4afU|E5k{IBPE|kjxT}z12y8 z*~VZ+ft zcpXYd3cbn=(;E+q%}#wBu}75r@{rr1_h`$=y8fv_#l$TK&+5w-iwiGiQ>bbRKT7^Y6^Jvwn9RYw9i%C;@MNW@++X4A5)1el?JUb2&~m0$CZkbVpLj zFTsPbA4gW3Wv+j^hksjTTBGSKtX1xR9l6|H4EgpKY^<%=#9prP;g~45E)I<7!I>r( zg~dA?BuMYBNyQn5V)p(zW9%4pk{M-xh%g#U3zhdShL)E@0)vx5E3w%G-3R(VMVFeb zKK){U->kTu_N}JOF4DcGl8DU-%l$` zR)_752|?`6r7+Juv^4dvdNo_DAtGiSk~--_2k;k$zQQHNDR zIj5Q+FN2SY0nD69Y{W1?7Z3G?aY|>RT*|mzo3u2&GpaJ5d?6BsB$lTRqU*;R2YV3 z8=S+egY90i`uUzH2ty;G!pIlwF%c-9ibn!L!nJB5)+}0b$6~E9k~+dH)3k*P3a5}^ zZz;Aj62kty31x~7g{AV1gA|HR;Qd4IW)sFs98NJ|$@T(;#u`x0 z3{cZv+bZNr=I<1D>?-wI78xT5udKC-X6<8gk@b&<4jY_2uflavh|e(pU(nE;-&ym1 z&Rzn1+|JGH=xVR>FRbR`rfvCMqpUwpUpnWD4Niq$sQvot?isPL-w=eOFx|sD?d@VV zygA-F()8;MjsHR(FT{O%c8|My`&6duo3M>(ruwFe8eKnG`Gz zDcWQ_};Udmx6Y_mMm4BdT25ZYlX7Sw^>3e zAeAntNL=Vf6A5KDldU%$neA%^^}seb=T7L}zfqf`@`IL^4jz;Pkf6fI(#Xd&8fi+Dj%xIIETrU|CD?|7b;2#8#?g(!>Dmk>7j;a)oUKQx#f z9F^u}OBe-m1aG&6#>gC6XXn}@nG{Ei9dmo!k(ftYav`;kdO#vxjk>_qnb`6kxtshr zPvoFA1cfu2;*0F-vR$m%I!0dYiMH)@SOkqaFF6@=zse+HpK3K|+4{mERzJy5g(hBm zDG^9{!YNd^pENe5^6>M^e1j805|i*8Zkt0# zsOV)PLT|A2;S`g6%-YA`;$+L&lRx}YpX=;lP7dmPx#{a_3>2MNXA8l}P1=uETX@P%jC^`y*Zd-vHIL_gyu!9FA=3S)QqjF1TA8& ztiJ3|rLs`KC8w$*xAK*@g6z?+YP$G2a|T>&iYGjxKndie&DDc+vlB3RZ{4;^shEo` zTM&1-j9yKc?!4u3__=or=RXSTHG$-%`a;o!i*-sKPQpb_W=^K8XX7P?rl02#hGTK& zu$I8Iw^I16jj`U_y!D<}ggpHNU@31iK4Q@Ihn;UgqVn8c^EH0Rj6oPbUjmIg@mrht zL=+WhKCbhopjLHNpyuhQCQNu9ip7c{j$1@?&NR;pC=b-~bD)OpQVb$Vt@bdBBWV?5 zBjdlEtuxBQX*S){s7~&CwjIsRSRXgqS%+?rnse`|FWhnMwIOsP5f5Yh(!(JwCse?ay@5KCCBrg6FYdVti#>RXGssd6%LN5M~5^gYk|7-_z z;ND_Ff|xyeKkWr}F_9vzK{xwX)*{olk7{;dii6WyrS5f!9eBqrp)G<;cc@Ui@$&NW z9YIs{TM>&52re&ectY6*(-D~s?ayh~NsP^~*-HYvGW5-B9ideBk4SWde%o*BHw*cV zQpB#HH7Z9Uk!l%Lo#ZJDm5CQ1u6n(Xc{%^&(K}*sO~GZ`a!@(r^3h&dAzfRv7-lBU zXQn5|vD>6aA?6cuj%;He?NAwviqevqwbPkQp<>596?10Tl3kmE7iSJ)5ruGsaXClj z4-nQA6La<74~eTQEjn*U3$K6g*Euv3E>_O~1{aOBEQKoS6J+m2smv%MD7L3O3zE&SCHg+$>~16q^2XOmB-uKHB#uG=AqrP(T>O;VH?=-Df! z+1j>%G+|Olm;e1TpT~6Hk?D6?qqTW)iZuStQP|kxZJ0nlw?CP|hk6Vk_&17_N0y-w}p-jYY~>42L-w z#`S8V#+&m!pFzY;tQr}iK6q|C4RqdB?R0YGJ1_xyhy3u(iG^~66`ylP2;92QAjmZ_nXB7zg)+`??=B7CZO6@Lwm(J0$0r z$f$yPuf=ihf0-?%0v0SdxMiKK%_OXYbI<9E{;N2q&q4 z>dh;dhsn50Z#;EW8l;+xJ}`~3(MJlQ)r1duJ85u%)k@iA=!G&hYTS7VnHrBb*1~6U z^6~Z?HMXpprg2Bt|7unt?pq58Mz|XisGDdbkzj+5WQ*)eZQcl-!^?cCDu9t?K7D(J zOJKuD6S{aAgDt1`wvwl+Qh;;q+@A#Qfom1|Bc3vC6i9-N&C@+0ber8C(3&De zyZ*uMh7$Tl+KS3(c5a@Rah@!M%A2*GF8+LX_HSXa8xMNVM$OPhN2>@{_dLD^pGcn2 zPf+@=86SFK)CyN6tXw6;979>&x7wy5{l&!sZOkrE&SZ>R{R%y{JIdSvWSG;jTjK^v zL8@JXizMpmtW?$oah2X&(w1MKIBaD_4!bS4!g9;=nqgHEJnM?}dhdC)6hCi<6=2R1 zh8)|jRw<)QMGKsl7bQ#szlsU0&l8u z$TLp(X`lLrSPCWRog-xAf(d8l>T8nU&4 z+iLl2Dl*``%t{QPCPM6TSe?v*kE;v2MJ1PU1t;#g58GK&Px>n2UEo-O-rYGw?=mBY z<>pmkKU$$SLQz**rC_{#FKo7}*=QJoz}T_3O{dNKr>^F>7y=p;9IX}F3Q+>kkePA# zn{fvzu%r^yIvhNEv!?K6VPq)SkP#{{jk`VXWgDk3F)}@|6^Q&frT->yilw9%Xruk6 zGzlG)y29ZHRSElf`}{9dr2Dlf*uwQ*<6&O=fKu zPESaI03n2)gcf?}pnwS-1P#54p(9N|RA7`zLg*0z=^_)V5kUh20^%5|G&LY9)(Hp< zIu@+MD6f2cKjAs+S@$|;-+NyhF(?z%*s4Jp-$|DNZ~5w7b0LbN!>}q?-YVb`R549y zs(bEX_L9z5dcFXx;=r^uETA89ji*hO9>aR2!0a;%MlG~r=f~~#z>ox75(B6%*aakS z0u{yLRKxN8?^H3S3A6rBJzO{4-(uyL9A_vBBgqPOBJj}(u?{*y7a#e(etSMUy)vWUp1L^ zT(s@ub6H?G^^FFX-tlekky5#yE~Q;5-7uwI%W9HLJZ|v1PgXD9j4ZYZZdwmmT@$>) z+KkqHRsPz?ZGiX@g8|yk<5te&P2U$=VEWB9t>T4c`(V7fe6R93wwB;ju3x zxb32cw^G8c+eBYCg!sgbTGHtr0jRR{5y7*!n&`Nq!;VylgW9f(_L;*Q#yp!i zPehfY5ju}Eb2un`_At{^g~hs2KWa7IdP$g%o)-P%3dSyR3?#2l?pa9H3{l2JuoZD& z95?Q7iH*0JB3NO((gkhoG}#QYazP|H})|4oHLC zUDQ_4_7C_mEY<-O=<>!+X<<=^)JV><_sbO!{4v>6ARM|qceAhO6Qte{=1a7EUR)2QHpr=T-1!z zjL@}nwC>})hIXr>i;o2qFL1g)GoM)-aaJ;~kmqMQ)A3QgyNi!F9vu~j462Lk%cb5x zf*=++N)}tnqWagm2fk`d?*|P}RD3IR`4!u7DH*8G*?8mJnYd4-({<7>?WiLH>Vtqd z8csAPdwxhG50A358a0sL0QQ%}zFN9&%ZEtLtvy_3yH*t(;)ovPSW}k+muowJK``Y( z&B*8f41WkC8Z;*^Q~h7KO@5rRR~yM%LK2+zrRD7tfF10J1{ap}Z}#3E@y~r#I+qU1 z)!|qK)3ujN{DoN5gn^>_{Nb7MNe1sIqyUE<4>)(-xBmKe$GlJbanUQ{u-y^n;76^@ zLV5baJzjM-Z~X8;Vd(9`;<0*s^JZvDSB`Vh<1@QOOJ4Vmrf945xC5r*_a!UYZ5QgY zs^(S`rq%H)Lq*Y#+;wrsR!l2-nU1>4k?16_3R;R#hu8O6b%)fh?-Y5)G)S@<^e*}8 zTc)hpt!g$k)%9^bMHBVB`}GX6N8gO0vIyb~*4}18(JEL~ny(VzMAf`ic?9 z3VC~-M6@0|7jkXt4U5l#SoU@Di(7jZJ_~S$LjOP4CNG754xOMKaR=6}vm=7PL?B@E zZF6p)zy}&DK&bJlf+MDgSW~JMjwQg6B@F19qS^SH#ay|rUYea5lsu0Y~IsHb(hw6M>k zRLTQhdvKmi_F}1Q_5qXg0z#@4!}An(oQ#-IZB;;XV2{r6JgKU&ft_PbhW!X0yKP$< zthGdvadz~sxVdu3J$aTFwi^`io9D3`w6S9T58#|DD>3A6c^_3>*)jPO8;(SOoaxA4 ziU8fMWED3t$S9bF7EIdB(r#}4p%e_%Hq5wjDtI0+WeK0s>KZd08Qt6B)HSu4wMg1Q;cnmG!DaGmfVTn1E}Uu5Q_|6ytg#W!)MiT6jd(=WWXfNh@#F%v z^%cDYaYTzeuZtpRI189Xw{Xr=6mb-nI7Nrg=<(QnwhsvjfDhFi-;1NqYaIWW>Nd4~ zaHgMqyc33DxO|!}8}#sV?<)HP2UIT&EL0UIQTmO19c)767Z0 z@7^jx9(?m1b#Z2c#g2N(dFW-S3E3w-sd*s4sqEEBRCg)p{gmm53?pWhgJdIx0C^}ohD<7TSk2fp65)*6oM!_D|GZAD}F9ahjEqyK#qwO_Y-ptLuS z9Xa1!rzqwp!goaw9xou$becK}VUAus+$9paHJ=ZU@^<* zH)ksyH1#@4cQcqg8A?f4(YdO1BK=uN z;P1Nr&&Yu8b!Py%femAd){lt{dTPPK(QI25jQ?{b%R;+kyoT?acDA@-X}w`&|D{7R zPg1+J$Mi129yZiT(VP?Lb0Ua!Rf~5K{vN75wCZd4)!4F18CgAA3|AiHT!dBEUnktq zW?b=w&u=f+Xp7xLxz5>OzVeU_Zjv_sZs@C(Ur1KpYiLP#b!m0gcf`$B zAKYC!uXsTCYg&P<__+Q6q3_s<4sQgUt&{cT_4|X*2l^8;edu}lNQEO)JgGi!SH2D# z6niW#&IGFzH-i0gLqyQY%5Ger(VVNle0r|zc20G@vcZEcfsd25@{!GjJ;J*2+2p6U zLT))kj@P(E4s}#hQj=6x(Ak*m9Mz$r%hThYqi-Bt9hA&$TGtY-jPbXR^8hdME24fqpQO1$ zAXqy1^nb4Bc5&fCdSr>7D}lmYp*uw`QC$gP7HA(rH1EGDd~}v5rphBQB(6HZZee%w z0{*Xhh_gCl!snctpTDL`q$dkhLTui?%>5ltiDYZnI`>|HHgS6|xWAHUq5T`vTA&zQ zMY9sdg4vpnXDCG>jKXZl9GvQOGt>%SET3mH&IHdPAhYsb)K6%?JnEilif67yl%FFp z=Ad=?C<)q55HOmD8_>UsJ@1@d z!^Y-HI?Jv6nJf9GUL`uP1B*&~kc|IrbL!lR_+t^uTd{1VP!M>Rj57a)S)-246VVZR zfx(gTAzDdUuJabZt>3s$llQhP4bZ&Tcdslv9_RYG`2B^KSwA5S&imfLl#|J$_bOF$ z6V}jYt_Yf8HeV&2$jz(|A+{kL;uOR@L9adGLb(D$h>;_y5`yN^9vvueAt2pPnp;C5BD)R697oF6%UNJCUr?{X! z_p5C||6{wE*2!qQH((cOQ`3!)YPPBw)6hX#GZjju;mS0$e63OC!O#k9Hg&H%gXf5( zx91~p|2fHG=CKJyhyu#eyeSGZ&L&xi%t08cm?p-jKG~JXpqCTK+YX;%GUu8yKI-}C z&c7@EVG2#8FM38`*+-#u{0R&oqrF|qE2%Vq|C)wdxfZy-s(KxhyrWFNf z$x;h`f=|~>2sFnQiJD=QNh?60kfv&iqOtNdc0^D)qvUgh7=!5{ zSLEKtzS~~43C@WD%4GTXnpIa#QqS;buDyBON7e~w1$(kG z=Z3cDlwvxzgHLagPS#;o=_~o4Ob#?%E-flu8FOfw}Y`-m# zf02_a{6j<&3|(o&V-wpEybuKe?ZTQqs&5-g@+4f-2v5@Ssq^r*@s$b<7op@6iWJp9 zFCJ85X|vLmnV?Swp0+HGty;-p_c~bxDj@8x=cv!nSOu;_48FcPiPw=hUdyFl3*Va3 zd=`WXKeKX`+0MS#!YIDcbW)pbnumpdzkc%v9T}b#b>!iR{z;|OsEDQIV>>@w78v7c z-Fk*N@pAE06xg~_gi-aelF4Qa$YzOZk%_bd-#y4fZsTf<1L~Pw7hgGo^8Qa&ze$Kj z%5Hxhc`B*9qUmlHM_4tX^<;R;!z}Bo>S{SW?O|Yej@{;9U9d50c?uC|9E4H$A$#(_ zft#OG5%O(2Dus*~-aF&g5>@#Q^R6CSxlir{mS0|h72NySuwtfT!@m_(TKD9_&0{O3 zCzJ{6EXA&eP1hfaK7zt2&)=Ij5eQ8jsGQ(rB45h8NC!8 zfMGjP`%mBJpv#gA*s?mxNgL*hasyKS2RB^uFVrd6J2g1B!h~TE?_-|hB~0aq%5Tdt zDQVF~8C0vi@9K*FJJ{{kat~ameC)oKlhWaLh9ws&CXsD?_&eO+3M5VLFbA2_`c^c0zRG2i!!=i5;CXVYrfSN3>1TnSL7^+ zqNY(P@`r3Pt0`9(lxhqY7yHZ}yF_IkO4H3k=3iRV1DP57AUqF(ybkWpPn^tIRYI;P zQIf&UcXb^l&|GbkLvwxG?Xbg1TKbU`?&OS_;rPa7M8_dWV}V?ekW?oD;I7;UxW4f+ zoT+;ErV$;|DDp2t+5{8-uIxfMvar0wf5u#J1IY%#ENF~jxbj=07pq2pB9A6-pb515 zzqEs5005Sx2)W@~HLcRw{I3=J8pu06kaYd)Z0QbCh<`8c-P{_UlmC;XLcyqTBCRDw zJ!zT*4tsGUvP?7^=g)_)MlA*MBd@vQzR5}{pd*b z*NJj&wx3zs2sV0HlJqXxVVI~`xw6bbJ!`t|c*x_K;PA8E7X{T#9}g_SkX93py%@K| zW2uZ7w^LGXgEY*PQ61u5THI)-ryJ%6>z7Xa@vn#7xy|)9x_6-u|-g<%bMbd^)AyQ$HBch`&3se5nGRvDkkG$l<<}Fh;ELrlU#Gr~*@9#H7eI zfh@2Z1kHQX7J*a|ng`F4zUGZsqNT%MXTpCAC9Y(+fi(ND zX@4`uh-h4M79|xGN*wRQO7Ai`6MJL5j13&jPOa6QB;gR z%Bu@krK5ZXK+4e8yD05T5l|4#6dFHQK>W}md0V7E=MXjZ=OOf5tZ^L&=uE`c2=D`* zH${Q|3SM%Y7>(VI}=!H^Bfdcw(0@6S& z;dRV#vnsmzi`4_U{U)+ndosOt*M}m!)x*f}<7SZKkw2)2HVKL{3c2qIe8JkUQ{v_= z0q_deqgbdNq4{;4fxzQQ8DAWB?kfI-Lnb0sS#v)qT=i$&Lu)$Ey!3vw9j9gXk91xg z79Drq?r^l2!0+}|zzH3eX0NIFGs=T24cETBd}ZX;J9?sE;>12(_bsEp)+#(aMStb2 z$c@odn&{)@a0c2KltF}o457-Ih}2#m!`{O+@u**r*A%Q5OdE=5hWvi_sEOBU>?H7m zA5oQcNfaZiK7XeQ^v>59@8_=#%xg}d2j@oqJ}olt$cLR0N?z4Gy#|b-M27ii&#{q$_Oz6a9kee8c>}l#>(*2uVbBBu?NAa zyZfWfhp+Os*^A_)FNjBr7AAx?p!KkaDuMrSxg6HPM+`S&3# zQHwFZ+-nTpLb6Lp-M_<^*_>7G0xF(JH%qB zRCbk6a)P#T*v1KxX2T76bN%HY5dn*r#Hq6AO6i7QW0e->L4Kjh(P#6OPJMsV(2IcF_n+3bDZt-#cmJnMKeYGIm5k=xeH0tM z)xqM?#C~LfWc6MVpzEtH6a-8TSfxBcb{o5N3`8EOnI;6XGXFvLO=F_8u1C@GQtwbK z2dAmp*P{_Ac^kO(#!AmlP0y)uhW3SCyZ85mHBisdbUjPn{u|~$)8u*0%0(*xW0urB zQ5nGUyC{aVtbnQtwD?p1{-6$=x@Z5Hi0CBUJarY-!aw^(6P_G!s`=gl*hC6HrFz*_ zX$4jtF+}iWIrdfzR~e}E^gqAW|BDf0l|Qp(<7)o{aBYX>JbU5SE+Ymw(Epol{BO4` z0d9pJo2ZjHYz$WVw*mm8VUK8AMRWpW3!OEg8D0%cS=BOz@?_J8(|nq-V~X1)tt~Gk z{dED$dxDE8ar@&Q6O|!>()lRkgmtD|yF2eOviLhbi*k*Z7dH8R777_4DhovH zTTEo~)Wa?Q**t+?uLyvg0=%Vmp8+6Fou6+^h@@D`#3hMa`VZ~SM&V^rF%Q!wrvEnx zP}!i%orx^lf1P3bfU!=2D?f_MVInso?)EsvO7BV?XU5EqjoL?o@ma%Xw8F-X)Q1!rzTG?Vi?mC1YgnCA|KcMp(mTy9 z%rX{AEfY`g{Oc95hd=v#hu{J<N4&0;e&J(dN2XTFe7k*%!LBdDro%;PD?cG$h!>|`?f8!BJvI5MT_=Hr z>fH8yj6=LlyK;jiC+U0;aWLT67ng~2rMFer&0MK&t%t_ldBI@y?PhuWg)Z4ga+hAn zy<%Zp56AoimC2DP7LX9R4{)tTkuh`M21PQ7Y$=C^$Jk@K(KUoWiHIM>CtalMZu(Z7 zf#Oe5fD}nHx8g#AuS$jl&${7QP?}=5|2RwG84e;JI281k_lX3(5OeNv;ir8syMOKD zqEz#qEPo^iB*s1GqPnfyuDjc-&ncce@8#aDjeTDOnQR@ll7nc8 z!J=D{nidDmc0gd#C7E|#KeQrlUHpCbxZ7{#x+02KBZJ-Kekl995^Gf3aK~*u7bu-- zHb$DJq%HGBl?xTccegXAER}`J}Wtx^h$a?3EYB&j3j&aY)39GQ)D- z!}MF@sCk~NjQh#x*hBe2rBc_}DvPJ~@z_WB`|Ja+cZT;To#}zceN!rv;G#$>m7l7w zfBdh(JjK@|g=>&fS*@Jse0*2y<#YABzYaYo489P8eQt4%$^8Hz$&ug$MEaIyeycq^B%JL(s8TJm;{!?j+?Wa+@C?Id0+kqFCdHV0lLK{ zR4(qN0^jvEKi8m4TL}SHS0W)P%WfQ{(8V|8P50mN((fD-d>UY@UU@ORe}wP;k!}ig z-s!$4nz*oC5FvmSiQ;;{7u{q^9i2r1qAnJvgq(rQW{7CrCb=U4uY05X)I;{-`JemV zE%yY?1^ay2tWdVh)Y|>0Sw&Ay&7e~6KyTE^cSpRkri3m?SW1NO(r8;XD$YG#Xm&4S z$0o1P6{GK;xI}R@oYu+O()aE2Opj{vrg!>$HZ4%P;oAqQR`x4NmIvqTxc5JHOYAo} z`opVhQ}1w-!ZlA>@D|_FYF5YBkLJQpqg&|NA$|KdQ^YBDxo0)|%k5a^{rcJi##Qt&`jXJL( zcNCS}SAJCb?XE4WME;{K6{2suCV6?<3AYN-kK~TRV82&grWbhoe|A(McZ$JEKF&ap zY8sAS1W(J_nls_{)g45Tb|j;CZ&lnhKAP9ldOj}s23ye;w7`W$GsTw>eaXNH&4}bQ zx?Y@^Ja+z0kx0ANC9U2O$rX*#aQb3aq1kNXC<|`=ETQ${fhEG<=gv_cx;LB?0LBQ< zC97`ei6!5I=*&knu3yP_k=Oa>=ox1Vp_c>-SRi;v8KXT`&f$C`V`W|E48P18nB-e5 zdURG!BwiJaeVyH}WTc6bK{@*#vUT(rPom~k`IK3>Rz*sg91x3evhjy}z@#UTp0R+y z-8Yi~F0$$CNNq0y7Gc!4T^=|Xlkz%0>}(fyPv$&hN;mR#H$d54+zLRdvSka7D;`-R z-`88-I}*)O7acFFGAXaEs4<*;cpPv)x-nsfPJcEwhE;4dooef7P_9e%Qq{qCJ9c)d z-dim;oH`>5wr*+0tIDV(?L1ANc{Kmxz+?NnjVHA;<)q-QugG{0JY20hc>r0?sgJ9)mdlbjD( zG;12!v&O#As=S&(a8w?xbF)6>C}LK%sw5%CWr9T@WvxV{cX|zO{&elFJsV`Ngo6uY zT_dJ7`hjE(uZkqiC#RAK|Q4LF9Le$y;uCh^fHpIsMsYOK~KJgRcVNWVOa2# zsJYSKK0oZ=wV{K(spqpBgzEnDZg!C>9C_a@4%TLmO&_ReH=*M*v*` zvf-?k<5L>3wEq?Kq3eMR#+QP6VP;9zOJsgvN0$Ff*rLLU?2WiNhvPz`$GJ1FnQ+NNb zm8-xw0x`czkgqWkbgpQ)MfufMoQwQ0CBm52jXHRwzcl=!BpQlIM99 znDr`J{Pd^!gxyc)|K3jCcWF~oJPSk_Fab`-PsC);et}!LSJIFr=jC6yY7DK5l9e~l za{+4*LG}9tX?Hff{5glp?S}3OXp`76qP35X_pnDaSOvG&$l4|e&fV{)IGq`xupX73 z(IWqmP>y=}sOa`k7X4gJeO>}Ugjx{MARtqiTCp3r4=|=eF=Z#jkWlus>>Ak!i|Kqs=o&j}O8(I2v!nLM_#m=m^f+0B`wh>yq6TC zq!WtxeJzT$)W=Fyl~o-}e0Y0W{^b_^F|21KA=kao;7{y=!p^utKcB1K87eUGI?31& zOj=SZ9-(%o;n#|G&4-D5U8NU*{mSC8Btbkf+zZ^uI5=M$s(a3D3Uh- zxwHaI(EmPJHj1`U8|UH%`L2g;qM!Vno8JGK$&<^#>DoR!{gsGdzM5^4J4=BSy&i3=Zc zmIF0jE?#!nLwX_wd=veuB5K~y*Zm<7c#gi^pUB-&vp?vEU)o2bo)>k4&dI?Cr~Gxd3@ci`%uz9<_ zKOdBR&ooPyaw#Bz7S+zP%(%cqbtI3E&8hj1^ZI?&yP*VX>giBw!Cwt#Ni#07H~;qA z_55TS{z~@9<5dsKS~<$+Mnts;_5GyR1BTQC0Oy;t zt~?xqS47O0TSfDM_AS6)T3AJ5#^qLRGBv#X0*K7FH2EB8bQy%ko^UD8DE0Gl*aj0t zfPALrKaZ^AYoaXDU><)1bXZvhWC|pfVKxxO6`>sf0EeyYqY|VQ?&LM<$wylke3JV@{E`jpOM#0i~)JcG9X$59BPPJWDWq=XfO>(`) zmih|-eFK2jiJdlE6$zh^|MSEA`N?Ip8TOPVHRFRmTcs+t3T;|Bdufl#$imRZf%QVf zaDYYN1q*@@@$9k1D#@x{OCwm*s<|z_QLF*4Bfj;qeBYsNBJ_j_fHRg>peT#g?^b|8 z+u*%^SuxZQ0J?nHgk@f-vzKnDV;hd4?}$*(Xow*ZinNU$VI0;VAQt$0`zb{1Yb`Bn zn5|GVBn{{#!~n!b^EA`}GKl{g;KIzf-*58wKzkD03DVB+0(%Ge3QRj((D0^oCT=vDli!<#XrRZ zKMV?93iAHiSO%nqS_DthWZLMacTT&C3*FYIF^`C{In55kfg$sSYQ)#U-7CR!2xua| zEO*V#F5122o6@IE%pNH3-y)K$`7R_G3;Q1h-YAkXBZ47HI#HS$0nwJq0FAGoJYwXn zXB2+F|0oq_s}Xepbnjc3VwA;5b%pB*(vwpM|6K%s_bt`Rt|ZZHMkFmGV$euASb4L> zBJ_xL39dA zE% zpG;t}dxsNyMvLi8Hu~J_|K==~o3(+u^?+3?8MdN`j z!g?1sh%+DEx(~{u>~=bfp?5@(2qv{?* zK|usVEP)NAPY++r=OaAZg1*vZD$)~6f!*R~jg`Rev)a_9A4+-s)CX6n@y@Dmh}YWa zXD+76zH62J`xxfT1ZMr&CGp+gniW#cu3^|~$zm(D|7N5zSU|ut(yK|>{}3=}DF2tN z)oKCpb-YD#+VRin)@@6vQLp*$Hh?NWK;R8EJ*?Gxn6=bDDWE9J$TUb~`T0YvmTEkE z>`maL6r9ZRGFCpaS4p}muC(5F!MLII9t+_N7@#gI4vM0?wN{nK7gG}wh2 z?)?^G6t&A8`VXZ@q#WHs$kR~jY2?(Cn!1ZntIvT$G5e!)UOpUuR)6-(73 zPmjsPy=1^D0jIw(A+>yuK@T99^D~$gQo4qBiWjMDAFsu>Bgey%Na z)2!6ZIcBuK@?W_V3cg2H`G{2j_|}n?VEz|d58u8J3Dj*E zYKvvwkuat7Q*E(2;V94nNuDzEX6ARO7Xx|gw_`qA`&}XMGvk01=0JQJET!D8cF{g% zKf|TA)0t*}APVsGOMb4T!SC|r9AzJ0u@8H{fq$I7MzfsBirm5P$dW|C*)#eBz@fKT zRJjx`O%eT)SX3H*g(-v%u?~04MYhhKPB81Vr=hCS3Vy%kpGIl}Md= z34ei=fKG#%8KL@#`jY?%S6I6CVp#SAUrknruDMwa5i1yRaNvhB( zAcGVMmNb|by^)`-L%cF!OC{>t!&bM@n3l0u-)@8vrTfGX^M*ar9MEZpsFL$=VM+5< zWQGL=bpIPDG@6Tbe%-E@Wf%>JX6X0;8h!6Akd&Y@IC_~km|_MyV2uucZ)Xa440DN& zu7Ey$Xvf@a5dKym{E6k&p4r%MkXrSGBXyu?U*Ydw7|*~BlOFU=G)5b{TxN)g6%9q6 z@*5Qa_f^y%VA?6co+`0)C>tzwDhwcD#KdCqrHvj-=>Pl{f0S{Hr_KnDrSwa{*S>(N zMdTB`7iAEQ-Y+szs}C8S*39Pv*SB_Mmc?@;+l@~>yCpxK-+vnPMC#h}Qd1?c*{dtc zPB0m*afj0a*(M_N{1)`#``cR$Zf9|!2mC+->rEO6#(*aL}mDgZi`h{qIH(1lemqMcqAj*^7>i|+V{xE+EmTZ6vSuHW$TgFdP9GH zRNFyti^#<4DrU7DYMRq(mUBeFTr5c&9}yo{F9$DifwoxC_mg>XECh0jWAdxb8(E#Y zn6bCvbVz%hOOuLbaOwB0DP#RIG0td-^^YoDsx?fJ56=st1kJ6TDV#Y-NBmU@t*V^u z`T11NrC^upkZ566-F~;=*{-1Er+)E2cjHq}sg7P7V_~Y0e{&NMwgE7%`;{oBR2ZPs zf)zz%ewQt(VvEsLBKy-n5r{=#Pb3eZ$WQ<`M<`AcW*E2%rBQ?K$p!ZdP-#_cZpqRs;2ey}h25 zJmbeIT($iuF<1xyuEHBBX#HQ?qkIg?&O&)*^kLCt9qP%S6Ox$=%e8vW9Ar0re4G#p z8}R8|zgw*#*bNW3y7Bf3&iLZzwNHJsPul|S)~NmF@?F88uS?L~USnnSd?5K{%h~?q z`pb4_2VE)y4>gc9UvjFCJ?Wkw$$r@u@{F9QRfJ8eQ)rHbx%Y)eMA0nc??0$D@xSVk z{?^m;(dFENfV>|{ncblhQT><4@@Oi;7h+juPdoSU!GDCq(!59N(AY$%+CZ6{q9aa@2XOlXTCU7`D{@z4pRG_yvAG z!a~f#;MXNgFFRCUWqJYrU>2g@|S{FK3foqTIpfAjw67=6hCl3@s(|3E9G0&kTBdxkiMI zt|H9|&{t_Xu-qaSA-6s$#x_o`7h-)8ZN(`dQww-r$^V;1Gq5XrSk5A{wu168E_;{U zGTN>ggu{{`rT2?K+A!DHVxWS*&1+u0ax?I}mGANtCTgcB^0G;0LsW=e2c;7}b7q=T zoRnt!-TZ(curAE0Nq{L_+wXUSC8Z* zr6`FT|J`Qk))-XyW<$=(pGV}8#)4zg4;c?}<&`3;qGeUaX{v7O<7wn{|FML~6Y6S+ zm(662oXvd2rp#VXe^15#%^E&UXwKJ+k$LTmw=5UHl@kRd9kQ8#UGmC2!LKTQiV=g_ zAL|CKj&HisR_S^|)<0+cCZbsmt}pABXH-(|-lo_903kGVUXrn1Cy1Z(3+)i}w$cRO zW`s^|=PcJWQLVVG%9)LBiSn@X7qO1~Z`S`vn#k&!tf}Xo=x{akG2_J&*pb?b!(1RF zj8qscctQ2UgV}~61>>Hs7Q^ezl_BvrB(SIDh*@~)t^4bLoE~Y}HE0qR?8Ck;P2Flg zT7W1Zl2NT_#zrcr7p{pess2kjb@d&z@C?mSDu& zbmUer)NXJ=Hi{t74eOeuapXA~Yf-3Xajk{WM-34xP+capy<)+jZ*iI`D?QTLbv4t; z&J_DJ`GUt?-_^VNFDHe{yJ}c1DDWAzg~$V zZPmpXSy39l6@|XH%t2(4II3FaphGMLlp}*ns~bg!Ah`4?1|%OhnrW#bk4i{`#S+dd zRRv=d2h#wu{xK|{%~2Uhuq@t2M_r!n)s?`+X)LXx9B)S%=@%W=j}hW@iVEUFzqkas zuj^f#o(()isOjOg=No&?J-vU@_|&A>C|Y*T^jX5W}@+420I|{{t+hn<-dN-}K z6=<-tlT2r4{{QQaMQ_-0w~Ff&dZ5);&Y1F^wj}OrV^$l+^;linoKPH$L}z7z)N}qr z+t6M^pMKSXU9J=1n@k}tRm}=7>)^Xq6bm&O7ZAQN%gNx?79u$-8u&^Avl;|KPp-8p zS8&@Oey)&l>2Olps3_;?KefENaM`}vKhJUys}b_`u0b|~WKfpwIcMxk|HwXXy2R9w zI^up)b(Q|tkEv$#dZlAUk#jLck*(I|SC?Eq{@NxIs^9hyC$MgnXGgePY6-!x8MfvSa{tPPgA%{whlQh4m{rVG+J_sFvq{c`y!u}8La=zgB=>F$<< zB=E&hD%48$9}l-SGv(C$jo}ST*Dm|d(35s!DzAi|`+7&ANozp)H=EN8%T|?z$BwcO z5MYA;bH(JH3U$=-x(e<)T*)F`b%aegyu*}EODnKa2c90_%h?1YH7_ikyEbf4EM+-8 zru^k`$&ryL#a>lSnc(J=8dP@iwcBoVIr8>jzee-qx}MZ1{1CsyQwLqTF1ReA!TO;~ zgS#A(QdRWAJMvcgu2H+-VD;X3v%23-a+$d#b490uu?8?cQs?fJk+#p0m~KV2NwQNPGTWBZ~f;%m*pPrh9n5@Hv#dQx?VNp(zu zYyZowuXg)BA*7u-y0S?pQp*B*30aZgK)V{-5vGyAZb12oH#~sHE_MiRm3bq<+u+k& zQ?qu#$QSN#>!k{5%blM+>PkxZ=;*Rb!Oc?l2!$3oIxV+jTIF8@90!aZLH>FH!pFJ) zWi<}~kiY=IvLFRp^hyAYqSRlK6hIdm1{SoSrm(PWYudy73aY#8+-M_ynoZ^_*$^Q%+m0oo>WWRE)-hlN zwvhTsHFvQ<$vn>#Teyd*)1h$oCX=FM*+NZ2-hq}(DNQJeM#8NX`-ix;7;eukXj$-w=w zsnZ4TPYi~7%A^8RL!t(clt2t5gPQJYB>>%WQ&dwgCB@o}PmTZAKbZ1>;5{fGmOw zf^e?>%=ca9_sDB_&u~%(SqaO0BSz)%lKF=J*cE(WtUwo=wf~_66lB9H7gd+A(6u&T zg=O`K?fpdIc>MMS4L-<@3CKf0z|$bZI)Dbz?#!%iyV>54*<$m#AdvtQ*OmM((EmWb zNZ0J^HM0aE9gDY=y%whe?vzP6>%f>q*c8}su-qAqhdsW zyHY~XT*dujmR|Ch@d9AZqu>B#=*7@bhWyvlI_f7sq&$r z)S{BigZXKI3OS%&%w^VC#qq6Z{|I8 zFWmTP^wEj&!2tN-i{)bx{OeCCiPM2JM&Th5{7hO|!gxWrd12Tzu#n})1W(7+@1 z@fd7%Lh)XHy>vBy-|huG8o+U5wSV%&Dy+9{i}O8%hi(w3`qP?E*s5e^!Bf{4)L*KQ z+EvhRRJh|E9C^{E+ zrrSS`@4T5|7=~eU965}f653|Y(nylfoF!>QNgmHmnDZPe3T;xUC`$5_YI6#ykfc)2 zoQjTfs7FV?{r-UOb?v(D@9yt?e?ITe>(#SFad@T*R==q5u+j)U(G#t@o#ltK^}ufC zdwJG)SygW}*&5{oWF-UBoa;14AsRuD{G49fL5^h`$YVc;s>EyY*L%UMO%g`%DA!LH zo;|HvNMt_pBC>KC^I}B1cazw&_y*!42aN!Rdg>pP(&(h>Di-*}=!pjpSty@8M{S{e zANqF*s4^-9(>4C1wOpFjWK`wVP7_Xb6yI(R&GL1_UMK9^i0DXk=Drdh-USC(voj%5V3c=u zeCdlRg%?=(3o$$kS}{2smeb`HK>`Psf>iLleRzl+kz)h7ZQaH{4Y;kJ^jfG0R3$?9 z4-q%lyjTtwuWf%CeLcM;L$5@)&rYnYqd8<|qt}6oS>`97ChN2r0Bu9M)bvyf z^ul_y1N=z)NoK6GerVv(d1xV~FBsiNmgYHc%;!B;fTXQ+x3yLAgCU-1xaT1It1Xv3 z+B?-zed8gU4A}ZFK$*ALg9d1z1qK@;j($=ViVlMzAYFU!**HIo9J*wH=SASR!_d$m z>DIL7*PLXopDTBtNMUDSo>0t{et)*_tA;a{8%*k#hxy%C!KpRKwiiWDir-v+zejk! z_+1Nu5#Xl%!y>1*c)I{}h>i-p0U2C^DvzF7yL;vEUpHPlpIo?eHJRwx+>C6&6Jn(t z_r=73#pk(?!BN$DxeK5Lkd*t{op;ajr^(=GID^TQsuV%to+uSbz8Za0i3El>*G5`# z43qrjO&(`yH5w0r;~}$yQvODbYxp3C_F72J;;qqDpvw-I+RNPq;-Kc4vBQcR_~I@h ztSuYbcyRY>b~(YC&3)yrvF3YUB)EGCt+E9BdE2ISB!)BwAl2ZA30#NIY8k;7uD8V6 zdhP93J&@vuOR{*FoZY~t))`Oan?kDM1l*za3aw@hI|MrseW|o5qDcsKp09WpbUT4L zzYrbgh=!v^dkdyvo2FoHEjM^LaDXb%!xjh@MqJ;lx2~XJ#pPOenK>15g1hoh>f~F> z0Fesug6zmF7p{R%!vYY~bG+M01bnmoQV5Y1;**zcm~dKHID2z5xQlfbADF5TXpV5q zJ{EOH#%r&LO4+Da+<*Q}bQ5f2>heTyQ1<@2$7@^7-a@uyfe#Vc-eHG!sn#aXD;(EX zvZlk2JKh?@mgY|W`|jcM#lvsDa0=wO)VHhonbS8Tx6%&{<{xUdK0t)!$Pi3tCf_vXvl`3q|F9ZPs zAWB%L^I{lyWv`=f++V=WbRi}A)%EUwTCqB=vz%^zwa$5$sZn*k`mqLrPc@0(KtLC~ z+)s7f80Gx|akoh!a0ENjSD}&1{q#N0mYL!4f+PM57P3h4^73%A+|s@ml1Abt09e~k zM&#rAyf5aRpMr(U7iy6GR8;$&qF}ek@$4S)>S!F3fx$A?zINiF%RKtfolmL-30Sbn zReE0p?ZcjZO^-Dj z74Puv6Bk48B|&?FaDO;?n_s$JXwyu_T*h|6n2gd1W+}G0D4KMuzQKubDz6^a;hHpd z>|ElTm#sF7kTt-u-G+dI*V&YUUi9#Fd+mv2%C6yQ?F)80^4$*ASFF}qEkK`8JRIo| ztADPkzo;y{d?Eei?2ECAL?+1Z;u>!8ur9^_&n(YjF)wPJ>(?yr{@N*>Fc7FI)7TB} z?_MI%@Po3K1EH#@seA<%RNi{HaxQPAbI`OX-?GcaJpib};@eakc=mB08goK~97AWo ze>p9d>%dwDK*jlvxwgXIz~%_|YJ()mxMivckqYr)ft=%KOy4GMJUh2R%2X6wpKR;T z)jNNp+Wx@3RHKWE}7rg zMFCp=4R;fEe|+&BJ^aRbpX0kn2X=df*QVkoQ?#oTA7jJamFbQ1{r*-lxxbpL(q#{W z-PBDZ80m};dBKJwQcmST%kuUYB#L>3lHZK^fPPWE*!BqICBK4w!44QG*!Bd^f3g!L#gjGqO-^S5mep@s30F=kDg` zCV@ET((e*OKMeZqo{VToqxW|MoAh0kt?~}TTdVwnOaD?ms*?WvK0{HoIuD`xt16GG zA9g8q3Eg8GLq;a9YD(5oQZ=x3 zf##187xY(2o znc9nkr@B=OotFBTKx-V6$on z?k+zj=CCrMx@i|(EmJzTYn={v4iq8MWJt9SVH*Wor)?=d5fbHZi!6H?yEKj06E^VP z%jpeEo>(c+3?qplu;A8=ra9wZ&O^R2i}1sX62$-+8yFzwS@OjITSoEi$#LtBW`Eln zZ2#T&hOT#$hPA)QBL955iE_S6C4n~i@9C=~wOg{_%EdLk-RxR5CRn#z6xOGSE)3`< zfL<;I47y+Fs7Q;6V=#I*KPJ>ikRK>KWj|76O`iEx{YlV2^ayRpx~|zKaqK)MQ6!52 zK^-%w#H2p?*eZ{}f>j#mzRM(zyjetTsy*9Zs3KNq3m01z17%LTU!sk!b0CEy{r zKLad-mH=GsjR%g`+n`Y)hFXH3L6w0QQ92W>jA7@>ay|g03Yi5w83&uh;L+12ed_Rq(UyhZ}P-9GHmP-E<3I&bx)@krsr3!OZLRF;aSQC06?oYX!w1ESf+ml)|+6ownW z$1lB#H26g?R`9v3a)vcS-I9&Uh#S-H{<+WX&oh&~G9x_%=A=_xmde%u(q+sveAmx0 z75BAR^eVsKEJn5Xm=Bk?ngm6Yq&)O525Jwv4<@cCzx5bxa_Brk=@A=h!w_MfSoBvp z%JK|ve~A37rYMoNZ(RE8E_G+k%V7Q72cvh^Jw?eRnAT7*$W}k4MA64zm!R1m7?%K? z;!o+)jqJ808Q%1ZEyamP^<1qR;~R|OT+t5U(=o_1N;0m3$#BwaVl^;7zDuu$Vq^8?IMT*)oN zQQbMQw@ZqIpOe8r|DJjfa>;M$uP}!zQKg=SYC*~=tV8;9a#2kb8U&kfd>~|AiAc;U z-~DgwRel0ey)zmRo+3)U+3e+_d{Q%e)F|4u%~LYrP~`h@)4nSDB0!_j#D=nLlg2LC z130s%;P*MmMwmGM)neOV#E6~NYQE2E&Bc$o3947Nm*TR=lmiDDh$l6z#N5$o$GV*x zP^Ju!871Xuxvo0?`Ex5J!(Y$L8L#yZ15nba8<`Wk0*`qLGu{c#cVaebyxv2@*2$cx z-K-Oy>v@PGF)H+PYf4GTHRp7h*tB``+gUNd@Qs$zd3xify?~@Q>B?8TfX-0)l?n`i zKiq$WjdYkA&9A2LEonnaa=kgi=K+WsF-Q$UP!3tUD+k?=1}(<3kP#&Xe_6yXg^+^Y zXZ4A6SS)S#K@Rj}B}ScQ1S_`tNd5bPNh0J+QH({y${=s-ANyL3FV0W5wqQ@0$0V~R zNnXamzY3nQqA7&$2+N6)gk=rCFPLb|t?Kkdu5gE@Qu_eXbopHnrSA!7<0E*RUrbq` z)QBw&t9GELtK{TJ$4^DUcM+n?DSd_pQ$+act@p*LaXqMn`c~xQyPSs2A>4P=(Y!cz)}BtUN-798f25iy?O z%+5oo^#oO>MptI|JlHJI>)juWhzh>DFgZ4Q3NkweO%`AOrSvR-k8mar)=!UWSGkyQ z4U@=rIEgZ`C+8!}nb_mf#x}I)YSi>TGVgyh6OIRn9U}l6CelcI^dOehtBLa|rt^zg z0PQuhl4X6F1h_d&3?Twc_+%p{AKB$&9TKeD4bc4sL}x^qGRbnX9%`PI?d8d=ODY0#8KBbNc?F_z6?ez($}Y9IupgRF=D;_!Ubabkj5 zz1(mQP|N~Y#j!zI5+I*dI>ScRc$@S+o$LS<*7u0kX{IE@7ki!-VttFF7h)2~PdLw`Mv+=ch9?!cbG>#mI2f>or{+DxM_hs*F9BS zu^wh&Y*fpD!5|A4*W;b#ar$oUyY>k!SE|Fv4jOZZ;2CvusQ=07CxA!(qG*t7H`R^K zT?=@l^qRVf2MT1IbtvgU^>b{dxaW7FFKmD59wC8u6xpCa*f5Ie8VAJ!9ra`#bf8+0 z$eOZP_r9KO>HEGd^qOZ$AcJqK;((S};B77bTJ8USH~0GK@aAW0kMZC>{kA8jNrlm^ z)VV!lm`f75zXcs_dEqr}f0h6C8M(sZvO3o}7KkV;i{->8gZdsD zL1SiGtX?aY zjh*5g(FmFz@yL=2DPbEDsrFN^g9F4sk_1{KcB}@mr7a4W!N1#mHjn{6QWmSYjWupQ zR4GFsQhn-gWo2+uLQ&-b@D=%nVbo1)96yQtuP#xXHmdBND!xQ+t^}6PKgCFa z8f$={8uoR4@gs%4^UNly`2;oS%4Tw?*9*_cUvKT~^HH>>+1qI<4?)!(*8(I?hAgrpd> zK!{UZqp4n<^&Yp7mi!pYU6;4rtSSLMsbQjW~JD_tW>kxEnu#`E~O^ulEyj#z)O`-t40u zB6{!b?uX$dFZo44lJe?VZr?UKs?wV56v4sC;-)GwN3#Y@IygRiD9#-{y4@6;UlbDz zHPto@iJ@BD!k(Y?B0lu^wR|J>Y{BO$Y}{1lfL>X15~SMvf;+XZeQ%kDj>KBV)s2a3sc z7^E6NC@wN_x6ck+eI55O&Nb{=oB3WR^758pCe$}mX=moPJ`f50uZwe!b6<~hH^qLC zJUU%t$0Khp;mWIy&O}@Fhc6>e%-HB&^=V=h^f38s| z=a%;csOqk=Y_Z%u#1TovC+z++z-;d1z4@dQWTzy~5PAg`#nBLIn#F0Yy^S-x58Si8 zIpa0#RK5^SgPZP(x5y*^D;?N4Xk4e%z&Kos|JQH9B(n0Z2Iu}KuW=uI@-HB)259$_ z%tre#U{t(4+qS}5K=HQC7Ns_c2|$viTulV#Wj%f;=My3bZju#jy49FN6&H1oxM=uzq{v~CA_ zx;eJg@T>&pN|ySLe>5T2GXc5iy&74Qfp0OLGQJbDSvp@VDC(|BXf+_ zLGBn2_jM{e_~u5)@h#^w$(D;U$QZ<#*KA_%f#ib>LjMH{cHHU?|9tc7-iHF0x*mwJ z6Kgxm06TWaI6$oesxp;uma^x%?MZFjwP`0T1OXH>ipf-420>P zV8rBJ-DqTol7XGmIb)4YH2wEknTrV z8EE=sB<9ya(`)egwZU&L3DT^otgoe9f~ofWU|CHvtPQZan)Ch#SvAgwSE!B?>|E#y zI7s@r!R#OpJMT8~nJT6zgT)jrpD9es(PFmPX7n|b239N_6oPmB47_3)d_}{}zKZb1 z0{f1b0~T1oytW1?@Uli$ZQ7#kjY!mg1{8}v^murIV2daiOQmeX`<;Zd&$J&FAU1+% zo8%OiY7HALzB6Y6wlP37Z0`-bEt^Q$Ulw}d0WV#qfXeqz?0V65>JE4dDrwtPAAYJA zd}51p0KgE@|LoUJJbKu=s+1Af z*vv?aH8rpYw39})s_B(~L4{}}AdWIM;w4`Sn|Z*Ga~zlW&)08}>6XPvc|84!*zqa3 zEah<7_h=5V>|X#)RfS0w6Q1VxYsU}Up@Fs+CfJIx7VUHjSVak~kH}jm3@e-=6;%#> zzu^;DL(QWack@+^DmDj6*Mt;^S?Ok-zfZh8B0t0X<(3Q$&ncLXpYuJH9iEx;;(F}P zQ+JE9Gf$Xh4RoI%wo47%+Si1mn+TybQ#a^EwHkL=4Q(-<8DqaqTWA}`sAA{%S;11M zeb}K!&vQJ*Q>VhCnElPWYQ`ZEH%$-ofz|`CeHWeQZ@`}DY|FhKdN=zNyuotxdrs!m zE#=_*VJ$yD-T0Jhkb3m<;HNi{CZU%8AEK_+I;Db%o?AlyNaHUo$1|Tw79Q??bUSnG z#m(KTJBx!gH%E0-W-1=Jst`XMYj!%MAGQC|wdQOWq|J^$=dOfW9luqgk(i);{_g7a zAQd;FCaaP6RWnR{CPi~`UV_vj0?%BtE}UuXyFtFf#mzcjrKc?Ai8VvFk{1yDpMr3c`@8CV zi(dU{3V&;U<*Liu`qq0t;6)uTXQM5%U%EVOZGX(U#f!Pu-fz9P==-~hfZIzZRRN5d z`SQI}YqOlbflKkxhc$PKi?RB)59-oJ{(<9;4g6!G{v>Wg;b6z^uq|Gj4zzqXHDDyq z#h%TLICAV(F7silliZ2(uHm@LVxr!0)2^w8gaN6^DT6TSf+q8q&jXdr^&xNju^)UF zdqJshfo`UleI65jns3^(DjYPGs13T)-S#FBPPt$Daop_6f%2nV4-T~OSKwP zjMhfA0Yhx9I_H(V2Ja6QSD$6#D_&a-R~T-P;F& zh>%glcN02PNkr8eq;qQmC~ONB*yi*M*D`Jfv~STPSK4m1!`W!tV`+J)V74SSt9aY* zcHK@XyYQMIPyfyo)Cen5w4jbz$$s>i_4RnRS9@CiD`>RNU@IW{r8aF+vCips*fIJD z?ody^)-M_K@Vclh3*zk{7Aa@W&*`7c;=0aPl$C##=={Yh7>okyR4o{39%t4bjB@m? z4YG5(b4h#4$BG*&{5kDeIt>qMhi*hQt3Z0@`eFGMWywt*_DhSs6OPeJu{5O$(;G0Y zE{T$IH-#6Q^IaoVL;wAjSu?hpIUS#YvZD~_dl!1b1Vy+#t zvIycOQdF!ytGEm(*clH5jMb1Gy4niQSuV7H(~b$PY<{#sJAbf2;Azm6ChBbSEDDM1 z!)$xyY29$7xcO@%iL_iABN!`D8SZlyi*WQ3utCp%*l##3EfNcZa>n>BKqcV9~lM2$Lw>& z!Wfia71iZ>n#22*UY(kn(@B>vJiNl@f)kS175TNwKkS+*N;=ba&yJMr9=&*Q9<+P9 zEcljz*8+B;rzGT5QF)@e~KFbi03Gpmh zI);@(ET`;i^PlzD>P~rSo^H>xt+szi(@Vx`5-LSo@LG3vPKMN8Hqk)~fWdctFh$}L z!;nTbPZk(QCBsTNfN_-Y{co8}5L_YwTktt@;{xo*>qT$P*FBo#=t4^(S>JAueeM13 zXDFhP29!vKHIwteAphJ0ZYco=2zhvCCv!6I!Pb_A>Py72RCU;M;tvMu8nH&TAG^_- zbTP7|v&m7laGho-TrNyKwDo3MP_Gk-;~#C zM@+WK2rB+{F}U|_1IB_%oh%$z=gXqUcg20`akELCauPB@&zGFB3UEai4F1td=& zvd8GQO2hE@DG}yZ#6xZOrdO)1WBNp_N8JYsj$=?QRXc^%eeDd{2O+8K&p4+s(=6uq zsFrDo?&{SpLa|-Ae4{P+p|&5m7N3IKgx^xAxeU+}ECZSR1}yUv0lzmwSm^jXYeJ9i zF$9irfz$?E^=`glI0Aqyb}uQ~HXAMe8&a$=$j~hsW%>2@4(`ANWE3QAZpDx5T@WhrB=)=HjTu<-T$r}_U(mChKqGNe%>mxw*EHQS1pmv&~dYYoYB6^XqAL=MN znK7gjJQ|nmtEUsF{~2gNiq>2qJEXMPm(4)WMC&ET&gN_|6S_WBng&*hi*ey^_2=2R zSwhHTp=tvfsXK#`e!zS~l>b5)ggh391nP|w3Nn6+_JDQz&FAyG@S;`oqxdSA50e>;msd$9#&B8F$^ilw{VtL-;BTg4&&}7wMJvV zFzHVB{-fEfpmZgbY?jegB&(!+JudH3_6A(JsU(*QxI)82r9v}hJUW+c#4le*9=bT5 zs0dKdrBXKwUKg)*a}95~$u|Psar6{dlDRBO0>oe~Qjj zcZcbdxdra|29*NAXW;!6scyjAb{`n-Biq^Xl7YO6mSxnP(+l65;50RZ_({?G2%PsL zBF~O0=!%q1N@w)5$M5KWcC=IIPM>p32G3=I6+TJy8kmHY48pJX3Y#P- zyC7U2LFax86l6m0Ww~rfl{!UNk+HfXco2OCDp&mJi%#h@eNc{5dVNmiP4rUr)g?OE zE=cx=sCeUt08V1x8=XrL0{Sq06!Zv%x*4m?R&wn_*m@cP?hI-qujiNJ zjguG#q3)E0OC9a&e$gN?n4;0~cMU-iM8OTPp4I?=n8uHjCgL`WsZYxHNc_nwxRHt$=DNq>j<=rLdMs(l_()P_ZR zO$3i33ikSp?-L`vjsgAKllu1*?gucTyTJO%Ln-ky`#;ZEUGacE=rn9)&&3SPz6{xv zcFxH()u2SS{gY^BN}>>Lr2%qcuC6JRo|qS|M%|pzZeW{4k&jJ@SG_il?3M039m2dT$^fE~b8P*{tibN@4zo z?)|K$8Lb-312ce{)nWv>$x~BR^G}|d7Eq%OuNB&e2H1cM8Hm%2?7iYlq)I5$7-B(z z{4n!l(R^|e=c}I@^5VR)(#+HE0tTBNH+aKZ7@*G0QIscJWARaU8M>Whu%890LE=vb zYCN7t^x#1Oh3IBEjQiwK`|XTVH{fz5jddPwMyQB2$-Q7fyMH-;7a(M^NV8k2cvapF zDMcrVFv#k_k{!MX!qLGmaW>Tm>kim$##nkmyta@Xw{a4(WQF+rh$xHQc3A;n7I6B< zTtO%q$?2?b7%UbKzRJbuKq#-eTXCI(Xil`xjaD2-q}!3DbY?B(dIJ180k+D16C#Vn zKZR}UjcZ7l`wT37brPyRG}FBav9yLLZgcv45+`0M@3q_hgfOo|(MD-ksP93JT~`X0 zuFzr-SSR|?AaH{5V7f7Z134Va(*48M^%GV-oZY3{)b4dkrMAsbg&$s=nTdXjf6!gd10I{QS_S{ z{d`!l^PN_(lyTR#m4R)1bO;%E8-Ce_Jy(E4d;%oToS5C7@-K5+*_6d1Y*R^=MqG3k z&rwOj*Vh!I^E-grZMaaoSEoCRZd$3nl@z}dsdo+D9GN3uPneq*8Mr9iUni6ntZW|s z2H6g?Z@A!6A}P(JoJu#h{}@sw3w5$HKeV(~33`F}r?|Us1}FEN%>p6ml51!F)27eh z0zM1fd***Zg38G_GQB!WbcDZQwugREH5U)DFd6#*B*VMNy2I z%;K8CUa83Iy>`d*YuugRl5PB`;%{{+nOKH5XWm;Fcdn%Ab9!36t#rpDhHj%M((}pI zv6iw?OPD3=VVzWa60Tt=)vlASgxH`a3f69uRO01N zZl3ij!YeixXd?{W6+VU3NOmT-!c&xt#K`!I@>!6OYso?kZX)l1@N&fIKfDhG1#0#( z4}RFKRB^x22`h8@2FZ9UMxRsv18RS(3EU6s2GlxbC*bBq z+NY!2eLt;&i_tRz3adpwX&Lx+(J>R9pt7B^IJa!B?OjaJ`oo)KopL0j6=%{C? z@$O+f&RGQ2bUK18SGPU1=f5&x5$(&G_E?ngPU+XXjl7{y_;MUnSK+QgpM$pPe*q*$ zJ)2P`JK9jbkv1KR*TFlWK4&7*rP>{moB>)jTE6e`FX2M{GrsJU3K7 zlKTwgv9KCc;M6&@^Sw)ENCYKrFXwU{(#R)5y2;(_<2%50A?Dj_Q$7ny69X2@6B3sS zcF;A$o~!3{E(N=L1EGtH=q z5e`9Z)cS1COg(3B`pcPp*#xf%w|#e-Mx&n?v36ieyuX}o*Dhua6%80Q%nSbn`sD7c z6CHc_xG#VtRk3Gv?;E~*U#vUeRxU>bGbymp8#U?Dokx$-}w*CxXsBaNV%ldwclB z<)aNNDIEBrJX`&Naf*5S32(<#x-e_|e}{mMDTTxJkq^JMy!0+Ny!j#Y>Z|b7rLJSE z`PZ(w9{-lg-ud9##hb1n@xaKL8!Gqjy|`pQ#JUf%s{e)=DyTRSq6 z(l2_Sanw7lmpt+F2T*_a+mm+_gl~Se$1R;a?;vdXHN$(Q)|fGacFa6QJ;k38}byAdm6 z7>+niXWiVTu#s|8%t1LWO1YU<9H5yOtw$2k+VRxi=2OSh;eyt+6)){p^n-!I4V@&p zqInB#l%`0!#~e_x;mOO_%FZ)1Ro5aq&tn@;;EzCc%PE-KNMe8@iiM_D`gW>PAy)iz ze8ZuX_CdqH+t_Sva+a!xnJs$mf!3S#Wgk@HfUIAuD@9#`o|RQ{9h+s;M2KAyX+U#J zlK_OU$WZmvvS=IaZFHlHh>8VjD-WA^VoE`cZ+!8qnGyD^)lN(o5G zQXJHWl}l0csPNj1N~-n*a~$I!zvN?{O>pv{9q1kwyo0`yhmnIfA0W?=VAYoEs?Vx} zJdZV6#at)7-H9GxMh6&Bm=%GJOW3M%+es|K)8)PN+~e>KlWLiQ+gi?wg5yFTXS-7? zZG2c4SE4=?F8G@F#O-gOsY>*cZ1pMqTTp_MR`9Q?a+mi_M%wXZ@tthfiy8S(T$(R^ zJDn`7_X^zhv(Hs$>(z?h$L>dR@Z}~h1#&K8_jlOv=r>gBpuwZC=%Lh5+Kp0;P}JRi zbfD{MNkt%b&!A!;4KVCHmvQ|$=xf7n8_v5GK)KnMAg;2@pIuizOxeZ)}5a@=40HBG;<;C0ISOoV>tb%a?8Pn7~G%KF@>Eia<< zED`uUnPujDfrql;d{%bljpxMS%59{=xX*r?vpn!d3geQG`rhly4-B{0s47|C>|T0A z%I#WuP@AO2^;eIbs^x;MXn5tt*(Aom+1QXZ4_&OMgxr|OYU-(UYbNJkaZ{t_Q%#6?^7oG}PjGG7ZF!wRAsXdA~{P-!#4hT6WZc62qQS~L=COjT+UDl;cyz zc0iC9-c@Ke_d|isF;q-mC?Fnw_{21!i_=hjz|2(v)ID-unb^!#EEyApj#p?MVw{Uw zw_s;?a!La;IN;Vrm?d+pG;KQGX1NFMJa4Oftfg0f1ps$nS+*_y%C-x}YiUa=$}@bT z?M_vIdL4J#9he8lUya_rf4coab>6^+-fy(hqh!s~f7u7<{|(GLl$xd(hI*t%277L% z769?TW|AILc!z6ZVV-T29BdK@*3AQ=h@hnCt%^>M!Fkuv6`Q^e=UbaDBSYy%?k!;h z7AI#=2frt3d|rC8DXJZnh4pZIA*F2kN20NU`CN;BFZTa#LX?EF&|8~$f;s_5%NY-KnSH5nN(zEd zc`Rr^BikdaA4K~KV1;aig%v|6PQ`ro{Sr{z7wE?g%J zbP1Hq*B@bL(kGRU*Jx!56_E@!)wZ*zG>~Sf#?bY4!oTa2F;i?R0N$t(pd+cwqvX3h z<@gw3m%in#U5(t`EHok7u z=vr4!DLAQ1=W~_FNfhO>Z}4}63@65VZ`xn0`BAO~PA6IY=FH_ismvCLE8R4-ucx#l z{2cKb*?YsSdCY)eJ*VeCqhgzR%qh9sNF`ZTWV>#v+1GfKNf&zWc@N!wBc~B}mju?G zmpS)ma=}_f(|NsP$S^U-!{B{?A+{h8Zcl()oK|;zg@2MgzM1|#gDObMxJ~67`p{yQ zOU`19VZZQjt0a!0y^xQ{QD{7o<6+fJxey(4uCZ^ObLP!_-a(c<=+#4tNxqO>nf)cx zN{(o~jknOOWBZamaZVq$s4NI53bq{8@Xv@>L&cE5HySA-R{$JMEDQ(-9#dB9j z2U!S&p&C7EzxR)7@RM(ov8*}Q^#FTCRF>>BVp}!? z-zHD(_d@#1_z)C!D5?DAk0TD|ug(qA)NedTsX2V;>T&raC~@dG<2089ojRgvPiHpz zdvuMTxcmVZ=xV3F{CU6?U|Oo!Ygh!-g}b+n4Idf0J;84sI6mWF`rXoFZ~@8zxg$cK)`2D$F{JG&Dy$A6G-wO{J*_j&Qm8~eK(#n*>a zg1%AB2V}Q#6PG-dnUTnJ@(wc=3u?hP@XA3|p3d(ZNt{}0!Zlv0ptE7Swio)URT4FM z0EoZx%B zqyiqQ%!YpmlqFj;m+wWmvrq1tU= z19bzSv7U+It7_8r4YAnU09@-z^-eAz_nsKWEi9w+HQRSy0+wBUr z+#N*qbG5ju0!^){yH^E4o6S6d1e$zI-m@Yn<^38OYHwb}!cj{4Ar9 zq30fItSpO&W*MFj@%vi_PY?pf+TxB&Ok)a;Xe^lCq$5X|!A2P1VKe0uQosp(g1KNT zDeq{aKrd@I@d_1!9|H%C8s%NE$evW&6ThiDCV}(}966d;@jTJb6QNH>D7RZ!rtLBv zD-1tDp^;I~gz8J(dz^bs8WR9j|+k=Kdyo-sGHW-64cDPFj9q2rp6N(oe|1)2S?cGgVbp)!7W0aO zJ=W%brvtM(qaYiFzZV6xR+nx~(nn2lEuJe(0X&Pvu)0y@&2Yn7S+usAryhq8FsKkc z3fi|v!3rOY(1tbB5$_Je94i&qs~tGhjE$A~3}}3&Z=>;~DroQOmdFd4v`&-1S-^dX zFswXWnicehbR_Pbs~m+nMsinWA!pIB6L^rJ_((ww|7VAB;v_7H4096_OViCiZzdlK zg~S0UWt!PpUOSn>WAA%Gab%djAVfLHJdSMa^$fwkRcIW&%QDRz9t2jUshuFhh2ao95P}X&cOio*IMiogfV{RN{=~ z8g!rA{dO(OQp-&2CflQP%= z^f|pySiC@CT(HFpjnqIW613%5QKe)wjYnq|9M-BeU(v+_s}RO@NUY>bCX_3qZU>K9Cp>>p2g zCbned?Z5gqS-MhhMbl=Ppu%UI-lfr6jrF%<*k>J70$=2Ob5ZU>gS$r0PO|pNDwTil zM27v9{P(?^D&N|aXPJ<_V%U%X zb*Rg*q*o!GQ2_?S7^9g+Z?8{GD4t>}*!$lQF%@*wlrzZiQ=>?iy|8g~PD5EpDTHZ<>Qiw>ecNyI_y~-soS3C5LIyoSYwA*u+cWz#XUE6 z(B$2e^w>NZDrpIFG1fQ}=@PQijBPXA=j4};Rl@8_3GzZE{0HsN53*${b+oy4lVB&d zP#Z1yg;_@lwx?G;VEpl->~H8DyUf4cxSV}~a`u+mmiR&i3{+Xvtzg_T`~ltWd*(`V z$o|ls%Qr3;Qxlv_>MY2Rq2{1zeE7hS2sCXQTK39$$u_3|0UC=q)O!)ht0Lw<@idR453(}^j-*~Q)us{$Y62yFKOAv zXQK9qrZ9Nas{&}VY^d@`m|It&vLZyZ&6I2H{U1f=;>h&(|MA_;<}$Z2M%&!xerImA z&D<}IB)5c+dm|N5Z8P^vb1Q_9q*76mO0~^xBq8}o@|jzy)ED(BmGax~Z+M?`-tYJ8 zJfDw8=fe4O|9h|cyF*5-0OU3von=6d5+#iECBn%OkH`C58IYnz$yBaXlWEZBao_?2 zKFomU0bm!_`_mc`C&&`I3~1`OjFTEtT*P?AlzbR2{V-hWmB{7;Q__Ezs|82FzU<&Rf)*Bri@Lngp|KtRJZ>5tw-6`3bj{{ zSalZUf-*Yeh>}zK0@aHQ)Wr!kXfv@eV~9AjUGv(Y(6(xy)%xRMq4XyM=<9G}KZfzk z>o^VYh78(5c7j+zNnMD^j-C*o5hx!#g~pI zb3+*}SIV^mJiDIj+5rn^Xb%Gh!W$)B8zt#RuNDBdWjN_bhIp{m@7WIV@sh}6fbtq4 z9stQy5u9o-`y~AKj@zTbk9Zy(6}~HW@-)3AvK{FUym!n(@~$bOGuJC31sB80LY7NA zz=cvEVZHeDq6;qokVT&-J_Bq!IylE7yx+dU)-_(xJ5TT)_wll6vpMe*bgb=E{9(gx zE3f-Ka4U!&K=Tq<(@*&N(pPusDnd%2#{|>YFE=1B=wYi`^gV89h$+k@=RCA(?%L8} zFOWPAr^g~AnQn5oUPF=Jki4Qo25-#DmpGyZI=@vD-wL`};D7g;f8=)*0|k0$Kkv3; zP|POU=A0~>5d&_Y?({xUS;|}xC>-(sXE|~*sPoCk4&TKY$>+J$PZ#4~&Kl~q6ge~{ ze2vGKiD*0C%Ylyuf&OU`>h+R60HnrWb?7absCJn+d5xfy18cFys#j!wh$Nt2Bx;%x z-%F@GXUJMQp1s0)`OU`S7o2NxEZMAACW5Iw+z7WPBYM|!m4c=;RA1G0fNgtUSzJYn zk;e<7S01?Opf9g<$Tq^wS@+rY?t;gFy;IWleXw)twoCNZ8v3z{DtryEwPPK26aZnmYwHVq4k-^C z;c_?ot?URfm%aclJ#jqbcJ&7&>UQ4=rmcgX->@XWE-u# z@@z+@QXMLGN`D9AxLPNbLA&MQEIJ~Hj!^WKljQjOt&D2$Kw0QLNlf)KXsz>Y`twOd z4KnP7CoJ21Y)PhFMp3*q9>1R>o^t*7-I|23y7T_Ll0k16`FX-<8n?_fIR{Vw5w^f`}UU$nijGKMu1|7^&olJvF%el+$jz#_;BVX8*BU-lKI zfhG*&4qm!TxOAFnj`Fh;I39M__N&(Pcn@epee}Gl`2mun=l4?|l;b?@BXsgPbp;W={y$QCmp|+Hg}z}FnAMEDL=4dL`=!zv-dtLGC5Q#{p%a_ZLksI z?yBF7WUP7Bhk(K8`(Z1O>TkbBp4Kg`mwXUK#5(9+{d4B$G3d1?$l;Y`MO@alsoWVqqXJXRsGB_ zlI%;FBdJ#nZudfqjPCaP6&anFOZk*`Y7;!%Z~w{sgSNt_hmsG~14$WRIG`SmdTLb5 z(5VmEXV7He3x2N-mLQ}%;@6RIOx>zBe2M>C09W2`NI;%dubon;Fi3dkhvBKJSBS5$*G% zJP^A4qU=OB2p8)PxE}5#4u%7AY zw2}`sf8Y!_x&RHG>z5Vr2K0PVD&lFNU9advcXO@UW6YrXTU^BMwp62b=4AsBLODp_ zXPmm`WRzg0Y&*Shs~0e6vTfA&j>0ZY)W(tj%i-pXZc#V15>;+u}9*}n$;niJEOntqP)K*DHtnb^SA8ILM53r z#UC7P`>I-VJQ7HHG=`${bHm)6RLg`kd*MrjX5CeFX9X?8c}77LN(l5xr{IH4FCkth zQNQ`p6$ZySSQ0Pk`vh5LumigHT{!YbMpTW19*!PNq@p7Grp=pdo}A{Z^~2@Np3sp} zIk5~A(@=OStMCXhImOIwLYBM^k{f5EudGvW9uWYAaW?ACC;8RFaV=R?vqUmm{bTVE zb7sJ}j==z|&UELPDdHvMRQf*0Pic|l3fyiVtA8J+t7~{1NM}X*qyAzbdhikYKS*lx zFVSO4kY*-jSempGRZ*I#k}YwX2l+kqp8F*Uq_OK&taJOsW1|z01M^$C#sN8!A_sw` z22dhVV*`HhIRxFq)AP}Ykj$uCM8D&reQLFDW&-}x^Xe4&7I;);l9n}o6BiEjgV48I zOpRh}@re7I#mCx(&ZfOZVFH}YV-!ruo=GFkM&#>iPz%b;s8E@R4y`e=IoyI~%9fL{ z6IPkWE45g@<4Z?GIjML4Z2lXTeQw&2CmU%=%eTR&!Cu=*$8Tl^B=Syrf<+=l{YCf3EHz32RZ01IZG#c58N8!{ISx8BJ%i`tx0B7E~kGzwj#rRnuY@dn}fRx0Lt$s#S%yqkn9 z=oPW7bSm>xcAoK+@?@E6wsog?zf5)LCuD&UGpK+>5)XI^@JX&GA7*G6krSfG2)x?i z3NY$qNxK;Y_dS$=6?ynKvz;_HO|LuDPWYb@W`~?SA0OmXEx6aVf}Nciv2EbbAK6(8 zho=`bHG%g{t9>C$E|^kD?|w~>wBWIzHlEZfs&u3)TPE6+gJ1N>Q>Y7vRef+K1)JUQ zsccl(%Kul{adV(SxE4KScKOKh=RuRV%It)4Msc2*LthOeozJd1kC6b#Pg)_}HEl)- z$Dt6ZuIG{(;S{y=A9gu!N9&QNx*gZ`&F09U!bN{hOq8T-C>QJNoeaSY- z&VD9l5sXBo2(48&Q53+{`;v)3m#TH~Qh--S2Cz|2kQD7yMR?&6$}!kF(8%LLtBzjW z(-t*(I4z4LJ){S@ok>}>#IcQpII*$|s%>r@A-%n*tVdM1z@t4%Q2yV}@&1Z~wJwHB z&Y0`{@Rl(x9k*$HjKzQRMOYu0;~$m~^~e^j`#xaECe6>R?s2o6HYr{Qyxs zK`BpbgiJdY^lNGrB28j3VuwgSV92Z#z;4Ezhl^lnlFSvu|Ip5k&U^QkOzOFbM-%r=a@nq409 z#_RB|2R>r?GnAj&sKib?VK4$Hn$<7YX&e_vP1cK1rj624J48lw9!TfoHqmpV8 zwOJopjSX(>fggqyA|Xd2<&87cwK3{-nd+rq0eTrgYqJW~?Hu0`5N;h5xt`s;#TFOz zjKZ(_7BaQWDwLVo#--0cAgKvA%7L@E?FuZ z$-N8%}ZdRTsv`!7}q5GTM4kAp8ye3+*0A zzrZ|o(e-0n76Ke}{P$hWSYQ~hNeuzE?SVY;gFf*CX{kJ6jI)OGn_t8>pD+d4nX<#T z9PRQSJBB4Ii<1e+cI3eWdjM^R%*F*&t`teG|H!>jos`5h45fxC__~rgU zs#AsXcz1bipn?P(cm)EScuiILTIsu>sPd@cN(k!%u=0}`OmDs9hG$;92y~{gAq~#{ z5BSLLTU);AsNFKL@1x}L*n7fM^P}mU6o%f02v7lPu^F#5ghP-$Q1dqcF=3WW7e5>c zT1TFFMWLjsbEb;(fQ1CTD{sitf)+b)O&)(u{z6KD^9fSk@tM4v4A3oe9jv_NcxEmd z*V;Rd*R+GU?^D^A-KAU0n#pD{dVJi(CNGqgdr`9ji4#W54vWpvE0UC{mf)sC&gEZD z&etRw02AGYs(p84J_b51xs|?mD?L`kW zR6kV?uZJo^IhDq42)?TaP*=ET2Z`#*P?KOVb>(tU!tV6nz|_sUUqwA=;zjIM?NP8= z`vky%2c3c%m34o}A_yR&v}0Wrg61QB$W(xWvYuga^? z1e(s5)oU=b|Jh*P(}KVZKu0FAa(F=*%S>Dzd@;xV83{Xo@wOJwRu;vYkQ0N7ZY2kM zTDw2Qt!q4J&)+@nM5@!sMYWxkSEMe80kVj~hYu4)tmc_#O5iFxE&o0#JzqZID7mz3 zW2eNhd9*3QI@m1Fm6?+io2yBOJfm==d!SE|oKcE5oy_UAr&(4YGTnN8*IqTK4rB`R zt^qDwoAe%a!l%5pxTi9vKAyXn1)H>ZtR@&Zl8;Z?>c!3$J25$@eL>#ioG0Zj4{Cwh z@dOpd4P7P+!2q({RaB~33^#h9scH+pHaTCf2aMqsa8}4Q&_+WmHv8i-&4X%2%Z)>; z`K~1sDhG-Gb`p z*letd&V4x`Dwbm%4s;l{ewR^Bs8NT#Imao!coIaG?%}$M)b_XyGc!3X)XpRdlW!PU zhj*)+^u4Gk?g!lDC{oHo8`WiTx=lqO)kc=;mX5vSGpuF#%ul~r+h+uT$89M{^&7Q+ ziI_bMuu|5`pM|ZCaQf%795v8j zr~^ow3Gwd9Rv&NbXR>;(1Nw4*T&2yK^U z=nTxc=hlmhb%fZup@kgbMxEXR$|7FA;|jO?Rd5La7k26(0wl{>y6UuW;JORGNNA*B@I|2-r0Ke;NLo1IX5RK6&*Tn{t9T2gkc ztvS#vug$5gBF9(QXR@qVfGfP*BmJ6J&B(Ud+PPFi%Gtjkk`n&FNO+Le@yd{Q z`+EBRETLrM$Nr3J#6afVpeGXh5}#iS%vRqRL7JfI2?2T5bUejUXF2phTd2QxrhVvV56A z($RZ~?jlud;!X{4L%2G)l-AqZ(0$bMtIK?c$F`12^%Wr+jxKiJ7M3Xc6;-l37^vvo?L7A%D6)cgHsCgCfRN;5Ff*3>WTU&c8gZpdd z)~4MsOQiNVB?Tn7Lymo|oRJ$!ZdKh|hiYU={tsy1SuS4W1RPtH4(xM(FgdZ$e9KSn zoVsjy?u2`}Z|y5TVovNjJ2)R^kOwV#Sts!*B!+b5E#DoFufLqAGx56h??=|dhJ@R{ zAC{i#%z?9;;fe|tg=t*SfyT--MH!--#GXqH&X_7nN~MqOCA8D7W-?q@{K!iPG2jAy z0>M73Ii6feoN2a^5GcppDMN32W%BHijc-F%4xSN8)KxcL+j^A(7#Q!(nQlR6qVlds z&PQrD-Db{5pd}LSO}kut8d^M^Xv8^&%1I~ZOoPF;frn1(-D9)?)KN_0q>py21j6*5 zWi>Fa7c$WyrA`F9l3F%8M5UR(yfzFXeq1J?3)SY7q_AB6;-8 zM&$|hnG9CCYPOl_eNfibcjjPdJ}`70)%=+kOddw@QK20m$>RV_=d{W~TEVlNzknLHb=%9cX_X);7 zv?kqod6_eEB2~Vk71&bo$$Mbw7E(`fU*EL^>Exc0+3}JmkWx4GgV`SbS1LMPJ7Fa_ zg!{FU@5@Z-^z2yT4;vo9dpkRZ%iU7oV?3TubpbK~fXS@hEP8fqfkWF?f7qW_(p<#M z8APUd%)ER0xqVT%&$Rrh>5$&0Pj{wY?!$&D=2*mXTu6EL>5lFdU~6Xe|Ih#+x%o@) zm{v0wn9f|?W+G&H(omd5Y>E*{!xubKQs zkm*R2;O?||04Y`Qj1aaf3ro zGJkCT3qHAb-e|M>>m>)MYWfS(57Du6jZGPY}OLK)7o0}0Pjhr^=BRS z2`yS2j*nT)#%3e*-!G^7IFty}ORdj_PI{W;5t|C+b>Un4IURPW!opc zz)XQld3;M<(!S7cwsN8_wA|h1EsZU0eSe`Fz~E8J#`e6f1UOssZUzZz6RExe&~f$y zK>hKLLAv!PPsk60${6b~H6umye(kpmx{Jxpw68G3n_ZgHhE?5@o-H+jED92Dx+A`5 zw=M2;#)Y(u_sNPynN&EjUYjl9tY{W5qfr+n?1IXA!7Z*3QQnqE0q>j+UWxx*)IDnY^_fW-1$F`M++nGVzhK4A zOPl0(Q&BB9I|MqX8;gTAGI|CviCxm~FWOjs9V|)a|MV@{HS(v`C)x3TqtC~t{seey zotsJ>(ZoiT7o2$98{uqHH(psxkaNfxvFPRS8ywW;%&m#h=ZyC57S-)E`|Q5@%|?a^ zm_00-6A!9vNJ#6R2+s1_(pau+%*;aWTGsDT3u41bjm6$}q1B?9eual-PCj^0_2z0D zr_>Znm3Y5UXWA#_(U!{QrmZ$Q*@qL$M+vD~nt5xT$2NxuXBh(b6WBD6rA@v6l5|aP z6bC zcj?intMwSwUivhWkl5sA;XO_e{5d0*fo4Ip2q^xIz{T=HG6&xiEOcF8VR>vvO34W+ z3Irz5LdetU60#27>1dC|U*SyJHFXiojA|0`5~>+ZE)6?;_PGgWu74X)W1S2U+sX|b z(?=l({4iblTH3vZ#c-lfDwxLfe?tN&G|LUdRs&4_-ar~@eJ>(JB^ry%v{;%55Ol94 zOkxI{6OV%fBjYh(W@XxIJwYI}Y~a6b~YUZhpr z_t|PvILCt;`{0MKMuU`VVxhl+n}!3{WD=gwfYcNxI58@*iBWf77!5YbJi78(Q4DTn zi*h|^b>13R-L9PZGI8$7hBQNQ5@jNJ6|Yo(xq5?)T_z7$DQ+xcnn``jeoe&|ifrj& zw?S>vMCl36W%W8zKb|fj*6f5s(wKay@gU&N(NsZgQT5whij;~yXVbukdvH4 z$*OIMO6=w!Q1hA(yR=P-HI+YPlj%o+Z(lzD&U9)$o&2InwuGkD-U#%s{9hLRkya^# zcJF6Fnnzs+RYBYiE==U>1kJE_!no!6%trHeJCTW>2;y4;)a;ITa>x1cP!6rF802|U ztx7(5js^}97GTcruZ(-e4m#98v_^mqTDr85hWFoMUn zzogih0JCMpX`E=d3$z#jp1PMEA6Vf;d>yXfJZ^z`C2^uOGs+{-Q6NvRL4jNG(JxuQ5>iet)t2}wN&v$e@G+{plvarrOol;hW`FcQ6#FXz|{_mCo zB=FFM>&oD2W`_>Z`P%i~oFMOZxJ)_)F#@Xb2z&@PSyx2rTwp?^oNV&aa?}oMs*S_d zO7#t1$F%lY)O~0zGXo5Po&aQG9ObbkS}93~!|RWPNMPT!UGHKnbb0Yg;+A5aQ(icp zONR6*f91M7de)H~=e9H_mU>AH@>wB)%@4nKxUzl9WCQOc_5NaAeETj&23&HVEH&$z ztEGrP2{*1*c)CO9%o{Wv<1S&7V*9fmSaZ(b3b}- z*!Z71MQv;N`%U*>M{3qyvqL&{C$4$MNxXeK-ZDjfm;q092Ugs;%rW-zgnSLLO=N!YmRtMz*!MbcDFC8}XWdMZ%u8z0 z&T?!A@1Xt%%y102b8t{uy151s>5y>bDE5Rf@~i5H^qB37a!(acfZY;?cxrRGtD3Ek zXH{0w<=^k`k@)yRAte)tJ~snkeq$fC9U;xBNkE)1G^(i^*mN`-Y$syah7&5CY=llk zjv#YRn!OC(UEHT9C44*DN1|WIEcRxT>iB=u`a6~T|MNEO%(mZtp5vB37?(Z7nzKzT z$#0}uD6wm-*xzr~oqd2kBR8Ye)u+A6vy(eun%4I`CtH37SbmdY&f%kof=?Vjm=y~{ zWMj7}lBFS9A~lz)Y#RrvE@wDt8&*FD`zChawglEh?M?JPxGt(LO#v7d%jydOx;s%+ ztu%L&O8(2|t z&#u?E>jn-76)*3nIj2!>EGuFAbK2?XLym_w^)5+n4|x83(Y8%pC?F%dAi-Q9mIMI( zDU?V99-zHkcJSQG=<9II6ZXg@GSG>rkYVoh*s4o1%3C*!j~Tr58xOrl@sp|sONa!T z7-wogx$VrZ7LhPje?KN=)c{qKDY+ z0LtRNd0PxN4%8xcv9*i;(VLML41KN$FoTyc!BfRC>x8Tc{PS<`#XvfxlGGDh8-Xw+OI+>is^Y=3V@%RxuDU z+FWWF9}B7*8IzN$*NF{0tsOkPLN<@B?%V+YiM@PMluz2V5UuCSfcfXzO=``jS{-tqTcq}w5=kP4L!i?SKJ&t>t5yt&_!04%Y2!B?YuN1=5@E~KN7!&&>iws}_ z>*Y_@0P^+D#fZ-Lk@$J#pM@#~yyz|yjcMFXc8Z0i6OJfA=Q|PO06sMGfe3)annK}mPlgJny^&MI} zK?=lq=^V2ijaQ86*M8T656(E_(t(oRMfCCJ{rjtw6Z@|FLZg=1I`626Mw7j^0E?+w zyY8UqwP(lDv)u@lx39MU^3qz#ncrXWAnenVY`{?<@KBIm$9uHW;UTZXh}Ekt zIeM=r$^Wxz+fsU?R{yU~N3Qz7#7}o%k|X=~D>nH{F8Ms;uF|A3)^Askn()q{E{L+R zmQIye4m`piASUu2_#QCnkuWTN zZe%tGo9Q#!4%x$i|LZR_iMToF?Pey{1Qk-%94a;nAL%R?7ewZ~Ah{QrTB%ijR;XPA_yK#Bzu1RJj)Ei+*p z3UVz8*>fc%fbHKtb<@2;A(q)zXd2IQKU8>dH^p0JZ4p_`df6y@Vf@zRO5nEt$d^DC zhQ#0Zj;D+t-*<(4V;ym;1JNGmsm(cJaTc;{)UUnxO#JGe$2W9GjWf*mrxXG3r4I*Gl+ao_}== zi7*e0_^Of*MjP!%V>Aytj`og@pfdo-aWZgCl}QQ4-ph?k%*DQvd)5w1;GD zpMw+?Q|V+H-lx01hO6v*|fGbk?qa@85w!u9rr<{k%r>x!zjJQDP?_5)dB6kI%8**b)2pZi-3BITjF3G zzFSLqp#ePJ7Wp`yx_P`Q@?^3MiHE8iI^AjP&rUFgbEyt}H&>|m)$8zH)*id5q?zGM zqfWYHX*CkC%}@;4#p|T--o#C6KR$RPOmID(cS8weR|vpsxx7721MWD7{Xw_iBoDfh zgVjVwi5e3bcly80)4IoL3hBqLRRb^W7w-l!NjN*h;)oF)1lWFY!4aKLtnFqjUWlVX zdLZf4*YRk!sjJK1QO{5s4BRquy}j^JFfFR>@Sleqpt5Jx0~#k`)MXqQ9GdH_%R5Wq zxb7=IPPUPK6q@4@@wGR7}oCvF6V@je8H#OLU(B=i7reTD&(9-YS^y6*(B9X5+^jPj@Vb+@(*iJAv)S(uWE}1iQ&)7^ zN`PT}kj2TU#R>v6_-p>ds{IvfmSsdtt{0JwVN!AZ5@iI?Ro4>=#X*etBIM0^k02VH zNINlK4s9%eiskyDN{6Bnnm+2b1CNFghhs-An~086V42BmeW}o)c#Mgh4E# zl{0n1lI4S^{d!m<(Hk71BKMaJv;EcTObS~*?F(E$ntrJ?Gf!x6C)k9H@2df#yvWDU z&OgF#+!Ilah&=0Rmdbxq_lYiMg4#1@U)$LH`?mA$+nRS7V@R+l4BptOzGvQ@47;&q z5!F5denhLsba-|JpRwneRA2VFLLR6TX91jmJ1x}Sbn3I}p1h;$I?JrQ3Q|E)S^^jS zV%_VP^o!9W=%i5>jh{dp5$VOayx9AIc?)o)LXk07hXfS(+<(Rb%{t&dxGyD6s+SWvm!1kd(ArR9C~ZW`t?$a-Gg21FNs6Pdfp)yraNW^e84_RFB$89 z`8ec2!7Xw7G-hNk!lnv;Ts zO6|7qj;mao&Y~qz)i$OW9)=rAbT{(rulCUJOJBgq!oP`qfryVnaH&eHCPE1p*<_!O zh4Ar8=JMK?QK}AocnR|Yt#T-OwZGrTV{N(2bv{3)NcjvL2*#0^S1x$X#tm?jgMn+( zyi=Xc7i|8jT+TVtnE4Kh3Uk8`qO3u9O~7S?#S4V$KIbpF zkSBzXBt#a&XA-GnNjo}?kh0iKL6N+}>2QK7czctg_3NWrpI41vTmMrJeY2M=omwCW zE=>?pDwR|9qJ~TAa2;F~>Gph}tbJmvf01!&J`fzOs>#)%#Zx(iYTvITgl0My^%30< zJ|~qr(g;HbY!tM$lm|!T?EWPp6(hjAo%`e1(jMwKj8)B@Gh=aH$Ml-> z-y_!YDa&@VLCfheKyzv5lEcs@dgRyosuX74f&VLC(P zu#`>Hm&&|OCF%~@oWaT;^Z6^H=FkvrNBc3$GltFR0WU;v-ZTLv)#3-I+r!W@QFkKc_%bW*hGS zR=qSD`QGweM06Ax8YS%OWQ*&piR*fY+6KA>i2bOWgr9Iz-)o`K0^8mMPAybsZ1s86pYsycfLnPub2g**nG~_{Mpy zhxDLjZH?7KGV_W+cY$OEcv*uPbMFS$5^*9J#F}Z&wyen~Q$JcNr`I?gJhx+-r_3d)rVyzL!zvoO`Tww^ zx)!AaP)oaJ8sSn5PyzB{tF|=&nQ5t6!ZOX?jJoSr(|MSbH1#~C)MD~j^@-?fe^`li zfZ8M|% z_RG;$IXx>HRDYFa7}p4bwE<*N0{B~hRp`M6N-y3NY#iV}>cimCkb!cB;SssT@%pJ& zAgRM4DFeGO?VaZI{#^tSld8yeYBcAjehzxHtG7}9kD8MgmdyRD$fr)2jXQTF?421{ z_WBkFG8)lz;F90pt5Gt^aLw291jnvL62#Rk}qOG5D{f0jkOqjE}PSAMQWve7OBg4Iu1n{#DoCbYQ-i z;SFd6AOjbDeh4l|8a^%ohw@C$G|`OUe~fWLGPdFL^CNmcrIPZ&is!|as9@AC=U6hV zk%yHJDeJf4z!Wbf!P=w0)^RsuY=!Qn@ny$KKeeAvu1X)&Jk}`fG#ilFt^P*yQpBZM zzr7HSNNcjo#=s)Q{KS!5c?x`|X?WHwi6TXhR&G?wdE%&bW~=X3+LiXF>!(~F-|5{v z(Y_Am4(U+qLk1s!kZ63cp{=k;ovLkNw&Z>FwcyrC>8tRTwSha9J*OTqj+*!4 z5PI}|GCc<*v(S9WUu!SO*&*ci_hi@vK%h4Io97x`nk8B|Z zN^;S19S!Yg@D^(YmJ_+m6SgQjniCoA2I`!D9T)Pn6}c^^)} z?ESBgRu{#&tWcud3F|z2f99DqcS0-<^4jx!`xHsKM(@l&UeYn1F z_jSsg&tt7-rhkGh1~#H24yTB%H{Wi6SpcLOGM`v1N2`0pb~4#k@KQaG`G)?*7RY>e%jGk;%B|cT z&BP;`sr=+o7$Z!oN+mUE>RF{BW2A+K+Gw@MEw{vqDuaDaQ<-#!XUHeG8pQh$T7Px9 z`wi3$N%=*nB@dB|G7YD|POKN5qQIs!F;kj-b5|cW0|<&*z6gSHn6a3#4!65<_Llfp zae0sFhYRZ>U{GV9$JISFvUkH?E%`O*t@hrv=Z~<|$HH*KpYQ11x}ZqHdfJ+FG}Y+H z$|=)uHBO6iZ6h@82!EFM+3xz8)jZUL%a81qM|zed>ck*gP(bX0_v~b5p(qPZ-A@wK z1p;Je4Rud3-CGt7>?V{fW3%f9f*J;b)<#vld|%LS8qG&5-EH3dCi)sF+EHB23EQT& ze~Sjq>#JHm|FIScwicmv0O&IlAoc{H?mfT|pePmoJ?5N>vra(RmE9;k3B?GjxhS~) z0~ zbvn^!!YO~VHK$F;8UP#Nb{DUf;@%{p+LqBiK(gnT$9^4E%7FiQKNqRUlxm}nV_;DZUe$3Cu0$CfLc zijZcCWYG(}x>&eq4DS|CPCSwX2Q_V9RX1AJ>=x~-)gnjs@8AUqA~K*N-p4l;hoK6% zt4kQH#7RS~hbmeMz!uYJHp-6*`Qomj-ne`0s+`im$66fH;ft&(Sq{s5xH?$n5!k(#=|p#|OC)mC z4bsKh{W^A6K`POixl0n_|LYjhh7C~GIv_0AiM!)OIoyIr(DBh@6$1H|wRewHjUo0O zkErX>pos&fsT;xs@cza~XFBScq{idXyJD8DmJcml(IvWz@eBHt2>=s#lEzmg!^>TB zmgTX}_Wn^^8Sg9qX4>L^g&cSl?%eKkZ3sB32Tf%jfJahDEsyNFVPoN%Kl3u1r&_%R zCF}a!7h64D4daS#Kpn#2Sb$7O&Tj7a#3MPGLT{b!aF=FEZ94#7cN--|HNlAX`8#{H zppx4@4Ty{t{*98F<5+HGGy8LsBx&!=RLqRMyP~N?0-)<@HpIAV_`@2Tln!kZC`uiZ)gC~I%MNT(4obEy<)GL*9q`4 z^uYh1Hh)Hy_zSA@14zU0jH<8u+pSbG{-=D~3c0r6aNu&NYYg_itZL`yG8L$}JBh*R zW$o+R@0P0!L%Do^kAvg!sO261BTk4#-15oO4W3^91Hi}G^j+)8i!7+XKt$}4_- z5rJ%!0NaxbIK{rF>+|6l9X*WXF|?Yb0{ilgiva8oKlTcD%C>L@k2)){d|9a!hK)w>wVq# z>-Bs-9ydVix_XOMH(Ot|b;c;zpkJ=$TrVCTirJsGgu2*yW_WaSxBP~V`~XIM;H=D3 z7iWIILz3zzNTe9)hR6pKpR#|ul64wdRPUfrah#*RC9LJ*;1mq{y$n4aL-*g&KoTs} zwa)fx+Gy>r@jmj)-B-VJ<%Wbb?i5fz^EQ3b#~)Yxa;@;JO}W0I(wO`61s=D&|2->g ztv}T++8I0~aai#px9ZWI)bFzY_$bHxQY%-dHCHnP)RXcDILmh{*3*tLa~KK z2W*uqIM!UZ{i_#e_k#?@fDBtCfVRsr1zWrCN5*}?mjC%^yjSen+bnsfqr93^l^Dmj zqy7IzaY8%Wub4{WbRry*=zv?uxU}=$Zoetd6B?;6DwpgiZ#$3J+qNX%01UWXZ^fwG zs{t8besYJo_x#z1S(0)?_RgZ~)luBNZpt_;>!Cf`{(GtO{*iP(ioutCLCd4{(#@M& z^i*1ZK4V)5y8SZ+L!aC)1NQBNb^q(Vl<(d%4G;+X4@oxLx8SzYJ*lyUC+tS>XBxEa z7PC$DOx(}K9ML0CPsz*}YWn(H=rA}of9^qs)0N|-n zsb>5_Trup;N~?a$&c}J33g}dBFkx#I$9j`R;BG=YZ98K2IXP_Uq9E%k0$?m(mBk4C zMJmna)=80M23~jDQ}64LdTi@6cG!0BNy9om{_I?P&sO>33+Tgm52N_iYF!4x3$M~C zzRK>F?+miB9f~bTMJ6zn$MCaP+8vXVVi&9%C#ek@db3|*82uk@dy_SV4OIS{oes3B zUsr7{3;XHCRR4W@580nmtlit)%D_h-INuYvqU?N6)DEtI;I-yGMZ*S)nkV<1O%z|R z|9k!6vnLOJ$Ep8#qT7O}y@h7JlJPohDlTT5G~D|8`<5*Z*2&E~NE4)!z!So5)M&Tt zjf1iLLce>cymtTFitg>fMz^+KklUUHzMs6rn;4hq7@Y)~l3=}_C5~^F!*ES=wzb4>_yuK#izrZf{pz$X z@{0>`#|K=$3v@rJ&?yiS%|L!13fvCN{&1k;1XH>5&{2@Urkf#4Qr>!@_vDUY$&U~I zrM+8n2=|k_a)YLdIi4v&*a0}&&f3HHALmac`D_rrnn-uH#L^x zf*HH4d^!(h$r}1fee?B7YJ;ZITstXRQW^~@YYrbAk)kV-tA7zXFwlV=82ZLcY66aoT+p#~y*YNF&=OIo%r6BLhetBwZp3s9dF<=4uhROhD z5iX_hSN70KIsG@ZGa~WsVZHX&m3Q)o>jXKVE8C7}>#$J;JI)mCWqeq8E;%=f0z_~5 zKV7f|`wy<5iY@d5#fep>1Q!)cwKJG;1BxQ@*|g=qJMe)Rj?-3XRk$@AB`D#YW3tuw=1wQ)uzdxX>xb_aD_t7PJ9@~f z1RHWCHvjL^uh$LgCRN6GA<=qto6aBxk6|G(QUXF-!oxQyto*06E z+Zyf2h3DvF_@zFB=;<0GrdR2!YJ$?Mv(!Z>Y2Ugxest?qvAX9j>(}yo~poL0K$vp_2<1|*G5w+7&4g9Y8J6IXd$A<-^{&_cH z)wkOGh?n)J)!k?`;({I!=G+z%TiEmk<#!j3 zh&G)t7w9nc{P+(Mt;#Wf%E=Fmel(@u+2de-WS!U5XrW|KW(+LBWWg?C!*K#m9a!wfjg#GtaFNB!@MT9Q$F4FrES7b&`W%#k(aUGvZl3UN(-w5^vbWdnE@k)Wu zdQ&p&Fsn#Ya{dqD8s9bu+;WmNz{ zmfYzV#?=lp01jU-2QV&b(ZlDzIi}LQPnX=`%&LuA4n);cqASnr&WsCC@Dq&_aolz-H$o-;R z?sqOEZc(Zh>mix92$9P6AUE;06~~I$B&T#tk!XuBioOO0!z!7^8#U9lE(9W3m1uWQrthD!yTnWWY8d8xPs0ixjy!G65;H-Y#<-^$1 zN&qJEIPR=fa#V51D+?kDe@3#bmDT)JY@-kh>u+O0xBpqvklgWT^B+^86NfbLA1n$v z6U0wA*Gqib(ocD^@$UR7x^fHhF z;!n`Js>k9Todg(U7Ag0ony$M#JxV?+Q%zC5+RkIT?C7RY?o8+Ye7vHq^5NbuK@ODl zBH0I6=S5)C!Lbi&f^M18bf?zeHvkDSb1;+MDDS`oO;nVaWAj~q@>bp}IODkh1DGpL zT{V+Ey$aF5FV92Ff}MiBMXC%26Atl{cUh${I_ZUx4B%CBx~i%vkK;3O8LA@OBWH@J zR4R2Ub-{vOSijaWDUHoC;>8(kf2=iSi%*vp@46ppI-vctv%lY0@U^K59LX;HU)eQgYWz@B902ZEWbFI7Ebt0XI zc#%Qe{&9sKTzB=5)HxQvPy)H=1ZxJk;!zYh={}lcTxVc|!bN_UPcQ%QjZRoBoRZTW z=~(BXI`0hnDcn*yS@_!4G5KNnal1GR3y_q;8_^{-@b$;r5?nCHQkd3bPLe5H!fduS zwCyA#@uoUnIi0AgE;SYcu_gADlqR$EkKsbvt|6p_Dt8R;9KIzIZS$An-+RanACsyvhMrC+T-#I-TJS|G5IiITo0q?O z(frF7dUU*!Ye{~$2ks?*crI{GZC~K7Adg=?wT=oxLxM+a;ChvXxsAb0jBai;rntkk zI11yKs=&GXX~`l5^=ebZd7gwtU4QJc_ZK{G2S|0EmwW3I;B(nzFF`Hmr>|}drno#; zfo4|LZdEf^!5@Dp9sACQ+|g4nBy;L4o1Dp9m&!R(F(oPKZ6Y6dFn4BQgnLiP*97IV zcHijz%DKQSPjE8n9Qg2cy~rClZ}NlkM$o=Sjoz$;YeuZTD^=lUe_K3miIyAE*J{;- zo^{W5$l+=2sgvb`0C$WNj<}4Mdwp+skj5m|=%+X{jY(Dpt1X}uMxZ`gFO;OmWB`3l zVHi`GG+=j&_<)MVHbXSX7oFDapN4*<-(BeleyFzl5tO(dDBTRQ;)2-a(CQFx>oMfIcLJRG+@7AH^rS28QWe<+O;1v2}Yt00nTmJ8d`Sj6G2TX z(zG)J$xiZdkH9<8GNu`7EuT4W<>DrzgKhr9yGL{XB+M|nkB@D%u#zuMpk*Ghq$VEJoF*$V5k$KN(`Rn z`Fs9Id+Xvb3Czi^ED%SehI z@oa4KTqJFuZMDf*(zzFu97$k8x@}UU!~f#lxY0H$=Ev79j{jk9UleN@F_h>nHhavs zyH?swb8VJD*xo=bpIJLB$X<^G*L#Q!TZ9~~jEtw@E97Nob=8Q}y*AE)HXwN%A5=Fv*_BT1E0pkfqarZ;;|-D#ojHC2R=6=U#F+IlO} zDkcQ)|14d;PXN$br7L|8bcg08OuEFhqh~Su)HT)7csWw9TGF+eV~R2dp48-A(@1-3 zk)q`uMan$cUwiyQZRrt-hAjpmPNEoH#~3_sPp``xPpmGiPsi2g=-1Z*V8ut_6-VJ) z7{TDyA~mu5?t}X;34!&v2EU~&9rJL9ML-d4YrloqpG!FKrNM~n`L-=7*>wBpBhNnV z9m9G)H>w*em$F5xfTY-_UsTP0yc_)+`0fRbq8RPLuNYU+vEg=|x`46~h^{b0_nq~b zol&ZK3ZQ%EB0m&{;VZ%jJXH`@{@uFj7ROF@^OOoYS4H>*6vM};H!I88z#?5t6LpT& zQ=}%=q!be!S8{iCMWZMpv=xswZH76Luq}YU#T``gp-2+OTXg7^kD@_3^mN2GgKf7V zNLRyKm4?VZ%9uFUF(G&Rn{EeBwzCI&6WzA+2Q?)M%C0+iquPY>tLcb2RiQO;1fP{atTd_?~^gy3_;!a;em)ZPYkw zaooSBeq~(w5q6~x04+O*#($ADSvQw! zMv*DtKwk4XI#9|Rp@VCAGD4CrhDsUuONej&xSA_>EcZt%#dtf_VIWt{n`-uhinC{L z&EKqQ+0F<>T9iVS=`9@+GDd7Px(+sVf@V^{@vFppc-I&!^PsW3nCgAI=YmhX*_Uxh zIz6zUTA0~JDomBEv)>f;zM4ue1g0(&4&vP|mw{FLW4h%{^q}Oi>E7?iI<4d;&qbq$^G)R3?Hx}v{>*?L1(*0zhD`i* zj^deh0XoB;d-4S38=MI5N#`e9NGI9L;VqcX!(M|A_j5$CBfkUNnP+#T6LcR58kErdzz0eOfS6)% z>el(y@&CU0fG2&Y1rY~&Z=cI|j+P)rulOK*;e2`3R(~Mdbi&YPKM71`+lR3|2Piv> zNDhjMp-iX*Db_=A?}cV{@Has=QF6EZ`4jsrq*E;7!xD=JYO{`x9-&D#;KsZ<>-M}K z-Pu`}xK<}qKF$d1uqf}3|2;;Xo~@mlEe@UKNP_nj&zf8w&j%peg(1ENv(7$2G(UMz zsa@Q}N4&p?k0KpA(E=>u!e?S&Y=g!tPm(TZUF@GY=J`3t!a{5GwZ`b->{O7-tGC$N z>z97MYVsnubx^V!w1mUg8au|Yv&yz3b7e{XPJcsG^#GUmP6D>fZ{NoO-j$$m5oGo2 z_JqwvomD~#GA-~bO8&b0RO<%n&Yy-mRupuu#~{a{j=QD&`PAJ4NGMNAyE;SlaDc6=rX|ZF$})kOzdOAb z?3GlvJ84wu@A=cjvF*uYgugR7mm#mVPGae7J^*Zr76Zy=EpPw}!1!(yIAo!pqay7E z0AEBu@Q)aGywT@%qmFPKQOm2REx5P4hhESj?>(M*my!-{Modn2 zc)HNM7nao3akgXQo{8o=&O&TY3N&YGA_^Rib8xozDcPOp?%dAzZn?du#4Py->qL?> zyjM#b>koHqmhA&p-=%^>yuhEB%MCL(Ix@oNEaV@5*#)+`13c=or)EbALpGcSBVd3! z72p=TN$eW@k-_wLJI8OgrI390eIjrvflSVDP+)G$Bthb_(MqPbv{%56{AX*IB+gON z;RY>^e2V82%A=lW)HrFPhe9YatYUPJw&YGZ$J`;r>~TU_t{84Q+_!Rjul!o_Ax-gF ziXy!C;0d0g&TR+`0N|qGbUDqIr{N;udHLfnw@=S_b&h)Ze6;I?{M`I|USc$o7eu>j(MQ})U^$-IX*CLe`Auz(n|w_OCDS*Z57 z?!L!FR#%Twn(X zR7fLcPq-i?WhB3w3-5v=--;v}aZj6%SF}k% zk?7Ehhp<4j47PzB^cwxRnOJ^PK&3p>SpokDm(+}V_TTRxw>D*4{tJ)R&3}92<4xVk zN_|}gREEOXBIWOBuDJ@|I*d9PNg~@AZJMIG_)yPPNqhI6Khw`?f`fa}U^ND^3IM1X zVWhUg8vw9$ai$8rE={Gsynd9iHp39rolO{H6#ZvhI=d@*{PxRFoCYya{XdDw@eJH6 zYjpkhd;Lh`2O$e}5y<)!gL+*a+?_wJEJ1Z+6M4u>d|^lHo{k2R~KD9HIM0T5i=L9jO1iuMyA}$O`05z z;mxA!iaW58(`I?Q&#Ac>7ZEF+$9xO*NQ_LXme*$0X&ly+CLk0QsRSm7Y+PDr0vr&N zb%{N3-qMXq&@39=ZG6_6>hK5c1ZRgT?t7ZO=Y7=lL&B|zWs%4koCmKTxX>Mkl+(Gz z*!&k1&=Sz*t|o7gD*xa62fh3DTaL7XPCl@xGO*CL)G00m`+PMui&{J@BbVia6)ojW zy-(614yFY(%qBce4Z9SKdn%=^vq?4>pnu$Jv?J@E-mUYK8@F;EpK&>2e7f#y(&H=d z@zx)nntmJ|JE^g2`XMjp%{lIsnimg$=R`yV9sG9xq{jM60S26pM7CJH2#Lj@x0LG@GbVWk1TM^CAbRza->mw-CO*|<{8*-X-u_#CVfjG~up6anKeY;IO;N?0ZM>?JNksn*|Cp@HOI9$FImh5RnXyo6?GjapsOtXgD) z9yCnTSCA|3ywzYrI}^P)>L>i2IOh{^&h=9__G_}u8cbWePG(Lyu_Mw8Te#LLR3K62 zgdV}zS-ASnz(HD)Sxai*Z(a=Ain5+piRlDRV+DWSy*EjfUP|xwxwqj|6{T7{$#r{* zoi|XCFP+qbW$KNypJvltqUGvL8HmS`jq_F-9@oWvkGi6q7jsTn_*%aSdvLk`SxY@b zc6C2fdgyIJ|DbI{Bl5onlP-h*_HnOk{-+WB??U|W_t|yL9eT4BBdhx_3S`tSY6xCS zb=nTLDcRZpT@V_;Czw&`eU!yy)ZQCw<&n}D?wDk%=^_l9LF;D8(ZglkBE{LnI_TveRu@Y--`{;txZr#u|DLzKq>mrg@vm6)*dQv}|>s z)GUq05GaOtS;jzqW>YNl>M{CYkKm5(hyxm+V z#@#k8Ry@b|{<2@;R+1S2@;!IdB;i%Af3iTLm&lShJe$|I^{GUdcMdwSPOKA(rJjgE zO8=rwd_tBY_w&it15^;PiUzD$^Vhogtt7%>Nv(xOayXn>rv8tX4=HqaKz#>?K}GSO3!8?SErbj{bqGf6k8D;a?6qe#ZK2$KTxY|DxJ7Zi*!ih?f*=;N#l* zM_6>gCkV%WOe$5>LJtH$mD2^NWRiq@AVWHzHY0_iGL;iO0NC4&9LP0Kfq(&0BZTL( zGOTY3XfT!AqCDanLrOq&QMyejXR!OX^P|<{-m$mKKPWUUECX|M$E~BHpf=rQ0Xyl8 z4>nQlppSaHe0h8R`!}ci{G;@)Qy0f%dJh?@M7Tj^6MV!#~1r7l?8xRO;VmXy984WJBhS=Qo{Kug}V@>)4C?khJUlMhD1xAtG8 zkT_IK#~3n`>VytilZeI$tZCmHDyvz_y#$Dw7`;=bF6umn>xpSVm%6XI8NS5!vfx*(*-&bLI9l9AqVU5Z{PXE51bsr{-j@` zd*tcsKc6lb6FHr3 z8l87f9;I`YYT**s-r@jq2yx2G(@ys0h}_7pAlNIO;7Cwg3wFnkVb*AQ-o-YEWMYf+ zszOA*7aOl8c(jKDvJ7oiFsJNdRuGAhY#|zU9BQEtx4dB z*ds+AFs4V>^;m2IM#Z$8kX4&&pMpmnR~bcy&@DUzO1 zL9-<2Q3}bXhi)p84g`tr6p_?$Eax_^jyi6W?78e-u)*XKT?HCQm5N;sP53utA^)jo z6U)ppCi_CsK_5qdg!BpyEhoI!GyVly@slCjy9JZRQcl4|RoeB!30l|fkxT!el1TBy z-VA&KG2|3j`AEhaxy!2_?FwJd?)r+E-*PhS1M-ws>*T{vno&n@7WF0`+E0!LRfJM` zMcYdEC&Y~J*4Srx_Tdhts1?||KX&$as3{pm{b6$_ChZ&_+)Mi5qW(pA*x{S)uB?Ad z_0_LVikq1d4!oJ7?GYo-Rur2GN0@gclRpl=`6N2Y_{xZq^hA*DRR2M7bU9n5q^UMm zukj0kw6@rW`q}8)Q$YxY=*>WULm->J<+=H)rwgB*FJ1y9-)46|C2g`e+XqfrCCXKT z2koeAqy_r9`MK10DL6pkr?`wk*10QOr&;&24>v_DXPz*aw@Xo?R$on+ttz?Q(sy|!_> z26v{myozKBMy?WWoBcun>hn-55y!I* zN)$87RZUGrY=|e?Jz$H`xnq~*jtBO`niO_CkAW;0-u(>wlp-~|W)++P4mBvPyioVV zqcrpZw%}DughSu#XRFNu&$4H~^Ph0b7Fv~e+gmNiNJket((Xuq5=wuRJNc!2Vy8-8 zqK{1C2k zkrxXGOL@R*ykXW@Pgm?zrg_!QghErShAs`LFA{*(1fYcLsI?*jfG80H2m8ZYT}CRf zEF~;RJx0N&+lg?)PKqa}7kaLQ78s&>BG(zl3Ars?raDe>+04aadT?74i|9Q%`V5p- zcfq~>LXjA^`7pfIWbOLrRq40$qkGbj+tt7ZV%Q}@iOU#d4-cxS58Qg-VV=dF%=}O> zOqvJkJPP*n0FxMOO9n(U6s@NxtIdSCd4qjv;0NA=R7(K3ye=5k7wphi=G3`RXBDni z|Dv&v=F8eEd1l=&h)otCcB!Mc4$JMj zs;4#ytlXESgYUTSVGrUYL;rqmn)%63BAhY-^6t9$C=-c43jy{&!OEm|*R4!~5M*m= zyNQSyTqtDd&~lwUZ#){b-O9sD-(>_60cg`ZEp1zI(5V7CtIw`un+vCA<4lKYZqU(! z75qriz|a9b5XNSRKnHE;vG)#osc>29H1liEysGRJ2?ZeOA;~ZG#X*C17&rv*|J2_v z;@0I4$~qoEMd5{kB$gQ*a)8giKE@Vah3?rXPF{s1mp9u5$cq<(7Ht?OZ5bk*Rp}HqT;6a8#PKOWy3BJT@SxQK&-aB- znSPJe^SaVUnO3_EmEUfGG8tvhM13nUK_(;8G>86oBLAK9GP}hxXDi$`Lpvn43~U+P z{77M&1Vv};?X{?^TfSM78|3?OQdbz+CL8bEV>y#O-9YS7fFvKy1ImsyleBGB-2y7z z0(!}+Q?3L2LW^xig9Ap0Z6y|`Iun`B8^C$sAT$Mp3UP8U%Y};}ej>Z z)yu6MdLG?b^*X$5v;&J&xD-BuuPeTktMJv)m?=pz5DoW*&QVyMLKfFQSbpV zP^gL;0idjeO3ABNa~L3XK09Z{G zJt@5+!hCUEID7cf!O}{g^3!nTNlEF)l=@MY;q@K9k(P#$5fLV9te2)`0Wprt15$HE zH|H*v-Ldj>{AC}#0ouI*G7f>|h$M1sU_>zl8_K>t$bR>^3#okmz^U!N7mdGVJjY?q zACkKt*~464(E0@uP60hgd>{G_HN*|@*p!bin&q0=>k+_0F6g8z*3MK!{$uY3T`K-? z6fU0(@}oF`e!c*9%N-vUUdNSJkD9u47daz<3OG8~yTN6gRiUPB8 z-cZ8P84eeIzH{VU_)zsm&1ty##!R&_#eP$tSE8%Q%Jy>E*f9zc>M9UHr(Ml3TxeuL zftRqrj97M0%HgIybfTyXyaDZg)_$!3cEFTu*=LdE6ySq~rSf67zvOk@wpcA6^VzBv z)3-0&^}Azc6YqLonaMT)xI_1Scqo(eNsH@VBj@Iu1C&NPSn7~emhe zKq;Bw?ocTxyifo3_-uky5wI+98e464_sJ#fQ;UbUIKF!3rQ^e;kBG_(5#uj2@4ifk zh-KGX{FAsFW+jqL+I%#$u)GGcC=QB#;J;f4-uo^8h&V5pa_e$)#i7P?-XUGyrq|x@ zuZYrbR5q)?3NT^%YG1!DYXBqhB$fmThUee+IQAhV&vJTU%p)mI_z33j0hWBfH8yYZ z8+bjI37IwSnmzIO?gLrr4IL>G^!m^?KLE&Lez!twu4001RTA&`Wz)(4!4bQ1vo!!Q z#y;7Pv3Phsa7}}-T2jBrsqg7HGdJH?bg99=#3@hirR#XUeZv5P*}*M&irDj( z*&8BxZvM7qXQ<3+&&|T3f$NfoA`3LIHRnZ$rZNX^*^UjLqQbdUB%Ri+-2P0yA%ARqh3s9u;gi^3%S4 zZ)oFPj#-e>m%{4eFk}nbeiSU(O$oKZNve1t!(Z)4BoV2-lnC{QeK?HWc$I)7Q80hG_&U|(VE7=WDaXBe8g>JKeG=7x)S z1cml+gRVON)eXfr>$|UxTj+m&S>95C@hu2Fx_!PS$Ua>>AZ7WPWjDgo+S@19!Rb_# zdd1y;`oB&=-+sdc&HBWM=-;d`8$T1%{QY&1;7kdh0(OQy`@pdZNwe)1?o1!a{LNhr z8)>=5QdFJMl}G|A@`1;F-Xf#QA)y)MR7E@;wHG zl4sMXr#tYi^*8jLKmnk$?|f3Byv6O9YjB^pRGl`2GgsYM-W-jd-LKKDLb={#f1Dr^ z$oMiEFSFy}?A?MY{Rre0bWO{AArCAW_Co=-2GCt&ZE?+S4p{7Pc>o=Fnz3rpT?A2w zTl(N`g%X(uDuEW|z`}fB5Ggb5Y8U60I|J@6;ReO-qM@`rCS^NPa(Xt^Y-nN@Le8fy zWK1}g?<$v?J+K}!GwHhBXMsyjTz|Z>*aW~!SXNB6Q{E3rX4&J!P=)n z7N*_IVBrSmNYjuVKO~0!5lu} zQTK*z&gCgOm#4m#Z98&UE%f-CvMqOxRfX(qSV6hS%ecC5?t)ytM}>MCNxjfzeQB!L zV&A`&QUw%-%porGOB4)p{?RB^A3Ta(i!8ajzPsn4)^Ze^WlnAahx62I2|QdS-i7%O zA^GRW*&M{5_WuDu*I8h_w(?P=Ndb?hEq5LeCFy6l%77C}Dz=p9sES;;pH0Om7F)If zVzEtEo#1+zBp7@Lxfz7i7m+w+mT0Cs?6#OWDgXE5q=x!!vfz$lJXy@hm!_@$k7W*E zRiiq}S=D&PnzJOGEf(b7YC}uOPjo~fb&{f^i;ebo0HN{=86$T=FARD7bcgBLH}?On zj@-32r_o^Od;|r%t#~#K+TY_DEqlpz58xC+t6N`=BC!d0od@bSPn!!2vvpMnp?U@xc=@=xD)@)| zaS7^+c$?;Hw2~067`8X=x2wh@+N`I^FrPMxKdzv7Pa8B(pO3MarjMZBKMnsc7K~aW zsdSv9hQ?JTwu}M1k`L#jx$E%_=p$+s6Qk|hW_ajx)#smKuAFnajd5Ro^pube*tbBR z*!DxHN!0uikul@@BM;>w4|2xq(RIPP=|ZlMFqMElb06FJc$==6GwEiYENXW`5?I`q zCqUn~;~qj|N#r`2NTRd0G~=$fHi7<;gsEQJDud{i2#O0S@parhni6IN>BkFU7pwD~ z;7Do)Ge*jCjs(HvehJBZ6tEdvUSuA!ndv4OzJV5Cx256VVF`GeETpX* zpvRP$)palnt%lmBvaB6Mg{7yD5*`<-9KZMZjo|{H-~#cjVWB&gNjeMe`K)fbn^@CfaQK4co;@oOPpR)wIpsqfhPxp6^*E6Xa$qm-6Rm~azYANXgfRdxO zZ@`*?^D*%fc7+1s)5EldP*?5x?H`+G7KL zwVz@$qHJ83_w3}-33i{`4TfU)fZBZCnrF}RQYWpaqSroiQ(h5%o-Zid-alH_+1}j+ zK{A}}xaGPIX?#f}c6_IsndHlAu97)#mmEryp&E!+I~8_jZ{2R7n^6%C#05v8okiXZ z&?Y>d2LMnG>6e-U0T^WsIb*&->4%G&zwZ=m2C^!_%j}llG1VL7p`Aa>oPeqW0PTf# z#-%Z~-u9!4UjJtJ-(BN$l$-e1h#Myny@3qnSV@>nos*;+C48mB&DslH7#tBT1)6G> ziV(U80gL8!_0xP@E>D8t79F%)I%adQyXfsoba)0NA+e%yDoj1POf07MO! z!)|k3+$_&vY=u-!i4IM0`!E;Hfvccx`X+ZK&@djF4a%mpC$1si?cYqrJZPBnh>OJm z;Q&zrqKaXQhLbLMxSmWPwG!FN z=%lx%3I;p|k5C1DfkmlOYxDtiWn+(rcL7rAW_hFxp^FiHgl%pa-plOim(B#@9r)H) zQkoSLqW3iLFX(LHbDPo^Vb+TLQuh$xp$~uD|9fFpl&Rm-|9!08(SU~?L5-Pg&h0zD ziq>PPPe>>6Myz!fMWr=k5PRm7%SVamJ>N`1wg8w^zaE3XPp-76ek6Lu#>wu802s;* zc>X31RKXRYy3|1$EZ?psAc3bSaU)O3S`2N<6i6wNyp75i1P9{KYVX7o4q0i1-h2}6 zR7Gx_pDBP;U{z3Q>m)FaQN|ONqFmVTM{ldB*q!U?qRsdlf*A5<{mC7OPt4J!Qt`OO6w`V zVp(MdcjW(ooC$QQ`|POQjwe8jlx3T0Z*lV#x|Lixx#-diNN`SJd}AwX()O_<3F1T=4GxB zZ$lIOYznl|@+r@e>;+xq<~jmhup=8#7|k?S8MR3VtZicuv{r?aofeG|>d(zGoTtOj_gsL@3ha@+wTFgMlF8-pOq#gmR)4yx7AG(oC+m zDYk}af0T$V=?NCGYI5o{bNjH~;nQZ)sU58-t?=D^tyYL_!Z%0*VsW1}_>s+T@4F{u z_+Xrd>2$`o{1$4GO8iywTtw&nn2)6+4p(^O@D^2~Tv=OdtJ<3}v2Dfqd#e4W5Eze- zNCr3?*fd*g0V=Lm9=fklc7FdI*%U`i$obb3YakU% zCR6-d9s3Ns|92mJeFXT#(1cFyjogGOymF2tkadvE-x}G!-RxSNWkLWT^9-l%haNIw zxPsV21|pUzAmJ>XAO%vyVUo5fDVf8Uq2Gi21Aq%apAvCk1_?O^XzKNQKhLr+Wi`}2 z*hzOmkeD`aJ=a2*Dt|^%_eKmTqlP2_flR^|j3BWSQp=pEK+;}7^H(h|bTCO)OJqnt^rKx#1M!%8(-lYPAMkDu9${%=+d+8a@N`v<3uKln-Eoj7uOYM8|96vSl3g=c&aHCMEEaubcjDC)Gl86UD*C21I8%g}!lD5UQtwTv>t$q)Hm7~$7a&2ObOIa`QOn(yJkO;x05$@Q>? zn&Iwz?(UzSjdF(JSxhM-pc0-Z+~TBwak15Zq+YTaC2WM6<>OKjLy{f785JwPg~3Q( zD)jMyFFV6|q|MScXLFM3K)5EvQtn zu(>GY9oa+P)vH^+CQI8rXwD_{`JVmHeS7C11!*&I|T}BCMw6O!bJnuikr9d9et z5s?p~9~Ufa(qgFi&;Ih!bE$ zaRbAGFZ0<0QYTV@)`=d{8B#>1MhRmotD6L;T&i|}*P&{=U4h9#vGT;_45(N+(34~B zB;9tGy$t$yfv?P79XkVb>w2ba36ipL(&8f;v*n7-BdqD8A9}?gbUqBV+dwGRLSs+f z95L$v>N2F*@(?Fdb1s71a-#{L<@9CYL5q0v)P0@iFCrkZPN3qi#|tF?q-hsjNW6F2 zYnSEaF7AD3vdg`x*TtOq@UFPvO1%e#^T6IJSIQpMuvgx%CAG^h_fSTBZIoe7p$my3 zs)zS8sq?2W`p=rj^=JakOqbSv5Y}OW5BE{=$=la(aS1=VsL~auRw5f4wEXc&GJvYG zCfHpc6c$v0c^j&dJT097YCi6B_DQu435C7EeD_o54*XH;+jn;|@+a46a2qEiqv77_ z3_$#CmKZKrI2EK?6oh{v%V@cg1p&sxSyr=Egh21CO7F(BTgT0{bv1M)3heg=7r z<;AM@I%&?4wqkh`j~EkY1j_OtU2Lw^SEucLo{b#{on9QWpJr`H(h)Mi@*_w(!)PPH z?{nL38`*^QB%tIk>>PI3t(IdWZ#PZ07t zy#n-6u4e&lF5|>;u}ID(ziyJ@?((ww_-|Fs{l=I}+L-LIb0wkj3gPTWS+6&yC6#L8 zTs>2|GO@LvH}CsdFdwCwQ3{{KZUlzCBdgYIqZ|e9@)ZThaUi7}rMlehbQeQ5s6H-F zhx>%T;oac~NFtOh?TL(2R8L?i#AU|w$uG`HqR^*T&=o83oh!*DpArl!Qs-b;NZtDu zYTHMB<)E;0%%|BlOyU?7d&6DlTK<3kx?;O034xQJW~p-WKsW`Mvms&o*}QA3X3*H$ zf7swr!da7#9s{p{n|E7{EB=q7bMa@g|Nr>4^T9U5FsE#DnnTR_5ZN5(e3nz12uT{L zbokb0!yKDq&dvFdx)D;T=8%w_Qt5b)N>bf7oC&C5a4Kc zC%)MJ{Y6^rV9`+ky}CkWrPB=PDB(BQ-zda@tCZ6g-EtxQ{*90J8!P$)5Zg4z6N!!_ z=EAxvIM70x@wTIW`_915H+HH{YlDbpP6}3C(oTGoCzNMurJPQ5qoq}b1y&}EDC{}> z#Ns7UPmOq!vA4TqTG!kobkp};YL{5ceJ|(P>PUfe4Va2oNCZSV>&5E?pDqIV2H#H_ z=5M?7D&J9u)^66}flUq~JG#6MgUbfi;#vFekRoFQ^{>rkDChJNRA(U_+HsTcgY==% z)E$v1P#puKJ-vE(BYhQ>#eP%J*qg_a`ldc&tScA7`yMfk!-_< zCYhtemdP=I{_JmQCxtYFp;;>U*y9w^$){SIwHQ{g90$PSY3HndY}B>9IM-)%;qlpD zVc6&sB|EKurrlAQYnR@BIxGlE!g-On1J(mPb5<_VWarD-(ruZY3=x7El zmlxdpGLt*&k^_i1vSktVm=fH=Lq0c^^AMEIO9zE;K)+IXKRFvso1wcJ&x zQFU)(c^CMR(ABz_e`c-Nt82YQN11LeS~@0_#Z4eaS?eHkHxuGN&MTxi*QSZw#(@G3 zGe`A1Q1L)`5MbQ+a`aw}$RT}4)Ya%Lp~kKuKDl>etZ-L#*s<66wgAn`=dDtg*yeM4 zy7rqUr$xhK#NaXEca>x{>1)L9 zXebKlX-K->9Rr1-JwrX4T;lZtZ*&U$ZP2P#ZH!iJ$Ph%yiqiL{b0@_$mrPN0!s}2R z3XHd`BlFw}haAxe@5n&+^`Swj_he!tVDWYt|X$-$7C(Lesjyw94&?C&YjzM!;~i#dL@v zX%4F46*8BlQ86j@s9xc84NrJFnmq;m~*XyP%A+>WCA zx;OK#-lymqf8p48(L^5Pe8QLXaBW(=r^zgVN;d9dNBbHO^~R)(HUt|bTdradLgnD8 zLJf^VvqFvhY6q-aLjj5DrX^&{)@v8;pbs@7_?%;!7vtyN;EFizj>S!8j2uGaKfdl& zYD}`T%3<_l?uWvTq16NfT*nfr_|WCMkI_mLdK2F7g)+wQ_Sa{=@LPYxEF)6K zN-*41)e3Sx(CX;N?lI-lo>#|zJTl;d6~YtIl;(1ZnewTS{@hFz?bqfe^#G%f;?$p( zXWWO>6lz-(u%t{!OdF!tbMQ$RnX`jCqo1oXxi|3icph9{*}6f-mY5bL6_zsAWc^;R zO;d5f7P*-9CMCH(!!>XA-`zh`yPOrH(l|@2=el+4_{@$OI%Er~7%EmkZB>kX3=N5T zs~&AqSck&Ib`ECh)B?sMnzeWUX5%nmoQ?LSFD8M$TKsY(L{<+Lj53Ue^9&||q6unM zM~a*F8DY1Fx&WFoCS_>U?PhtwRty+ju>B0EKg*l*gm|C$4b0#FSj~Ja-_NRg_1l)D z3;=llG$02P2?B7$SDPq9{GXGsYS*J+Bkhp0>8m=z+oz1Yvk8h3+C%pgX}Ig)M|u8g zBJG&KiFgjk-+zk_@_n}QzwhSVTii`A3QD$_Iqbvt6@$^vA)^j=@xFx}F<3?@GZac> zts^fB?&=4xyYK1gz5G|q681R_0#_xW%pIL^_4d@e-^NlwqpNL1m{I7rBBo`J<6 z#tR=!Vvcs@n-AuNGoPlM<*31(xI3*54bMc9!0$x$vIAB)H8om~`vBeg2o-epBTv0O zs^0$Tf->O`C_~mjf=!I)YHF}D67whyIEUw&k?T1pRw#1+CZx+!+37!?j4-%cCi)0%xUo_g)F9CIbx126rUL(x=^C=8A6fA@gi@F-RRH7fGl)`g;tuuCI~%dsi`N%= z1xkO&JB>u3=&$mfU|`+Yr!+6RS$TwvPDF#gOb+&kLHNd92;7HsLKD&X5rGlS9ZQEX zzj~0&c$q|+V}ujZ8Y4{Ra~DF0gMAqi;4>^E4FsXHRduLYSPeC>kpeN3%&(L6CT?#Z z$vkg5SNL-g^k^%!ZX2G-Lu^sG@g8E(K)eXGbB3;+xF|w;Evf&z>8kU$F&Z}}Mtg;< zxWLz-@^<749UV>CA%uv5%L1GZx=`<gn>o)dS&t5J_bxwHsIUx@G)_k1# z+|IwqVb3mr^W-Q%qbsoYXvd(l2WHonYdL!c-b8Uv_DcO_b5YS}@z$3+TQ7H1HeMKb z<3%DrRY7Xu^I+*|AjQc3dAs>K+(A_^o+D`eVUcJ5txfYR^6kYf`chGG4}Vzk7|I06 zlp~Yay7x?U-3t%Q#n?H9PKUgwJoaT=_SDzkq3VI?Dy`dRQOD0;Ifyo%8M^iH04xyb8ApNaAK}?bx*}28OU$&SR2R%NbaeUO&oaiO(B=@+h=vWTBm7?^>z5CTR zvi3PJRNGLPp*`|pTMq|Ms)~Y>Vi@o{{0QRR{tRuTj-@i2o|G;J%FicT_05(Otyy_L zUbtd!;LXoDsEA~(iVhtS)RjDalf9Awhnv+%dG2j&L}{ERP@2g^+vSGQJ<#H^pE3;-)`h2TsExnY(2=@L5|I zm;5)^GHoV0;nsADmWf}fyZinkIT8=N`dZ!H`}6Jmk;XUO6jW~3T|Fi-m*7y?1yKp< zD73-9!^gDAPk_rhS#R|pE=>fD)L)Jt!9#TMFrWXmI~*S;Pq4r9_Q9{l_u%)Vzb=sv z)Vc$IIUX~KI-(c!{Sk4hQGk zS3<2*BCDA1?9{4kvtUOGF1S0ot66V9kq>iKgS^cHmSLGGg0=HMZvgN@;_4p zADvpxfoi9gLOG|OHthIjAp)|t{lW<#k)DCVZs7G-o%%pWUFRk zZjj7vIToFQ7%}sT1vHQ3nzHISJt;Q)JhisG`me$R4Epfws^07t$rX-&aQO-towum6 zIxI|N5cyGs-uZ|hkfK-4G6y)2)WzQYpVZ?&scAE?CZH!JoTRU#@TB*s)!4Ggk^W!s zBP`A9E0Uz556&yVvs(RWt4yyBr#lg@C-aWjy z{RgBDZCCk7AO6^){Rg2b9Mn?HxwZoj4tlG-=8D3_>k28OLM`aQe~_vkD#Sx1wN}`W zC`3VkJ&uR4>cS$|U^kc$zV6>Hn-&sj_OpK1sYk=phn2cSy zKa`~PyD6)dZ5sRe<5+=U?jHfp<hZ32z?5qz&-?`p&my_ z^{F-%$1<=<9*`1o(pPx@>S&T4&@cE@8u`Y>8!GRpDIX<)8iFv}RtPK5THoNaj`3q> zTNLu46>q#!yXkI4BS+8prRFsf0$YyFA9|Uo`O`n{XW6}DWqhxsEX08i4DATYI5#KZ z{6mLaHw_Ujg%lh>lsJ<;+*Qi8LWelmN{XG1F2*;CWIc!`eQu6iYi79{uolDW>%vFI zP)>^&G)P|baU4Eu9E7~a@r9d-@ZAuolL&c((?%Nppx0v()_q?*`LQQaa68eoGv`tf zGAb=a>hF)RYSvtthL!K4YS|ST_8M&JsLbQH7{v#e`?Q(m2@Gv+??$3ZJxGBg!P1dC z>ld*LpF^!UDrsVA<3*Kuz~05H@W*f=p$$5MpFB%$rwZfNP2HwU~TF7XD2hX#$#Ca4UIyZ5*$hV`{OgCbxJo+usnE83BHR5Vhz;27eMA0+A@+vty7 zd0H5F5thJJEO^`X*ff;Tsyif+UNBkF`}|IDCG6Cc!3sqKlsf(fjchQ`OxC})GK5N@ zLgKgULnTPVVTUiJ4n~BJBRK6Ep<^}`WUIjn>=yK`qAOaZ0(r32AZRtPyIPe0OeCZC z6*I*FmBV9fmJOLHd$+z)3^9lIo=Ba3CAknkgYMrj2!3po8K^I!V{130-#aS0L?zIV zxHI%+o^&e)_Q|b)iuX(PjSwDda~~&iAvX@7e$Bz5^dr?}Wo-TtLW{G*$U?}7i+2p1 zN(Zc#wilAJ9jwfhuIIj4)g$k3-hieO%ufz|@Oe00*?0N)0IE`~YgRe}XtRzj=fC_q zf>^?uR_IoL#MS~B@ltjzCrMB~amma;E52Ua zpYoO;6dZ}+j+ok-3kfc?@Y7nM`Ac0zi%2vTLjmkAINJ@2U5NJ+v-6nsv_(ZU8#f&31mg(AWaPjpxalpe!?723$hZr z&)iMQiZzM)8<(?e2&)F5!sS!XIXj;L=l0PF^wC+>%cwJKmmsDLfs3#P5%3I|fHHUk z*Z!%=f&8AhD29IbqDt)7-9!G0SKvMK9E6pXO3~N7r|RM^b08u*@pfRJuT`J6lSAs@ zV>8wvr37G;WpqTEcJw_emb4j5XQ#F~d%uC^VV$=lFsB%;xSja0fK)@B7bWk(Z=`O; zCqF+`cr4THXlL&8D~FOQ{O&aVb!_6WLg9PG3oXyfHocm=yucl|k(gvi!QTYB)zRY9 zTSNz|4`~+_hE#Qj6eq+oS2&tL6xe4!d8Z5}#z0oEL{dA^ViW0nk8lbBT+6^6u ztF6PEv7~ZLz?DF6F!G5RU3G*Z^Jfq8C5$jbIq)|J-j&Md(sz^RR73>1c~b0&DEohg z?YkNFUn>v^?eLpg9*Sa_yBmz5!2cpXsJk#WV(3PTJx1JIFOXcq|2(p`7@8;+J-KYB ze3rEQdlptqpXeq@(H=c7Lmc;RG4;6zT{}|M_*Vrb=;&PjaaNayJ2$VT?brmAi9W_$ z_^X+BO6C2ucGW%%AB#n5Mz(^73~Ph*_3)tfw*w` zROqjDbyHv4;&ffJMd_k?P7y;!_JeJ%^F2a0cWO~-ilH)9hwowFtQhupDF$PuX)Vs- zCLo3QwF~q0_)aBx)rW>0no;efk-{Kam98XUwOK{ul%sKa8^#3{CMDOk{ySz;W_{+K zrDJbm7Cn_!Iu;eo17(Uqosc+-B9JFDf;|HoVf2Pcq~RelTQnBu;YrRtYLZ3Fs+gqo z*}x+x-(&Hlz6K~Z`mx)IsCnk6giaSC6nfwr<{FE;Q(n3LVfcR|^L;ci+I_s_%!4OM zx}~csN8-o7@yoq02OE<`LeO?XSG@Fj5^O+H1d*fw#p6S6FG5V+Rt_o12i;71c5~{z z>Azo7Z{NR_msN3wMSSwW_kS#f3-^@Xj@LN#wxJA9ujwoI>xdt}Kjm`FcZY$hzM|rO z&Lw-1Xa|5V0c69RclBQ7z>$~pVasLaML)Yb%o%c?mU?ANxW*-t&qNjICVmQZe>mf> z-YC6Hvz11eL3>6?6h3_OOP3l6x+cO#N-)!GM+>LjTTiuMA)@_3DgVp-7!yo7W zl?uxXL#IzuP5JTv+c|t1)(i;fu=+6w9e`#w=dW#}pcfJBwQCm{_nbP9n~VE%RcbEm zx{e@DsTJGHcgde($Xrap-bm-|A|@c+u)j3u6S9R4sGY$Nu%>m;?ZhZK#!v(LpXQIp zRMFQNLRL^4Sg2-PW5I^XCGh_GmgpqELQ=&s5Ms#*nL2Szq4KO-t?#{~CJ%k1XO*tI z-D;j55Hu3{&cE~xIQio-xe7N6>MTM`C5Cg&LDC~ zl5tekh6{WmDVZ%Uo};8%`;O+RhMw2L7(|9_g%ylzy$X5s1$p)WqUQ_J;X!+@K;G^14KwYu1!2kL;qz}z>WweI=wb3 z>>6EWNPwlVigp~S**ftE`r4QgwfgJT-WiA5(!h(~LK8%0DxGyufX}O=KRSzgPeLF~ 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%0D6hTsL?8D>nPVk|@6&s} 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§i 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

^x_R@Ymf3`b$4m@p+ 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

LJswA=@ngav8D`XKKZxfI#TExRB{ARhWz58Hu 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

UlLAF%$nuPB6@jH;rdc`M;c;pG-3mZHa5u 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

;#hA2Ql*%P+eGBNBZCmzCy1xVFzN8OFz z#n*{Pka+2JGJBH0a%lB!N54e%w?0Tv@?}j1ump3Z0HeWSBpq?52W3OY&U25w_$7E* z08SwbF0uF?wjtLJ2ynX{zT0S@i&yy__Xl|^Q=lDC4~a8eusonO{it9%!f$!}^|Ey< z=dISOb&Ci~G=H$zqmwzn)5%YR{mP@qAd;WXe7}Zd>G{f@H`c@A805w?{3iXRQ?Ay2 zF>H9w9g*Q2VgN|R{GvKtndoZc`dwRn|) zM{eeba?=S=Ez1Qn1X>Os>j6r;bEF?fYDBGizVbkt^Zo(&wQzQ>4Nne%SDk8D5`U8= zVgYfr3j7fS!rObg-Ym`GN%UoYXaoV;58#{PqMq;6Y=>D*%?KuPp~{}PlP{qNPa~Tf zaLpTG$_K52F0kb951bl?GcsMC*PAt8Fm5!p>$^9FmTvhDNb5#DEFE1&A}>d%Zd2z=sP{;w35 z;p5qodY2;ba;H3FEG;Ry@sjdKmYzi@!!ZV+^rX=*^@e&fKxukG?p7y(B`9aU* zKu<0}YtB~zs!T$-4m5e&_z7*u#8TlA0N7s$hMzz%ll26JqDyAb_4e@hRQI>!(_6gx zObb4LL+A+(`~<5wexMVl}D?7 z18>MAya0WFVME&%4Q^~(bay}Yb?bXhU1{_R52rx%U@}h0e>@n z&5$~IQ%1KMY;zxmq@1xMBdZ@C)~A0w8CnsY>AS53qBeoV34S#sK5?o^*?}ieb40$J zuPMhEHEY~N#F+*TpDd}G^ATxoPgSl`t2!eha`i`3xIvs`a6lfwf}N}+gZf9RUR6z7 z+2;bXmL&ZoEzX@JJJ3ql8~x|+Mc`jIQ3WhN8I823JNbQg8`77VX-4q_F!@~q5U!L~ zOHeGq+y!J&qljSe1S+w7ixjA0;bq7@lYz<18xt9Qp_66^Ec&Vpfi z4twrLGZ4!?{%7HMC#&eP*5U_#bjC3J&yGUDkdQPai@)c}s*RN}(m_^@6=Ij4aa5}v zj$6AMcf^)nP)Tf*XDQx;hTboE=Tbop8%Dv@F!EJ~z`?0p)u4k1DO(upUpG{IJC%DX zF#0&cvd}r;C^CAp1PAF@OyX&DQ|bG{ijBAtY3GK95p-8z;9i|!f~kxSZSGUcY16rc#&?<>{sFU=o-gl$|C)G?_d`#eEk=iqOg=!&QcfA0r#JTmVemZyF;u(>w;k8^&u>&&lwp`psnwH%#FJP3?R zV}H*%TDarMi6m7W(uUkVTd-c>EU%C*Vn7rUz`TOHXA$r%Zf0M@hzhy#4K26=(AXw6 z8+50%J`XLY)>uRe*;R6ML^*g?Yt*VY*MxoNm-h+Kk0?%#T~)s7pI14Sqg-OpkkiV} zi|*|1OK?HqCIXP1obKU|ZLQM{(VZ=$duz17hB<^Tha80TdGs2jDe|b&kD5b7&Dv)s ztA!Q_k4v1GHW=qJH(te?GC*|skJJNuBXNfj0b|K+4h~|m@s53Xx-PHYRf}i?tiOGKtrv}FAXqDL_-|SBf^_qTD*$>v!w?8syR;IQy-O|q*7wa1UwO3jf$hC) zfu*Id1vZHI`dkgfsLd&@q&^-!09U}NKysD{Ca*@3oqW+Ioq82gaAGFvYDCB`Hk^uQ zToFi_3HgL-sT`7$^6R^E;PJS`y>ScwigS*w<9UUK^Ks)^8c3iaknY>`y5Oiv+B1mlhw7!GJ z51WcfqX>hmRj!JA;dJ8*&7f$EZ4}O0z#YFz@R_h4OrSzd0Kt5JZxQ&}yrJF5r>38x z3JdCf$Ot#HLC^JXs@~GiGY!Emfgt{BZ=#r$#K&+OmWIN#xD*D|N%+ zF-z{EOO$+-n&1deIE}VZ&Hx&b0f@CKdPXzY1UrlpV7SLOk94V(;ec25ob=uT`3#h9 zs8CvJ3Gjnie#zYiz7lGYk_I5x%TJMLRn0KIG<>6bh#;ZIqbpK&kRe*@J}0Y?9see@ zaoNh!Rbd!!C~NY zY)_j&JrR4WuJ}V^-B8-4H-5wg68KcXXXX7L6Duv{-YW}N1!A)OtVN@>2 zq$U_FOBseCgh7Dd`aG@hEvPi72Oh)~5&S5{x7&1rN70h`obD#F$;z<{Gb~iZfu&ws z!$^IgBKM^mQeq}x438>h5dagU%oJj+QxLI?A+b(sQ8=1``P?V7-*2Od3sgS0t?6>? z+!$X3KtL=oVeX^)72Ba5F^wVC4uv zJJCbf4;F}ZJ!I{(kJ%hk37tNtgZDdg-Uv1OktzX zP(U%R(%A0KGHXc;RgfCtBj-mG=)$ZQoB7mSt00I$vEZ)*4@6d=;KCIE7-_?!w|f)B zGO1w0_khOnj8MUlPqSgm<4jx-kgpxx>SJfqOQ_F$@iBR~a9qD6!NXYr2eXaGzP zjRiu|UdP6yuydZf@}E~K9W&2-*^yK*Y*l-IQaph2~;18Fb_V@C4x|2PpA}B*fPQklM#V1_@MV8 zYa;l2V#^8J!g`mF&-MiInld;M~-fgc3}I^i~{IVxf;UmIqivh4~J;oCW}_)0K| zLXLWpu!?ZR!8FH#AMaXn&)=@T>AT0%I!ev(slxMVh+L&YumB+E>rV|c)05tdbrpB& zWP9F?He3AczgJ?LV|}>tx~B6I%bfrw?@roG*({^0(epAGk-<+U}2lpmg#r{e)WQFR>@ zL~?`b-{IT4*7;CBYGwuj#p*lAUjZQpxX5+_YMUOA3og2$rmM$?(aW}8I`Vh2WfB8< zN&;&T@6TWa#5b>cNN1@n(a@FphqLS2*&&k?AjwVI8oF}8LSdZ@tOw!h6EOb~hd0s+ zw|4}O_qBdy!@j}`2i6rd#fn5W>vc2>l{A5WFokWa8j@U$QX*TOrjS|p>D9eaQt;70m*~s=M zBfxV+=#wV{Pk+PQe-F%nIe`61GAP8p z6L76#9dS1>;OIfXM$`0=@8hMTyW0A5zE^MbPw0O)9K}3 zR?CC-N581w^krY;YnORAL`A;t)8>?@4{^0CMKtacC@eg(ATzR(8M}`^Ck4rKE#^tsOwe8-jST0hD1R2)o-`=f~y3y6^*#=8v zo!WsMEKiJJ=M_Z2nt}uuyBw%_jKW?YOF(VJ7D+oy+dIt&-`mfUi@)}`bX1)J!%$!& z14i9Zd_H7o=+dLqegyFK<4ZlJo=Sv^VpItW6L7B|7LgM&Yhw#EMdr*0liP2+P@%T6Fe9rPgS7UJ()9r<`* zK0o?jQAWn}G2q0bzYp?0OoIO6@^!H-9{E}Pn@AMwGEVvz#8i=2|`0d`GW<1O22CxHlIsK*R?gI8~iZ%y~GJJx4~UxvFB3 zK)%gsT@2W_W^j3)IyY^#JU1q){`B#d@v^z-N_U4In_)6*b+BeG8uZ3{xuN5f%(K_X z+8O`xEzh!*B%R2lrYKjH57jy`3*eoRoak7&_}5usGq6%(<~PXkAFDx8vCS>$SaJ*PNwbyXyJWk z-Gl1?5iDN7T{ssk?qr~=riTJ}07D|E&I_U{tA4OpiS3%3QfpWiAkC_@G%QX4ba!C) zwu&_Yd1iaC;SIWL*tN$?1Ji50=hQ$x#d&<3yuUFBkWsl$RGwFG9el~LC6e~+0`z66 z)zbN~SD%bemTQAHjmPyxPrBszyb_(Le_;|V8sME{s16A1Eq!DAP*i~mVr!e_E}GBY z`4Hy*Dc>Z_SS5ecoiqUshjfPJ-Q6D56zX#**?Y*-u>7};@9B4#?gZ31gZ9DcR1V%K#h7Z zeYmnk_#N>IYF2oxKu{4vbz;w81u&nl9;ji!(qc?GG6OwfUEK?*nx2Khse*!lI;RnX zlLA%c^n;uRjU6HNCFi#OTg|n%l(^cYLzux$*=Pw+U!w1p@!j4|zcgF}J1A znz<{bLexPaqOy;Dli|MGbM=R>)&_&$$y(4J25l%?RE3$gKHZ#l+kia!r*`~}tNYUD zH}8y&($YH54mfN&*Ty_$d{N9jaQMv(-vOOcE5{L3|CZLd{gm(8$#dO%8|s5+1_17H z0huE7>@vKTZ~@;6S7S0FuYCsAF@xyfYD6sw?MgUo&J-a%i110&Z$0rDztUcTxk#)u z#9xC=52jKzs1E>ms^C#75>J&qywth0h8(6Jut4f+5FMDNI#mj2y=fQ;YU^kICJSFb z$!Pm!&=ws~C|~q%novNh-f&OZR0%yx>nEX=9KU<+s%}LyAEG7d9LPeF8k2d_YiM zVyTZd++QwexxtP2*2;<|Dlp?s`Zp(h{^`ns@uwJ_EPV5)A7LBGr*bIIzSy3M`(Y9{ zrE<;>=FfpPQIDt75usl~N4($9l^l751}U(wgT?8R+>*#Tt`1iubNA(eH<9w5M&#nN zYf6X{r*lK;x;$pkro=g?g8W|+#Sc(6l|W-EU5uwuX6b?PrWG@1?dL&SSeLSQb3u-qARovs$JvB_$;&|G==C^ z*N$21&!Pi^FoD~<&Za18ofK8psLiZwEf`(NgTH#uW0@ZwzY|%9Fo^IYR>xM?h(Wd$ z_Syrhk|*oJF*{kK8z&t!&Jd{!&lF)N*j|4XUw3!9Hn9=!)jzqt@S4nYZNzv#36Jq2 zg}o{r^M`_oSsgz1rT+ObkJC4vw}!n+dN}{rc<`-N!2Dp~NU4dxPuS;Yuf5;d=9;X> zJX;SP57%-(Mr{5j1OKAs9{2ZQ17)zrrd&`oI4{$AvggdK`lh8+mt$BBwB8XkfYQC1k~{-q;2Qn!67n{hPCMUn!VDcuA~G+$ooTFLzSM~DsZ4e&h0VBx5XaYm3z-F zc*K_5`o{HCAY984jl`GxNeHD%CL!|E@{KT`aPL`xpmADW3zw94C7^&=|L-o?>0-fpFG@t^1r4P%V*&=nUMu1pDi9{`#s`0LxghR2dPws z`c76_AMDeW*O2WzdJVPzew^E*&rUaK3=xo9_WPkBeB?Nw|DW^yrhdy6r~m%^9MxRH z^`F>RreA(TKU&G{#b>^UnZ{@vQ zegPnSLMTTsIOU7cF9mCtdi|nb1x^I(7mcMxR1yNQKq4KrHctKp^qT4-KWmO=oV)KX@iFw*K?om=;h-@EBV0y?w=)4J?%XEriCB#N3_X4MD(Xq^~hAyrt9l zUX+Dr&Q~wRZfx$nEu$@!SH)59moEeXUTE3aNSdRlBS)qB{}b()!NQA zxwt{qR`DJFr6E}e@lY>2VdDtDGPO|8%$fR70t3J&EsIAV0Jy(llG448|6wmu|7Y^Q zgPWNGAP|O3WqQ?Titzia(vi#>1!$%xF&>6K6eZ}Ybzv<~2>So`MU@HM;p~9Q2?rsq zSgwQUJ)_mk0{2WRQ`9q)>nL;kId_*7EJHFGJf)QQ7IR#c5MqRIvduRxY0lj^9Z zZ-mi@H~h1tqH4Zzbz$wZ<5DnJg6s;YT~6WT=>2z#k0o4p$W!Lia%XDb-4i9UiAnPR z)xf9S9PME-f&P4A|KzR(rM~r;t(EW#oas>p_Bmd^Kv?JNo!#p&r3zSS29Xcd%*|c_ zb9j`kpffs`Ddt@hjLX(4-{(Nm#%z$VHdaKI;NEFpp&N+{)PbhFe;RV+M;_ zKc1RSwWl+$>Gf?D)ZkjIq2ebl-1tz}QTaVzxJULQ7b&W2RE!aXf8=V(*Uxy^$U!rT z0k>;TIIHusjiNy{A@)=lWTuSZZZH1n%Kv-#uX{62%z1MoHrs8dWl6iZ2 zDA`ryOOpSS>b`F412I04>2yJ*?M9G2cbY#rXZY#aik>eUTF}E)L**x%b^CB_z<$*8 z^9zyJ50-8oa|;cauSmHL+}#GH$#OFmZpgRuJMlC7NUBsWU9POfP9AY>iRgus;B-Jr zLr0`cW+0NYpG(|Ylw{5dE4Y{kmnyW7Ryh>o&d+SIF2c!;MJ`Z^iLEfcRV9}tDsrWNegY4f4#bH`LXL*%t9!5Z`1LXx&j0p0!Fp~Y3@{kI4P`0mfG*7ld)v`bBQYz zT9aAN`g6cCefFObCjB=D>KnC&l6kXprU-5UGAZ zrBzi7p$uE2q&{Qk#XAAua}-@Lj&Z140@7VtLZEZSm$8t=<)gFUrp#SY# zwZ1;_Fqn{cTjNh#nAAI}fZ&K9zFn(Oo~0dJWe52|>a^QXkXl0141J#DN7k%EFYrc? zvEXWk_H?khas9&$d#OvaG0a~YSNK)$0;F9D&85q$BbBcf{)yc-ab0qYwTaJsjb4S` z&jH8xM%R?2*N5S#xFny&8i#^%#Nw)*?ji}Om-4~lbPY|anhiGiAL?k5_xeHLKB~8F z)v?g|MyMR{;*q1^eolsf5fyZjsbK_!TP*>?tbmYSS_@yW%Zj2d1fp$b-CO#JreNG# zfBd|}9k)ojn2$D8f#2ORFP|x;0RVb}SIZyWp(CT6lz8L)_uR3WrmooxgMH8YO|~kM z%{%7A!%VvD1VHhj!c(g&baANqZTkXMs#bxd;!NpV5fWPDoU~o7Hh0YIRTZSX(ay_a zc405DNu&Jr^9$DdVx8QCvfox|mv#)FxU!gly^DAsK;sJ!dDJ5gcIrHo+UEgp0hk+w zQ;%dVf=5cY_g$w{(=qCSBCF8Uff1<(IWXaeSlFU#dvJ|E!Oi%<@Pt^23e!IBN?Zy? z=7@-K){ngD(OAZ{DL}S~r5bKN9i7|w9u{(%GNLoR8s2eKHQckN0j^RU(R1pipd;^D zs@IPFy;t`Oj^zsly&L)0>Vd~?lbp~?AQ`{E6E8Zgz|~}3GGjm@CEA%iqm4zHYNrjy z7lv7@Iuitr6AoKc{YIFlwjdI5fD$yB5qE9piAwvbgLkAcGjBBDlp;>TqXn?hI|_UX ze3|$V`sG~i#tNb`R1n_>5~Pp<-#r_W$*-jM_X8CC-@9{8>!<8zQn_Gdan^a6AOz5wJ*S>>i z(D>O{TDcrhcmt%{&X~}vPspiyV&v%8F`d-H70i^MCJdpL3K7#>Oj*z~J6i3&e-O~^ zLT~o@%~>dfItSs()z(W(N%Q1h&DPX6G%1{4;=94m|AuqOpI5Bw%P$#uW?4E3UC7}l zI&IjMx%*T(5?Cp1 zL{`hzoD-reJfC28vZsFP8U>hKemKCTYK6VL!%x5af&hUN_^^<^@axt|GN$~_yMd!x z7>EsR`urQZRRXIU@Q4MAE%{d{*3 zz~1XVc1*9GC)sghd`3%;-E1Iq0_2wcV<#8SJz(xrW^R{#UnIkMa@hQWH6ODSW66aK zP*Ih@32U?dfOnu8s+)Gk>y1{>%sggU{)`IX6JV`Msb7236{N zB+6!sSvxASykWakB!w&8@c_+R)8Zfx>`s z@2N51(t%n&h+>~Ldq8ud(T{A}%Wu$vHm&8;*oN&R3bJ%-jFb>m91*S*j#);!PwR8B zF_<7%H*Rnl@8Qz8N9#t!9;wu-0L=u!GHndSW@RWS-EEsn<6z(7VgAc`^VCp`^!rEcP$m$;>CrCr%dqNU7$~`kW z(4p|=8 zo8Es`{tgInY0q!tEB}buGxRGh|9AbU^w{6+^@B7jh-w3*bCqUj-G{!#4Dck;<=EQ~ zy_r*3{K(Pi5uLs=I705sf}h*dz~kD}VXqe+PAp8nLBf>$8Sk-=;I!f)FVr_|bYoVe z?9ihuP+XF=TBgleKY!su8bygutWZkqVtJLl!f_v}|0Gd}KnqCsc(yT(@*?Q+B4+^H z3!FEzC;aZhSAaLx&24SxYPs-ma;brCM64Hi}sX=U5Y`-)j}z za%veJnzVBsz5WXiHGSx8l^ojEo=4Ze*@m^hFlcA_+4nf>#%~YIpU;pzCF)iKA_1oA z7N*GfwyrciX6r3$CIJ{%W`Q<$I&2vKH`1rScD8c6j=3$fmMyOk zr2xpKd(os{Wxt8r0r1-YF&fG$1K`vcQf0$}Wqji8@;E!7m={5;kpOKZysDhYS>oiz zQY#m)Ffs|^C?`Q9>y2`OnpZx$8#^16;Zj#Ytv-U#Ho$~OFwcq8x;yew&-U6(!?!IP zB|90hZK|{u{f<+q`mU2^FCF|k?(H~1g53q?AoYEsg%_v|+uHV6;9k6F^ILt9oo|{2 zw~SSWF1|bRS9r{Y)aldm#H#gUCe8j>x=<&Tc6-5hBDje6y#DxN!vTy|-2fYVOdqOigj*PH|LPR-mXj!;!fvaAjtOGb=0HBQq;2 zwS{J8W_@hWZ$3Q#06ze)>$>mvdA^S$(@#zmuIkzxu2d!A25QziKUWKqs}z@Xn7Y^; zG>5X)24wn=uV;RW`oNTrBBKop7O*XEY94akoX}BBR1*``?zFPK07X{gL{WVbf;8GA z3_P~97pi7H4l+O41)uxs1lOg&`~givNMoq7Rmso`_1y@;s&hKfZ^R0a(ny-=j6e3u z#jQ{=mc7gHK3+BtlI3*s%+O7c&RfZgx2;0F=PFKQe*C%OyyHxVaB?5%{`y{QWkBmI z?JbX-xX6=wOgJNTYjU@f_w$d|ik%NZ8^2EJc^);Mts9c=Dn)foBPnD%xn4Vq{$nD? z0n72yVV3Ub#89bzncx~yAtc11fLqqd*cgP+rnPA_C#)9 zTK8T#?D&oSJM(kOOQ#chUB5Q=wIeZu-nq1|uly6=(oE}X73cSU-4?17kOZ#e%MPXhIthwa#i3a{{$bkWm8oxAq>n>nDdaL;t+oOIMG&mghER5 z`p(aTEOQPhWG>~@om0xN0gWSe+z)`X|9FP_I$_M10N;b0w#mFEP0*Lf>SoDv-!#7X z5oUJj7}iblFH%0FR(=GmsOfaCmHa<-rY?u_zZrm3pEhuG96Rlx%44&RBHYfB-OmDr z&w~!eE#ApTR#h>*&NA6CLZWP4jqF+4amXhtW%)Fg!WvjMius}SvFw^p<3|)BCm8BX zF2BW8b^uhmF@a%FYmi;WVP^^1yl99^h|nJN51C{jj$))R_A^ zm$lxG(7(KT7c_s81?mVL&LB8eRK zr;u*HzG*dfGke#m%zj4ZToA0fM~NrU?H8a;3Y6xG37joS$@KrH(HhV~josq1sV1hg))5nzL*)OP6dAYiB}EnNH8 zI|qe1O&Sj`H`{1*x-V*e+OFd8n%o5h#*`i?j1>P8GHQ+)W=o@Q5n*bZfw&;IH&d5t zRKgz~#K5%`BepF(eB*Z3m|uHq@+U=J+HM|tT{XL!#CO))bRfyssuqrB(802fj3N;U@7D# z0FrUs1OO$dK!}uDxhe!z+P<#BF6cI)wTyo2@?(=Z0ED1<9hMks~aP24ENL1!m= z-VgtN-%90IjoDl!BuNux746pGx-Hq!FIVNXN|&sKbEf&E@+A(%T{(ajiF1`7H8*~Il6=(oQW7VI% zDKan$VZ8l4bvfzIz2O{F#|e$o&OL+CxWt!bxo2Y=GFehnhQUm8!uJEB`wBkq_gSA% z5Nm<&g}VLhQK&}3tY2utFDkvRMU6sbN-sxVr#ZrP>XweZK6KsYB_|2KF8GxFrmFLq z&&^LnX{S}2m{uZ8vR%+nsxd}eiqZGyb-7yzL{~wcNN3tkgz84J(-r{>007=_HXZ_6 zySm;6!P1#q6!U@R3hd=sr+le|&Xr;5U2~*SS)=wLm=2Q@QLWlgU0xVI-AeusZLS!t z_#@SeGG2KEdMsA&aJz5iHk9CR^^QN!E96-A3?c@k!ku8{cD1s8=IX|`@^dStLNklB@fsS14ZkSGJ>+}^26KJ}f-M0^Ds-W0)1w(*7!tb((zd#OvG6uPS0wn|7_YO9_4N!14Wm`^u} zR$G6M4MI*nRWf6*!t}CAaBS0IqY+nR{{lrfsZqK7Fh61flEyqwd zUR{+w(WI`$bPI1oVw_kIbT9Wo7D?ez5nLb1zttjS>lhTEyoxC@H7Y$?#-uR65V_fE zWRD)|N`Rjlq2ipSR`6d>a$nb|OcZJMJDN7^jqgexrCXn`cFLB#T@8{o$4M##4x;o- zPwQO*SlD(a^f@jY|2=0My8lYNL_99)N)_>+*97_W0b117uhs=%o`*Zv4{5{s_EC7c z&RUe90-w2{{x@Xwc7THN06@Pn{040{g%URvF` zmp3DyPJ|oqIFR^FuID!eG4Mq^kR!8F8-T&nc*u@Brf`2+Hcslr%l{o~o^3{^HyD-0 zdSVH)4oub%=*%|C(IL6Qz0F2iK6`s`Zt_=k?;5DJ?hRgCW`RQ*jE*QNxTpPH<{pCu zAvt$FpFZAm=qE_2^m?9@yh`m(GpxmVyEdVr#@1saWVr1$UuVghw8zoHXxqW-=D6b- z&>jk?orb%dJe_5>nt`<|q_OXh&2rW!h7NXd45s@sMn8E`6!e+dDDnbECyKb^q8AH8 z0VehR2b~qTi7{Hr3zJFr$YQ`5Q~&t8Ru>7fM}Mw)@iLm)vKQf&rkqQ-djY4%l1S`7 zBy~&#m1L+x_fCM4#g;YQ4YDjvF-MnHpvl*J=Rs*`PUAldvA0!NWHdA&70vSL|NN?7 zDUPpArN6KX2|s+J^}+jchjV&{8n@mDn4JG^i90+-1n;j-#{PS7kMfv&?yt)xQ%}^o zc0gaF`nT-uA`u`f<_a`YVEP27aMP{C<_7f+UB$r1x1jwEr)1=GM@=MW9oX{?N&!{> zd_p4H7|nXWi!Pb)zR6WR>~Df{G2l4kFPdDf7E3tGJBsjH!GMm>+$`GG!!L2x%u}18 zvkf~w6`FV{S)g7|AN<+N)CfG2zhh%5;Ha+33tdXY^dMP5*0<(G`k>8~L5ZN9@VBpN zjgko&4py=c5P9z-R6~W`M>-U{jgG`bTXrjD`OW6w<_A%7oZZJ;H=nA65D~qzjcoFX zcaCEFrh;IQV}50PCnYxiKmrp#rHyirTElvfgE`i!Oeq)LG7bDUrDFnkki+2Y%?~cN zwu3CIb`p$6KG@kTo4aG=?N-WU?!nAMb~>?YHffm`vNZ%nIq&25dA%@}>Axz=S1M3au0zG37P`+01MoeRranMGL`1Upom1G_9 zS*6d6F$~8?mf!BhPWUk<#Gxvu<04y9SwMOh6|Ud|5O_&oW2!|*asCmcHFx&Ca3gP^ zEkD)mZn}m|9~1RkINgD}H;w%uoPmk~nnQf0IVaT>mdpwXd-}D6q7U@5Psh*1j@alT zAp{?~OJZ6>W$TExiarrt4n-4*hz(+#;UHde<+QEDoW4QVUIQ2Kr848YUo7u-J9?t* zofDHtE~30L?f2cAoG<#&o_QX^TJ$}`sM2=uJ4O1Z9}trji7gdL-@ckl%`26Y_Ddnf{ zx2+r^sgQl`nHJM}FGqZR!5>LLqBU&bR zP3uSlkCxOh|8^Pf0Wz9uu;exNnhHh>(|L`LGmu3!YT;r=P#cyCx1jpSrwo zz6jTvBi(xShTwz3NR*_{xL14fVQ1oz@&v2VtE$CLyQfWde{T94$cOA@lD1TcaCjnk zM9=rR`#DW!#$N-CC(?s|@B7JT*K;nCROs^ZhJ=|xg$PtIng}u!G|)OA3)#?;Hx1&R z>U@R18XC0vg=F9b`46GKoo47!9OBhx6j(SaqO1;xX}9gGaLdsU!;AFwiBz1j3Ix7) z5E05knWtMBMq;WWr-dY|u^D8uV49&pts&xjX01ZZZ00xaWoXC_IbqNFsm-xu=BSnR zUD!)3OJ&f1K`jPbNkvg(9U%yG0};PYgdPi*SQlbHUxcO;KQF>fZ?-&K@q2`AGCssZ z*Ah+60Ok013Fhxj43bOn1)kfk^y(%*)P=^S@=)XyNLF(3Ry%pS&x0Swh$?gz!HZ{W zPK0b~qq_uP=SRAK=*f$b?pTsS_0{XED>*H|&GrPPiDadjk(M=MZ2c%S)Mhql&xZIrG>|vd6Qu=OGhn9@b-Xs;l06{N z4ET29o75So5wbac9Y3pOhRZo{gh4q+Qu84a@T;&JM5}bj<#fnh*(Fr622~~xCQzZlE z@6OaO-4Gfnjt?rH440G#4Ga`v#teM8CJTgc^afjw-MyQgc8f!@yJG>yC4yD7;Qa*| z_QaQpx&KBc_hs%%yJUe`K9te)MWc!?pZ5)y#g`=}Ha>hb{G@wdF=e~ay7t$%kR_SM zkBHagQb^r52&)~)KFa)ffoBH*;CacjtHk~U3@aFZOkc_niGsI^b}MyiI*6KlKM0wvQU*u!(Oc1~EAOQ-C# zRm?>m0=)9ajlz{F3|LAsVbyybhZfKOBujQ30d{T28>bI|J@h@9TC5056Uc7}tH@;$Lq6k|{OkfeSO=Q6Ir5`7+N)m}G<}Kj7K}Q~7 zm9LFP2S{NKW4R{ym67eyOoe7|h3Rng*Y7tR^Z)*nXWY8%r6_jNz1_1!Rlpxj=}y_L zxbLXtQRRKWs}Yo?IuN!sfr7=#!L$|btl#|~7<>B76$qCH%YM5@J3^i-!eoKuc9?4i z8gCy2wmUG6XiO26m3R*;h(9`yX!wlWwrX^p4Jnty9M(+s zSoO%vIF5b08ezP*F!;%6s5GSYNw|NH0HZ#bciYRkc^!!#3lA$taCj#sm(XTHt8)Cz z#y(e@D%Yn>ncFngR#08pYs}zgL@i&6utfN3?C@Js^1rsCjY9jeVI0&ba&8!PjOoz; zV0Jho#8_-vb{a&pFs>`Kry0QC8S7FP-}3M&IR|LhnB< z^x7lPU+fmw0lH*qnr%Tv_1CEMNHP#lS2@`GuSJrkE&`#oz#E?D*4;B{Oz;d!T{eOy zv^X5Igj@N9Q8OI+xNE!K#}#YF4&}`K*AUu^gk=@KAm33wW#jdF8cB{udQdTHi{2k7 zDHn^~|1ap&;*gYMc@vV@V*Z(YXj&nteJr-Ld5ss$s5!v&ke>lnU!{Ez{WuZkXN_~U z2#E~OBvOy3+C2ABEv24{^WR0a<`|Ae)Pb-TEycfuxX(%V`=|qj{zc2C(QE@4BbdUIClDL!M4$RyyNQ=Qx?R6~ZZvghLZeAf>@~~Lwa${Y-g~M|6q>pwA@ zo){9(`)b2e08L(uJb(e26` zkbiu5ArUi>amG;lYBL*7~y^Rj4 z9rUFLuU~Ubo9mtA>Y0q$3a27StC1UL`>0O#t&RWvo^d#N+_Clb ze*+WS^?7}wOo=087*Qx`=!96dWV^6SaZQhm@AD5FVlR-W z^Ym);BSKu#3@~pY?kYRs?ZY7g(XEsONC+KB7owjCf1Eu1gec^=kbLs;4uSq!?6QaG z^DIxV6sW$9Y2ipS-z|0&{asvI>?rY)XASYotoU~^*=el{M*qv(1MW+CX?!%pRYB!e z3Rnf8t-Z5rwy$^Hcj*`NeAX^f;W#MN35aH?hc4j=EPMxS|J)wZ*0(gsV|&PHt&tUr zoiY!zes;f>2DGQgSLz)hzwGx1c8tcBj;krN%14``afXtW&F$A{O=flblB4gx6^UINJ|lEIY_Ht2_X>Lg}_aQ_1r4y&I2PW|H7gsWFR zq;G3-^O>{%htE5VD1leLO+4GV?22Y?=OwdXvYvP*fC*trSc?)40XqeBiPgFOrvUgX zL+W&s6#CnpZ-Qryyl2wi;iL?`#|91@ZZfErCIGzwmIh=z8d`#W}LP zN>=L+&658#16;CF@9FveeL?gKs7Jp60a_R}9t2Gh>DZ_u7JTJ%)p1GCk;OqYPgZUJ zk<5smtTz`7_v?V&_}Vlb)1>3Fc%o&N8<|UF4WNGSQ|*L*eXEu24?lC<)RLXFL*nOpRWPjV@VGxfgaJf|Fh2T@VnZqJN) z)MB!x$g@XOVFC@GRd1&Y6K52RtG|cX15C&r&*T5Hfb{=n8RtHZLM!Cv zZp8i(MG)G|Yt{)WIjhJjHtV2K5jH*|vb?J70~Q2Ke82)c(`U1R2qJ3)-|f7BBn4_q z`+^bDO)NMn3#ARB=hXm;sOL^w+BFB|N3=l6u1%{>1Ytp09YcLqi>f2NS#}NDnqbhC&bovu>8{#-R# z|F7Xsl;uVY6=ajKkG6K5(g(!rzVTURV^%XE(o3wgkWcqaa1^(6c9D=*M2i1=kQ1&* zp&&Gi3@y|PHS%J;3P+r0hfeDv1(DMGZGk9hGiTf&L}qgnye;XD=^!R!w=f@SR{23# z?8%YPEWc{l$yU6|Ng3MK%3Cfn!IGHT^+#o)V{c$DuCG#1E>Rb$F(=tNX9?4nGI`q$ zv$^Xf;?&HezcTJ_ifT1b$;l6CJ6edrNYv@VB5(RR+cw!YG}ghg!6D^ z=Pu_++2ByC#?mX-gRI`~ontplNUs_8ZUkR3{^qYsQ~6}**Cu~vGQ^qpc~l{SQ+8J& zZ(ggYcYYnS6_y@u6pz{rE?L`c$fISeeI1hAD`KNsx!FO1>cGp%d+|PdOpe=4TVC!Q zgvIl7v@ONLpglf9d)rzO8y-IT%bRi1LgfLcC3ke8WYCz$rZ^*zhPG6>N+C<)c{b#F zCtI>j0J3&tNobkoWHs>A{;cSb{U;U_#d`YsH`6hW32egm*SXCSB~Bj#T?rNPd^=Mx zUbKFil>Puo^^%C^Zn`?$)^6x1m{1Hecu*9f34mH9V5%7aH|cBN9dF^Gt)Jtii{JZD zj`$=a22frluIxI;$?K9?($57@*`H+W1J0P~92aU7KI1J|!CwFeT(n6+r87UPoEL3< z)p0O#uEMe=oYM8|U0v83r;ho%5=M0?W@zn8-5eL&Y`vmcCs194yNBQ%%RT*(_97$S zPH@H|;<|_aXWFiS8~JkWv__@*<5Pl{l>7a&-GSrlkUgao_Leh*aP=od>-J{;&Viwr zG*qk6*Uvk}w~OhCcU4rEO(QF^(Lo$jNTAFXS#8i2aRQ!}AxV&}^fvGNZ)|+(o?M^w zWvoVGwkDoeObQhNngMaQ66c+A7v2L_8*$0lmf`1{ruJ@EhGZ6mEL?F9$es?tQABKs z4RYbH0*d@4t_vtmn!FwIUNMI5+ZxWnApE!*Rc86y`(7(c`7N?Kh?p(2Hh!t1dGUS; zR&O%%NzUeuJt+@8^3wF(_MH-dW|d*zhbTYcnxGC0=-z*h9kTRJTxX? zy55;cR@HJ(tI=o;GmcC5>G0|usem6-YJCzB<(ajJchN2{+d*pbA)r3)qCI@}#IA|- z(9-y0-XHve#M7g8phUORaL>pB%q` zqQgb+05BdjhL`o1Ch-ve8zQx6Nruvf5Z$J+mbXnsCYwW;RovhfHba>IU5k38Ia|qc zogJE92^-dBNxH79vaE!m$OOe$fAkz&mnAVphAOAhA?~qTfGly%Su&kUP+Z*{-uAB~ zInir4KNHt%XDGp75-6*}%V>fFL^)6Zx~OjkRh}nj9uwpQavCl>ygt3Jcfw+8W0!=y z+-q!0U#W{oNG{!kvrjJZk_Ek2?g|IZ`+;3nt@QZab%e)r-0|)BoYNY=Be(4bH5tmL zy6G~MOa3`EP56CJl47=V)r5%tdlE@foUai@MCLvkUA`N6WhwO&dh27NBHaz_q-}G9 zGhm%IXvsMzT(Pq)^V!WjCPP`CE1S@g(VYCYhZ?Wr9d0poq_pk8`d`^gb51f^>Pje8 zW72?opo7#oO6X_7680E$NURS0FTl3bCzCPye2Z?Ejhl^-ZQ1w|=v@7>v+$vQ>^Dt* zv+HFbqQH@6vwmKFSc69GLs=-LN<4I4`I3_itQlsqyk9Jd{8^yd|8Nz!W` zt<2^%>c!oLKZ!UvLK*X5m?1whFj^t>*N%N=v4lKvJ-$;w6;9J&uj=az1NIXR#ALRt{n+he zp)tH7mknvIroQyHJJAht#YPw-u%CpjoyNrS1gNrJ2oBu@?zxHqoj5A=W3)}L))P>J z+!bZ=c8ty6LHSdP!*RY3Job&>@TRwIsU#gXnhw&iQNQy z{8_als!J1E4Mq5!q&kf%zZdTQ3L!-UGk%rJ+Fsfu@b0)s+$tl__6dMV4bGKWM~;85 zB~?YIhhtU~{_8BJ{A-*IPI6?tQr!5FOa4p3{lSco2xeQI$xN&rm6Tmu;)RHXQ3P-IvNLa;iwIh zVZNr=FjL5$vxj$3_tL*_ZPTZr6Jw(H(TRx9qz|*Xueh!UCiaE^#!*_iN1}XVCt`{i zmZxOZPe6~7%3;zph?S{kxf}=#XcWlCC24?8uUJ!;tMDEab?LPDq2a zT&Eqv%zMl-BoCZ*r4y@SH-p)VXYm4uABz;zl7vxFIyoEhwKv%5fo{h_Ycb4~H zfy57`GB{Z;oJ5&G_Vq4k#xP|2gWX|dRMU6#4hHf%kl8KR`QazpZ4;5diT)`^yUw(! zk&}1@_GLh~P*1>$665r}7GyTg*p~|?95nV3F%4-j*d9|SIY2C{0U9B8JSdf&9y5!Y z0>C_h#4!MVPGDmFHQa{nx5Juh>mP}ei8AH*VIonaK4r)wD#ag`%Y^kVDVfAL*$F^i z`A7$%@%oaQM}`G$ZCj+JD^iPSAua@Pb8SV)X#w^wF_2*;8QRJDX=Z(HCo&F;Z z-RWKuPKT~cBpQ<(#;StPrFp`*K-VO`Q|XWn$-AQwxM6z8tjy77@K8Qi;+CKM!4%mO zOptTg{<2v^r$NZ22-zJhP-?|#)+YMTSbhg}4Qg*XO zIV>m8&A|_1W^R2bO(RngbTZccY!8sB2TJSCDB6`#Gm0=Hf<7SQEB0k-h~R&|7@lCF zBe`1DHQF%`GeaX1TKnU#vV(hj5^&N9jcLIbHKa4My~fYjUw00f@b|iTN$0lvp}FYd zN3t>xS1^zI@HL+k)flMSWj)@|p-;1j-(L+sHX80COCFd-IC4>`)RPZrVXZ@ut0EwE z7;&8mEzATb(SZ-urO%X%1_WWNHMS9(V38a20ZsO6%Wh}7c-5ErlHmJ%nKlRB6&L}R z6F?-RV0N<*IVSQd$@E);iTc;H>2T!-;W^*djd!$eDdR4glC&eNNBBlfk;EfB$W2pN z5dl}BGL_QcVXBNvfN149^)gkr)8P#~b0r#R^AlX~Tin>J3Y`ludbEQ^L>HM#QjZY> z7jlgaj&#wHcE8ibTFi7aS9NAnj@D5USp86@MxHhx#f5P!K^$3)0!@G&sDUSfa$?+* zx&h2);C|B#XETr@U}K>k^g%JGTL5hsJ#PZ+70WnP`P$cI>>8`>2EG;wqY`dtdDRYI zs#pvbK0S^i9hmLAc0C_3CP^1I==9q;++PW~oq3JGZ#&EM((m+EW$B%KZ$D10pQlT% zGLU4a*bfl(2yRpCfd!)%4f4pf9)alm0KP4vp z3iI_rwU2!84v)k7WQ*`{u=ebceY1yAeiE;lRb#-u$Bd>A4466{37 zwX&;DMK|v&tYWWfgcgE5H{C4NHA?sGk2z*@dJeQAgjkC58IAhs8lZ?xfPS2DU0Ch9 z0SRNszPEulm_kbEThjgSikPVmFoxZ6uvdGWXVH|WY-er7RIpS^A%9+{XR_#;mF_hQ zuehw^*fNLg%VV?hdKcOps>~1RI~_R55claM&~LGyHqj3@4ZWD4|MKJ)F16qVJF}QO z{~+SsOfhgG_K1Eauj--(9m>laxzq(jFwZG&P7Zg$3O7$2pqF$EK_X|7UE1YMTsrFT)&sqhX=W1c5e%h|>C&R1_CXcqvZ*)+z7q-Zq6&b#hf;2Gp6q zreb=4<2$9-cO#_!2)4kcW@sp%(V<6gG}z-3+#BFf0oD`110Eo{xJ0zN_G z&bw%nytvph&A=fY-f8_3A%5zn|HoI4L2n~FHo9Um9bdURGddMM~B zOgHmk-!*6LbNlU%(sU9c=43$lobtKy)S|9m{V<-cdM92f#d~ZQ;)Vbp^rFsS3BzDJ zT7XW>h_E+BGM^%4Ylt@-$+G17oxd$G1E%0tmo5&>-dNKuaGA#NzN2+$H~)8b=e5iF zfqy1NKMV%VOV+5+>jKI5qAL>)Oj)_RF*cBB0Ys06xJ8Hg2;q86WB~V;Vm0Jx@m8ek z8Rg->0-U>w#-g(2E`fz*nnk7QkL5`148DWb9ln{$&hdMHhVHo@6q9wtrz=>AzM$V| zzU~Awd^EVDjjc3}N+3!_;w(dK5RYjRCj$n8_{Z9!QT4^C@}pTyyLka$n#WRvxT7x!U205?2g zSKe6Y@IQ}s`GnyW&p<=ZU@$z}z>C1|yXJyxYq9I=(WxNV@qbFg-eK5)ZeOdG@HHqs z>+#V6{G1Eo<%;)IqoYNN%!3~Xc35EBJ_8s5%tQo;Da1y?b1Ae;SH~@|k5BvwqoBsQ z>e5T6!iYB4dHQ|{vQ*<89(==>Yx-AHp1?EpxN z{G3K_bWz&&b~?bH3(^xo57Q8tJ(hdj?~gDn-PfaCzE~!AH2N$wMK)vh@T~Mwo0E}8 z?~vV$xb6ovK)Lc7MOJ66MER<8jf!4iT%e)4DV1Z(i4Sr&;{|r}N-M`#+<=KmbNFo< z+OPzu+mGwSX+SI{GNL=j zTae@Rm<`-|R5z#P_{gBjx9t`4E(=x{&U z;>V$eQ$7)pZk6+-imne7VTx(qzjEb8`Z&mf@G;nH$IOF?;s15(I`rCjsqr2Ds_C6fMGZvN z9xy+S9kd_p$KQ881SKCjDYBnK0Rvxrg;Bkv7JY^2(oRrhsGvs zRugL!j(~_v<2yir38nN_TC-=3 z`aP5=8?U_U!us>cbUojTfsm{>c0?htRULJC-Zm{Sp0BTBD_jS$h6y_Le$~3Z>MptH zxn%PwQ?ux)F1wPqh6yp#^Vh;E@AUq8{IO6$zs%oxy)mMgFvK3u0$qxJ z(Qu?)Q#sNFYi$;Br+89Zo=>fk-1>L$(gPBun6PC(SIu+C%tzg;^URPYQL2zqU2Ztq z{|3P?9-f_^vWk$;ut^HNmC;FjSVm?4t2a2KiK>Qvm3hvW8rzW#MOor>i6Ai24FG{L zvVuF{Uwas25XhfShRDdylA*?$02PAJ6y_kMb$I|x7TGx>yOi7sb zwCNDoT((S7R<3^pqL{h$l?qDvyZIWw^NHDbeL%*q*K#(+ipf~(c+sVXY!45e<{mKp zap1Y%XrL>x)~f9?A(nUf%3hd(VSS<^8KKn{yIf4EIz&NB&iCi$sM*#eNFZBTMA)tG zgeA^dY)#PPlb_Lmfhc<^)w9yny0)Nb_OX@e|$VkG> z8ta=q%`(KC&uq!gxA?Gfx>z3Y6QWV|Z|!yFPT1+H({Imb&frF*sG4L5)NMe@N#eJ| z<~q1}^=cdpXUP~-9#)ffrX;PDIrpLrg=ev^w53PD_1^(dm|LRqD0q$OtgzE@HjkDV zwLeC|Cef_^?15$7(+c6yOQRB@%k;nQpJ<986zC@|M{0*TzXn;GpnZbK!g?S{*?#{) z_ztMw%Tm(l63+JpE&KF_5+aGsRdQV)^fL(Agd$aq6?rYY&+6>;4?V5_?N>+7Vq9xe z$JT#m0zO=b=W#(HhtC|_dm4P<@8{0tZx>hM?7s*0HO(_6h>rYi*;Y?P%!)t9*9M_7 ze)CSSBMe1wG!N|0&9z|&$m3a%@QT6dgT0Q z)Jk#OcI0|^#gMKP%$3fRw&kTs0WYAl7WRA1SqO%?o0H@aJ6m_;aB|=aqXfG=YRE7q zw?WlZD%YVHOHr^uPD&CPS8#zOC^gyDKh(o{(X(yZVpj@&)(b!lFxLL%3GQL|`gLN;T99Vu%;MaLsdCV5<6q5%JaH&_RDn40i=c`H*FZ7VFuPH zCS$t;hwz2EaI>fg5F_GXVsIQTXsWAm*}cJDBT6L%TiS`Qji`ayRIJi8qazuPejLYsF>O^6}y!u zeQ3NK)OOr%^@(MeCixzW4AYn8?ZT0W9oe!g*~XPm9diyP=3V(RXAF(qJ|j1y{&`eg z=6``HBf3(Vo8*JN(o&us8Wnva7j2saj9yK-R7-tL!oj;aUP9}>6YG17eD`@e>Orr; z+})i1;!#$}$o`(XiBu^U2e`?J=Vy__Pb5z%>v;N(+wstxh_l`3+ zde;qtQ5UV#v}8j&&z|t$M*A(-gf_;)o{0-e-?;nQFZr`1?tZ@d7u*H|%h(ReWr+1P ze`y>F85T-Do2#4yLAb&#ls@WW)16=yZ9FN0$~||D9S~|OfbXiN9t?_CW-mK?3q0GexhUhDD^RD^EoJyT(Ro7yt2RTlI(#~?JBl0 zzYJflAhu|oD9(N!nb*-uvMx2F-rf|JXgYu0S)P(qr0E;^P4NhD?oe5nRM`wZ;acB= z%#ETaH8UesNALx&@n|Nr$zUtN-J|0*ftOV-RZN7dr{XcdZ4KKsh}B{OxF#;dD_3;mXV&D& zOUp0Lhd8|0biCl#P*SNTd(z-mTD~h41hrm!`eFBV4W(H%X#(#p7YMiqKp6H!M{LYBP37A1gH42bUE2>)s^|4t_LdxdesmWUh5dM zSmFN4+r+C|R5fLq z%eTRc!y9M(Q*`1XF6p1QGYA(ObiKCPvOUsWhAY*+`kk4q-{Ci4eVKS+rJJQ z{&3UlVT6LNG$~+B22(Sb$udQn zk=6BN5Zft+P~;6Gw7sE&JzQGz*IKu+eRs$E1{A8Ust0%N7vT^scW*323fI^POi)mp(Z1nCz?%)N4No7n>*0PTPTzIaYOROXSi;ZQcbuF4tWeyRgD5_(MFv$SXWnoD-?XuEUzR zjk((qCBWDrV#y#`n>q{I9|(WtU!AjLJNYx$ZBBH-8NQNb}iK4&WU)=I9bE1(&ZcWB-c*`=6;jW$Rd7{Jq`s*d8c3*SS`@@VWUV zr(7rvkYMEdPUQPC@*NauJp>%;iUnvv11I5I8)7k}4&}*8_iB_Szo^l`cDs1?c*fTC zKk}UVOgYpK-@loiK$K9Qcwkvctxaj(L+$knbX+X#o!J9*SsUoWKD_7Y`5X{8HH#pRk`S78;?rW+~+nL$r#=^gTiM6OY?w;72fYsxcPbp_Gt=Sf9Y@gQb zVBwyN0oez`b1b;o6kCX+5j!|A$T2{E`0GfL`n7i5!oVo>s|p>m)`hF4{@uE4kDd{2 zK%mO-e(FCqL+q4-6o?^vu5S51j$^UU`jJ}=3eq%?F9*D!S)BTuH!>P*#bl+p@t>KJ z^Y|r4SF(>mYvcl1+!~T!**Mdue4*ciu}=a?)a;!0_fxXv)M5Px1rfuS0|T^B-}t&FI-y?o`_{nW8I7 z{&yejxZX@-W(zd;KYvJ_sdHrG?rOxI)8JBbXLeeDF6b3XEk7F_M$}>}~C<-Bu+J$a39kR8&}JYSq32zfk8eS|QU0`-2xNuJ*`m_wCh* z=ymkXN#N4;tSTw(cW+NR6iq;|k_AE{NGbx;+6fqw2R&C48`UxjFZwIF2z31eo;$yI^bm&}(^hl^ zUk0LNa~}evht;41!h3P)#ilLAN3zc9we~BmsZwYle;2)nn?%Yc@jxK-ZhwjWk?}lI z-oe?(x~Wc;-_Og7_{2T>J3p4zl~+d+^@-)cWA6sDuPm*#^8FdCspAlB2C!QnKGg|J zS)15ypy3bX{Mr(nZuSIdx23j5x^FDEF^$UV+i!qpnTLKtPtE4LG?>4eXvTupDnoSS(b^6few-c8d*k-2_`@^zUq%e~{TQ`YbSa zV!#-8SAd0aVTi!j!@&Phbnf9yzyBZK*~YLr4#UQrPjfz`nmHdEAvrZ?A?Ku0?Swg% zQ!G()3{g&<Zmy zV-|laC}sMJyS{yImMef{%msN;i8_ghzaOUmzBXVUi01GQFNN4^k))T_lu zMfs((qF5R$Hx`2UDSIJF>VIpArhE8L4kUr6`M=!^$F;5xX@$_WL;$D1`s}BR7c?zz zXFWjHZD1_kenA03rD%-v*&0Fxc62_|;_$K;O7+xRm76-Kzv;>U*wEO5 zOtBLc`EInTSDg<%Y2r?h{d};`u+TYL@E!+z7gbB3>VS**8g(z_#+*A-9!@G1Sp;

MWFhn@FDqymsxr)q=3e>N>T<-zRMFkF@Q&^LEy;P` zJANhgitcdrbPIJ1Q#D;h1qTXZGZz?;o|dXJS$iaLh;t3|Y5`*pdxJ}4h%{!RW^dT} z%#hjh+PwW0l-FI2>cLtwz-a$mUs%Mh#cJ2={w4ftula^=!h7fcc>Lnw%dqt>={Nq~ z0q?FJKGkVAW(}6%EI8VSX>W5+A7KVF=~``V&_244JLfg+d48Xeel)A@%ev#ny7(@9 zn*T;C5qIkN^OubuBGwt0+$6={VM?-M`5R#(10)2B0kv2vc~dPW0Dl<<5iBOZ+3o0~ z0R#aUnHY6|1k$R-s!xUwR$Qt}N~w#rx^_kUO4lX_83H zBTuHoc9kZ7FDOFC`YE4_QEpC0+-zlBZv);PUS%H0dw)gvNO)e@hQH@jn#IW_KV`N5GGp5^EiBrz9C^}Vn3w5KbWrpqRCDgZ2exH^t2Ov{PGa(t&k==y ze~(EQAZ&Ps%pSHMG{QdWFjwgpO?}ZK;7(9Oi+WC+XZh5K29G?Du*1}taVqo zo1Xu|KF^`D{Ow#LJ9nkV`37t+>=9r}qo~;lMdf-!H~fn|3ptoWch7 z8Dw4TL%a1KeZTL)4OYfP8FjJK^$y0794%ltSVq?>b++-AowKx>nZ5};F@#6MzP#yF zG~IL<@64WUq)Bms|#9 z;f4UX3n2L60B`rFhnxq}%t?43?_QXKk5u0fj1(Q>Af?w#WD4(fwEOb1(&=F1lvQ@W z(c?d%W9CDcZ;ORPaiJmv7N!P7D8Lxi;tMoyxT@Y=R{;?^=bV01f)Ap19Y+tMGOk$+Xr-A zsQ?9-cs`K@cH1f2vVt1br@U%08-^2KF$%5>j?F`fhcaon`{o=1puD zdc$5h3}pimFHs6ijHa*{@{%#L$>UbR+beGpnCT&#ghIEPr^)S~04yo22_7l~Hd(I{ zU3_)pI`e;`RA&0@Tca+*J*VWXwCArV!*Wql5-JKVbLH3Re|`M+epqDd|1$j+-%|l) zZtx$^k&$|WSMW$hE=_dy(>%c4ydX&07~D%1_GEjFCKn7_F#lIzVf;kp`xqT8mcn*k zDL?RUR!r6VgG6oqdB>(aym$K4uyO&Z&s8;8SF*0q9wb*+-hxTVl-)gCnlyrcDWbZ1~xkbe}U?)F&lgdje&L+K5d+&<7)iY zM!VCx-TSb6dnssU+gyz@xzM8+j=mh>{~RO@(ljhj+QSUvHhdpg6Lb3bAreaKOy$>?g$xGzMUG*zBD>dNV>pH|j&feKYn-3)*L*N0_3GoRh zN4?lRGpYU;+4SgvN~^+5T|4S2v>&bN(+nf#?ubWX&EaeDr}n*i8}AVxV?*9^%sc%T zih!llFzLjKXbfF;JOHF@)XQ?(qMtjU1;soPDyb?QB_OEMKUb4WKm?M8%}x$Q$%U3W zmT;uIJNh;N$RK*#R|jn5f*3Wl2rZsYg5~a-yKZA~hquW21WOU8OfN6wwMB`fDOwOJ z5doc5W1YW0Kxiux8sPMcWbs};2BongHKk3Vss*t|)%{BTEFtSLyrg;=B6nxqG{9mO zZ6brX**q^F8KjbUzE&j2DM@1V6&5cY#n^Up^D(4Hl zESN^GF?luk(CEyzOXqs}uLJa=PwZ|SQdX{0cK!4|)?6n{wkgcGEvdi;L{?CpjC=)I zMn}1wm7g}*=@R{flc~WjBOAFewI&-#2d7_pMNlB<^SUzjXHW`9AC28|7wxmIPpOwu z3qU=S30(DD3tv;4DHy>JhX+M==cJgrJ zR#72v_G~D^AiWp;<9W8MZaBm#8VK9|e%qX+Ub@F){XL^nvhRLhMw<&+E7@qig7-ng z&(rmnhcG)MaH~f8yQ9vQxpdY2LEQjD;lSXY7CG1Z8c~Q1-_?%k_!Cd{l>qrd2O34i zKiCty0U>)exa{B{|I%DmDYMwo0Vv)kPw{q$dHwb!(YQET`?jC(%{I7mWy>@CY1v|2?o2}F@0fD^+f+Q$u7B8LE|a_V!Sus zMO@IbwmDlXy*4q&HL$SkRZv)0f6jCjU9(Q=y~ajbYk|dpe-+)zAT#Gm{co6RvJK00 zoXXO%Ap8q8nTy-U1>poo#+!XAPw^in7y6vYH5Z-bP=L5QCdc8vb& zi}b^bq6r)u)`RS_3L0Ha1DNZ^$pln*`|0g#-#!#y%%bvZYSivVAJ`OPWW5WoHf(?4 z>RGYait5vyW?ZMVnHHCL=mbuvAeFzScUv1b*XkarXIiBPOIM3nZ*V{V(@10&#!J3e-GsfQF9zs zH2JSR`}9ha@;*9|53r+K6z#*Aw>`~b{h0*9O_}IvhFP2Q&A#kSV_KX0>!KUJt;-r+ zcMn`$weygiV#l}BrPo9g&!^}oV0v8=P-02#feqy>7WjXfjxG=F)bI<|wcb|cE<{{V zTh3O!A(|;&_|zV`&Hc0d)wyRLcFH`*Q6tfndi5Uj@Wz5pqv?1b@S_w{e2$m^i5*fY z1#+yGs{ehzN|M41t-GlI*2K_6KC6quxj1?|)M$&ECkDY?@!N6%@*)Q!GqGGTPC-#oQ^!;q}H-V4^{?N-dxq>mu3 znkawzlmh_>pZ&aWy6NgCHy)VO4{m%0j~mdvzn6J`_J|;TxK-J7)YihTmpBH{#s&VK z5x%(%p%7ek2grZa0L@#+h6*l`4ZVr^eNXQRNfyTt`6BX9Jb3 z--=|=F+B_bd&zmIM8wyJk?9bf8Xi|Ugjw%1C4oH1WGN-b5<8ncz@4;z$SH3&4*L2$ zU|O~MH4Vu-i7XqR1)PzRdB)8?=2M|sAYU&R>>1-UNpqZ_0(VzUfgv`pPo_>my~|c2 zlwyvHKL3+_>}?1>)Ddq2RMA&~*a15{dy|LvG@t$Rfi^nOm#awU4LPu$d+#2vp%~ML zw96qj^{Npgl`L{Bs28<&hIK&BY0@K{45scFPgdXof%7sxBdXR%3|5?F@&nEQUG5i2 zKA3Vw#y#6IJk-EahGIt_od8yj&Zk|S&QTAQLiQryo^elI-9`MzIKZO804ind9M$$bit-&h zxkE2jgoWx3$>!5B8NE7ARpNtOBxt?{vMOG&w5vH7*qIlQ__UyxMG%5+OS$~hJ-Y`SRQaO;rnZ8%*6bd z4W|vXaV|#}^F+%M5$51rW_6@gGp7SMAvG|aR^a?;hs}|=s!QpGSOC_!ByL7P<=Dl0 z&z+IFG}P9}I_Y}sUBr(fg>xqk*N|Muy19TYvB$*AR@COcPn1U{n2vUFZK|JEr7)=~ z*KM6;`(Li-9KDI7{dW-Bl0n0k+V0*8zi|srCi)Eq3=cYMJ-yk2?Q9twt%rh#p!?0Z z(DA@PDVk6QRf1Ls;?siy`wq3bs;p|5wJ}Agf-2`D*u9CXsTk3 z#8&=HdQpzsJ7#Htf_~g`tNW?h{7KRa{HSP1`;?-M*52n!&iM1~C*h(I+}^4;UGjBB z^%5+eN<09<=?%VCtj`9>2GVsD#oGQ3>&Pe@W`I9dUejxA3O(SQ3_qN2?Of^JYr#eo zQWT6&zBIx}iwit5UODdyb-lblc=7BjgefrhbY_6%8e-#qIpEVvXJL~5QXh%G=MZ;@ z&{P)J1xVz-vSev%Bf+g>EZ0VR7lCsBl#83-e}DcC__x>tk$m68O2C=G0|KB=1}Shh zmqGwIg$z2i+Btt;bX`_HGc*?zE?9d6g@oc@9@Gchy_fcfBXSEVYFWB1{mS_^`m9*% z4HDnzf>5KCOS%3Qt=zg8`QMVXfi7}ksi_Loe^O>}O(@@1Km))CM>wOm*n;(o%`Z?mUG9l14K1!xE{z;Y- zTj_5|mY>t^pp3+X+nVA~+}lqELU8~UJ9_ZSHT`vY9X)d5hSS;~Hm7+)T2pq}&qMo4 zP3VfhY9m0}q)(dgY#pKS{Cqz)z1J_PHDfunwtJ;}1ED|q@=K%akH5~>mYjvwd#kws z2@I*WvE;us>hw_`NAd>8zTa{y$_2?!eq<*&kI5E|ZwiF6{|-1u+pkDK;h*UH`f6`2 z*($Jrj^{{#-kGy105YsDpl2qC@-)DkddljlYXS6w7ncGUq9S1rH31I;KN5QKlr=qj zh8emL#=W6d@!Z}ER|wyzF58xqy0~7@`Wq6d9o@k1)pR3hj>Xs|$fD40Sw4h6_I&%1 z-DGy(p%q%tPIf`f*UV2H`p4Xx6ajO~PbN~~bAO(PSIPX6QBsq;_B%~JW7XhU8~qtg z_nDK;IlE7km;$pSM|sSA-m?I&W)5VhH!pAOe-+(=-#P_BF~e znROeXEswAF%Tcp6pSTJc&lm3512$8W>#!g@Hqd0@1Z%UJgJEg#K1S2O?)VQLq0gPe%e=cT1^riQu!_AC!?frUxf{wfCNlP~gVZu?=CbO#Rc>Q&Kr-PHfEqMN z5{LB=Vlb7x!-c)`B7GJJB56!1yeOa9-rh^UPx zOwQey`PDl6;ooP_a}eTbYk38MR@ z>7WS-GU=PHHF!n({({J*HFF*fj_JdMuN?%NRCKJxSA~{IFl1@Ri&)O;{-34Wkb{i~ zt`p!iz4i1CqJkF**(Pq8DjQCWKuC6E{#Av!a;Njj*5@`g9+P%`{`-ir1MBD0lZO7+ z*gZh{?|R>fOb^IS|NC|s%g~No zjmJe%KMqyijl)fv>L{X;@AT*0QobZaG!W(VVL7eTk2so%SJH3+L7hNMZ&B3(K5Pfk}gKx<~5i-d&4CM3t~}TM;4|nyQYpxTZ@I zdtJjluMqcZ!+C&QaJ2GhrN!>iISPSpkT_#z@;%$}yLVS+&c_YqXGwXNS))Y|{1_HYe+qSQ z9li5KDEB+=qrH-*oF=W%tdy%v0-5odmQ<#Dg%9=HA0}F84Px|J6Jdia> zPkymoygl~T2#8ivAz09tIaHMxP=y(Vx82m^wW>+lpmKp|Kh}Z>ZO;_|5#W@mByf$`N8}S(sX{FtLI9{~~U=+DG{ue(!7_G zXrIhdb1Kxma)A-%QR0zKW z=TuB1egBL=N%0?50YH#byL<1(&m0O5{QUaA-@m(}-k71@e3b>*P`iI@yc(DMdhpxc zBHkJ#0w6vFVXO;npw`1rnXdZB2bc)0EuOe&d-W6B@P*mEE@}qXJ%30>Fi(dPsiArL zchk{Bs-d4i4holsH`w~Ab4iX$wup`^iG5AX3e~2KffAN((`sF{#i-M$mfSDRx@@zG z;cDGhskNF?eTs9vNM!S9vvG5-^9^#URc$V@b*7`%azAKI)v%wKL7l z>UGhfU^_c>JsRC`KeVV_>4b)!uh`*_#IOdweffj9YKTF1>eNlN>1xNIT;HP|ht^}S zCGuMndMLJ{qrn{9ZWcx*9S;)ns-$)X%Ff>D;#)tG(J=~(m)@7<#ar$+suK6Dq5$G7aP2pJ#9j`y9sm^vL|mnMZ#+@n z7JSQ=A#L%Q0MxtZ38ZgQP7E8s;9&qT=eI5Go!5{gY9UAC$Dk}yP3-GCSL^Nce%Avp zC<nfPP8t8MZ!@?onWqrzVw{Ba|~HJCCP;OuO*Mw&~&J>-|}qZAMW8i;!#! zJCh+2$a9 z${5~#dw`5C@ndP?l8PxE4R-k!2)8hKp7d1-NpsIk?W8bC?|4t>&71)?#STI7F1D{2 zgQ!RPmt3WG+-=?yh`3Yfn(z>@cG3sor`xdleBf{wCQNLDSBV#!lC_;>cxP|TEn8<> zK3okIX4#0`q`Z{5*BBvJNJS!KQMxiYD@VFjmmyZfp5am>KXi1}Z9>~kAqyT*QZTL? zn$i=<1|m8InSe7TtwROI>&m+ zCPFbe%lKB22udQvsB{Af3&uf^jcri}7hX+fl-5$P^l0^HnAoKOgpzO)yNtN@wvuJ1 zslJip)idnS7Aa-Wo*JiN`G%8W1Tg9iPmI@Ly-BQLRV@vug|(Ep<}rp~pK{cX1ga`r zrd}qaMzu3I5+=0qxKZGIFACzm7_m;!m!6vF|-C;D9PW9!!7qjV#s7h3*#^ooLBdTHz1k zhYEvL*lk8-A$uZ_LRI{hn}gNn(Tm+}o{G>WkzBZ=m?iq6l5kOs`{=L}mwitU|GXaM zJn}BTL!fP$lp`r8>2bB=Zlk|#3ABQRxW`2&-^vt~$*de9x$wZyr$H=;=or#?b zKj9@HiQ5^n_h;QvEDnR}fV4OXx@6h9;>$mjn#yfw?0v6(n^QS&!|7|xB*tld#F|~c z`udgjk;c>rJ!XXbu!VZSZLF_|SAVvQE_F_R* zefQ^K&uYqKALaKWG^??1{iCe@7xt2lYsF<)x{<_8xS(|${HN$W>kCL_D=}%3Ke9l5GJSO zR*JAIsh-^WSZf}x`q*r2=z{J1RAM3-ufBgxDyP^~uc*#dyK36u*0!K6^Rw#V0R{KR zuww0wrG&#dG29M@y2%|a?eo7zy0dEp7dd*aA01r0exwds@0X5`t_hQ_n$uD z)r%Yp5p~k5QEcAa@Tbj*Wx&A(Rf``3rPdzYU%(M}^&W?pf{$xq<6?XXT|u$jJ37an zdyN&Jc5AJWZt?$;JI?<800W_np~q@CokX(XF}y!;Jp?H3N51}`=wxe(s_01I3AScC z_N_|vbIF_KC;K$2qRvl-^whs(76XKTAYbm^q1YS!2*bUTPYiYDl{bqHoH}*w*Mp~n zvLlr@KM(}39}0YE%Cst>I83TyBgR4bB99_t7y|ry!n5;pK)=!nlgA0PyM>Ktu_nYu zolqMK-LJTQ+YsF+F`?iLK+#ff{*FHqLv&uRJ6k?$r|qR_hZVo^)%5#A4IQ%p=5ne= zYZcl}_h=4>GwpE2qQ2Q6%VMA&z;alfN{VdS-Mn<}kM?n#3Ch z&$~1?QxkvaY0QSZ!vxNDNU@St!q~2Zn=knWFC4(LD`<&1(fpxVSYx4rhqU9#HBiwj zJWox>WN-AzAv(IvF~x3B|5pt<5^!tDMKrbd67|+H?f^jVotV3(VKg8vsJv3`XyO5SDYgdVdEsqKEwSXkBPL};X>rO@Y*;{ zr^b+MniD>2`1`E^mC;?cX5{VqYlL4YH?=MYX@}jw&oEw#? z`qYXHfq8#vo#BZj0*D~nQOM#gHc$y;BN|C#1=A3vt)*~2z`XW7SIs0V^Ml)>Va6aJ z`oA~^A(u-!Ee(HC|rWFseqKZ2Jy z@!3&Vg-5$oK*LdI5d6D4b?SwpIVb>09 z-njbHb|sp4a-=Z>3Rce>pBcNYn*!-*=crI2$<^f@dny_rmL&k@?;+ePA}i2Dol{0p@F83soTTm9K( zB5r^Dx{{-EwQ%1t6y17-jvG+H+r2=+ZS~=s`m6gyg%t*R4Ryeo6{1_|5Ow=QTjY=f z?l^2C9lT_t2x)nf+9Ce8#3jS1>C;N%fP=?@nVh}pPfXBitxIT7*%98@<}YbCeD8nM zh+0h)|NX477=HBj*IxBc0RI}GL)TE3i#janL?TjxL)UHTwLey>N6AK$?GGbu&gSN) zSU+i<6ED`(@~P1!oWB_}5gYV*%JcH29Vn!@80&Un6LF#<$M|n4jW(m{Mz|N^N+7a zOAj?y$+(2gK$yZK0er<(?OcOe$bg7}c5OFp4<@T#MXIyFvt6{0UV+d3`+8DCodk4!jpT;=|Kf>M=3Sf5x_l4lY6yw0}MCH6^B5!fwJMqyx z^?#Qf?A1mvYYzLfJpsPdAgCT-Eq#ZK1g!&yP+*nak8p20qr3Jd1H{s88c>mj4PH9} zFMJkgo}XR>1AvtWfZ`;el!@RoiAugDgCAtZ6i}>j(qER2Kj*~4l#Mou z3&U}fU?Vt_cA{sj7d!S$E#S^8HZ3~RPtA1H;Zvu&y|6}hv<9?17F3S+3MDkdVu#`R zi{rm|mp#l<6J|sU>`a<}e9ohe9n!4W8Qav#s1f|43|th}O#Yf*aneS^?dQ2f_hlpP&D z#52a64$;UJVsSrYKuxhEI0alY;MPtC%NugfsB@vjl(TeaWMRBazAC4=RgPB;&F4-6 z^gf!=A>lh~XBIpE(aIx%<&)JOw-Ni|MM!_5tbRpVOVIXVv-XYFDu3@kUh+(F)v!QKvBqi8N|9J>%A`;RN0uXpXLo_%t7 z&3bI@JCaitOOi8`og(q>&jYrpiHF5P+z)=jjRY-2&l?%yImA*A{dP4!vifT~J)n?= zjG^{x^}$h$*lt>1NMDy{p&4uD{kZFkPY!;bGpbkS()CFf8zETgGa<-jbld2Dol*9Jd;2KIIG^5p#}>+ z-k0NH-g3jAJp!B)NMpH%(m?D7oYVmI&hXUa{ob`)(wSTYCm9GGphX^Yz4ymoIZA|< zV0OU>X8q3d;N7(fN%d}P-hn<`xUPDLZUaF0chE}eHQnDfZE3NW`LuBNZg|4Onx~_- z-)X};`}QSoj^@b@ag9g`;~|@mq=0oVxY_BP@BWt_>Wm%yY!@4RdqB$k=S7L1RAI>@ z3!u>xT6#9~^4u8y_V=w_yBkKj&F|pa&%+y=`y_z+66W{j&ihEnrb@WRX+D~sx)J(& zI!mo>HMJcl)W+5OkEhTRA_qF2_VFsl$o}8<_KQ>Z^r_s7K^KXl#zdnC*hJ@%GBr@e zAd8m08h*nW&op7!gNV2?a@bQ0B|WWPzV^LcalCooDK}_exTo32S({r&o{8piVTE5h z+m=Nl*F<7IdR*`=|9&cX#U9vzt@>bSBePj0&tk|CuD9jQG{{(Yyb1-~si>EPbYQKN z2g247WE0Z`t<`JVjW4}9))&TQFMgh7In<^ujVCqwo@T#Uo#O=y|N2sS@cM1lAJ*d6 zFZFGglF`SkcNcY$$wK}eUeX<6jS~4E5)67Mezq#Lk zIY6~YVt<8Tw)I^wxnJVe)SGnmcpH&bo*cEp=fl30r8_SiGY!I*U;Mr5@b;_0djJvs zteB{!_nMBDw}D3DSk|`fW+lcwx=|Q8FyW>zx70*Nrxcr;7B>Qvo$HLmpQ+0hH$h>S zfLIwGoLmT*FfV)A8?kur)+LRAcW##Hf0XEH70pk}v)e+_KPuiZ3x}^(c!nH2<0_b1 zqq^LW4QaD}hd}I9S&0Ay?wl(1hZh7n;kwgtdeJ>YuA3g0B_e5r9G;rvq(jz#6S-No z1qbQ={3dr7zTE5X(X(QEQ#^KkX!-V`LuI3C!qt+qB#BbITrp5=9Uv|ch@t3fotjAs zK|*`*5>d4%;kQ;Pd(AyF1U zK_^D_!O*t8kFZbiJfaw2kjX9GAoQVen2R_daA6`_<8-iK`XmG=EQKTOmF?f-NQD|S zVMzM=#DfbYL6FUF(C79jN8B;`dFZ>fZ$|{ZY8ycdPu0p-vyTCVw`-`ha!4bO3H((g zWHw7-7nptFGYc&2N~eV+9rHC8(AAX~kKD?Z**xjA8Bnd7QM`$Sv(j7kqN8e1__Txk z=X9EjtVv?8t2~sVS^z-pJs5yrC1&Y_AQ?iqv(`+tva$+X-)0H-mOk|IWGLC|fi$T?lMR^#scqYDrQ|zV9wQtIF4=+w0+@ zJ#*oKe&1XVx3mnpc(r6KX6he~FtznLRuCh7q*=Zu3!ai;TXwX)tp0`hQy!O0NY>+l zBQC6&(!t_;@yXB(yEcHbB%A&Q@fzRnZ5a1v(|qAB%oK(}*glLs0% z0gG7}V0y>3=X#!s?jzhce5!MtZL5BW5riYm=baYjoJzc~Wf({^P(l?ZUD%!15Tq~3{ZTyk&hmZx$oBk=>x@6rfNJKi zorP>V)N0j+U%p*kU^Y7&W@3hoNg2l2S?^y!pTkZKqQ_aghlMmYw-y-EcZQ>#ueq!^ zr8(;zU#eBR@XI3FMeo5|0D#y|I0XO?)Y(bZuGCtPcr8jv$X8PF5ZJZw=BC4L1&h#|S&by9bWEe+5K?>K&fo7XXVfms ze(owM^M71;-M1lHPn97YMyUn`8&HOhKuS+i<&;MpsIw==H}S2HSH=B%Iz)nL-)V4w zNC0Tn?U4))i06u6Mf?~F_YE(-7;wNOIWf`sg54_JB)pco_w)`KNLSoJ)ypF?t5C?X1Ld(JYXHp zztFRYL+l?W!8ZF@xoP+lOCh${O{Rh7{B60t7z{3yq0(q0>akU0pElfkC9;$-tLB{6 zb+&4*Z^f#(&hA81K+asUd7a9}EtHg}0|tN>qN2V?UMFU%Y3(4~H9d-XJc#-_KnSG? zhU*X8NbkuxQGjaDwZvgPtEn7|<3JBKZCOt^38v+3Bc%4A=+bC|s)ClYs?JeFh2Tet zK$IW^p33py&+Mx(vC-`2^kMVco7{J`mbLOwbUc>}GG#q8kNMMw>9f4tl)9{wCFt{; z0NvcIzohj)4inxiCtA4pVIrUEeCp@!@v z+by$BQ%ek!ygCcr=e-C6jx9jNG#Lu{i3)~GAI{)I76OR~oZG zzY!a6!Cc?w9x02ET5+eIYc*Wu~n7 zVSTuhkY@2EF@c-Hr`BD`^I(WN{AI2$Cse8yWfuj-v&ZiCB~}E^8UHBEC#2RO{CH|8 z!4@jV8iKBU)~743g5HY+m7o6$FQ&=O7FX7B$STus&unUHeD+Z9>nDrhOeJLjy-4Od z%6H`qhZ#t_~4md`b>j0k2mH_^)Cq^Jk#K#jq z*>Qjyk$_#74^pvN%HsTNlI0tf%e}8V+jFPDQF^gin*wAChpD8HoOPv&UJ%5t1*sR- zN(MsA_PR*Rcw?=;Z2; zS{CR=9W1gKtXGk1UuDJFX2bOc)^NJ0lZ{770f!Pp!PlSKIGo9QFW>rEHDd+sYSJO8 zGH<7D)|;c4BGEIja02H;Eqy0k4c)6dD!p0qzVQezY99AhWR371$p#!uYtYuo;%DN+ zr-J?DRIkP0hmPhOLW9O|60;35;_6h{-+wcW&W^Y~z~r|uU)vvrWB_66Ddmp?)%{A> z@^_=GzX6amaQ|#KHBLz%sY;s_Rpme>A~)nlC|N>YU@`5uQSx$6;tliGi;TuIKJpT@ z+(}En^k4n$Uo}r35MpA6z0N;lH|ZiE)GLhm5A9{yLIc%sk}j$Y}?cnxbROfV+2h%m5kZoeRG-fOY2xUmC`M z#wRHv^jX{&mqtmI;`VH3|II1xcy`@4db)EtxU04vN_7{OSRADY~s)<`Xx)3w;?P@G#slt}Q!V5^2@x)_1r;@gx;xoIbC4HPofSZ+9av zo$~vPHdlBfYCZF7Xr_-K=#dYm9qBhHtQ2MW)N5=%)G1ng#}2R^&fzi79CCR4GMD~$ zU4;&X1K$}?L6V?%n1?g~it;V*IT6UP)dquMd zV>H=YCSKX#HLBD1wKT92g~H2$|qRwo;jR#yGxYOAAOxD zoL!#IzL9;}Pi%*Gw1I@I;KQ0oLhLrMenQ+=snW|{L*n9L z?J(kW5nv0b23L>01Vv~hpPr>4OC1QzOm=+JHPtBwnM;-K1J>8}82qKvem27F$ zyfFaVFF4(~#g?F>;;52O(qJ47L{3|D7x(BX%<;3UmyaD!zh)}j$`wvt7b~HFx(Mid zTuUE-$UQz=7vNgO14hwe8ekBgMBwenv~!m;A4`=c*PcmtBy_Jx_AW|Za5&rk-p#5# z_S7`slqyP}6l8(RyVfWC8Ku1nW%=3&J(`t%my&nH)BQQ^K)aF7qn{%AT-c(ts5iIZ zwbA+XUJNu_RxyzRG$D1o!eD5+7@iO;Rd3gN9kZnh)FXoK;PpXmfPeR()>KGaJK`IW zwm}AMSzHX|7li&lM=_yCMMX)-M-oFE^i+JARH$rgtCVhN22EY8X6S1ePgfU-8QW^T zRCaOjmU1?7>LPBoC$FPZzL%^K2!)Y35f@6-lktf`X0(UR8isJ#3n~;$>;kq!`9SB6&6NN9#ik(GJ z3z5yV<5#~&2O`QGyVEP43t#67zbJ&Ow}G$mUG^AtekhO+AJPqVwG=*6qmF3jBFk=+ z-)m5%ACIzJD7Q(DEqX=RZQ^LQzpswiZ14bCTZZHg)q_0%u!(iiH+51iZSX>_M*v=0 z1%Rj;InhLxzRQ;`q>3#`>J+-joG^@^Ai--APk3`fU#(vKnMOHfa}Z5E&%pytxR`W0 z@HJfT(@Ts53iwq9GoS(!;{z8tP|&*YpAKjMRbGIDhzSy9>*V_qtJHn~R5C;RrVFNs zH;)NIaTM-k5iVj1HM6i;4VlvP_+3<<7LONY{+x9!!2)UpL zo4#bg6y8Fc?C)3hUBd0}^g%E9*p>+WbHPV`=la~U&a6ea8^*5}3YSn}_ejxJxEne} z8d14uSwq6!{Wtd{Im?}Dt-wd<;t=%>$bD2;JC)du+Pk%CTuT%2lb1qlxLoLu&UZ-9 zdwJPWxNMm(TwO1m4uG`{0I_Fb`ZO0Ee`qBbQW<9534yhvL_oDj1se2JN<*#rnHx{g z>6;SP=qr<{x$M)DMbg$_s$=tZ_5O`&T~84;BK!qU>OyXO77?8POS*t7a+i9q8^B7X zN|B_w^A2p0&SA}a3BzGkYX_z{0yzxh zR5n6`NAdh0)H#B@Zq7p4%P2i|htK z3w{qdt~c?E&!^dtuD-d-iG8r~Gofl9P=fg6=4;GbO*t}kI6FV2V&D0qkP8MB@Q-qZ zpXN93_v-#;h%^#qd#lMGngEaAq@XGRgZqf}g#fESqBF6_$~=53iM|i6JbCu;qM_}Q zf9qz`wcCFb9ZwZbpz&C(aFg`ZSMwr*c4eDyw}16U1aI9AEE6gz6iTFsuW^N|riEMh z!pqe7{}qaTB>`H7)FacaT)3`TeO8&t10XuIu?r>i3BjgxxaYOLa)IA)gQpS^1(eCz zGeXr_rz8DP_Xl93IH3xfa~b5gR$RKqpUVs`Vg!dslc>p^IfKfB?#2NiF{`IX0Wcgu zSSBTzN`uF7A@&_PS1=N1(#$kJ3>2LV1Wj=+BH9gguTUy;w=?-<#eC<*V_J{(heH&0yme5JUkxL-EhN?lQ}&<8i0pt zD+^hT&ClPmuDyL)V9#CW24(qvX%l}|d2}maeCxO9`nuSzJj!Ym(BKhZ#U@S{_;Bov zB>mb@QTKtZ1E4-pm{eQ`w03V_@%Cr_ zvQw>ve^wKozRt(z`pF7%?oh#obZ8VW1~o4%^9{%)A_O9!J$`#J*UCY{oVR6mBWfb< zSEh5%bgNL!@BJv2`n)@K@P{J6k^)TR%2Z8@CEo-khun*yOtKR9FWSTBGbaUM_lQKH zXg8fZH5Knw_k^y`s}u@;U2d)vxsYCz_;pBVw_#XhJ>j+H`J1`AkDJcl$OAs?ev$b0 z1qKK4yLJBPt)-(Empe{B!4SKDC5x@3iuG1s+a2inKZ?%%k;(V}<2!G5U}l)}Hp86f zkmS@hWRA@tNn#{9)!V6(PCL<@j|rVd2uULqCGRyp={QIFj>(`4P(s(9qL$^P5~fAMktpD_2#{rx`FySqsQTjG}u`D z7{p8hteV9@_KjX~?M7(55g8!T#Nfk9RotR9qqMOx3=Be>ol6YV9$n7F!ic9}y1BUM z-OUS)wW5}BGfY+b)NMBhY0=Mv=Kpym`=j(}h)1LH9?giHN9)~9JNy_>W#Y!B5glP- z=JP_g{|*3bOU#L!+lU-9JY)g4|J$!D1z$j!?v=uagoO2?d1*oqBHNYTkwzj$$J{U)v zL~6}$+*cQCs}r>;tYo;77Bm6=-yBLK$i+le=ZS`;@z$oOeBy4sM^kgVd|I#ArSf4# z0mkL7!C#4)0HksYCXoi{ar|R1q7xO>$nqw3!d3Ub|-h z`&Dh8+TVqf&0vSQ(7lv>*IBb4AVLY^ln2_M?P!FBmf3;L<*~5=k#%f_FI3qIQLh-y z%B|dSk24(e{qekc4k1@@f8Wp~nT4*e6L;_L0%<$^xER6>?-Ca4`#WLzT?_6cln^n+ z2Rc74wYP{LT>fGWx+jx8M0RY|lmBh_zZQv#4W}*kU4sZx506 z{KuYy`4IJm*tV;{_tTVPNFwWm3WV2!n0U?L66MM$n-J_;?rN6+6qXJ(He-v|f^W+q zu3|KSb^y!T#CTX(vRvu@aB z?0>_dyQdg2a{qrH2PmJ!@EnRwJhyRfbnt#@(6I>K9y1Qt~xp*h?Pj87Y4)0QsL@+VXK|CtNGGV&I2k{t~EdU^|f#CLF zG;BHCy(K^a;O-~3MY+o-7Nzh|C}dF(L^14@rE~#w7kMCJ7N7t|NS%r4@f?6&(m*&s zKY!S_m&DVY<-^p)pFH%eJYV7<0`nB*)0rs0J|&^_{pCC4Lw50Y(Np zRfFX8OZCfUfmbfhlMEW0k}~Tm8!A?wT@lSMSKn}QG%7X^AfO-+hsm5K`#&B;>&I*3 zbRb3w(B)D7PUkc})3<*_!$0<9T8&VOO|APNg1DPQLyo@I9!O8qN$KckJq6Han=rD4N?2phX_ z25KEES*1adYz6$ccNZY|pPSPf)sEPez4=(CzB?C73jqp{2kd~%{D5`#q7um4VzJma zYyW81N$vRQWthP*yx1shZQ~)h<*x+8^&+S$mNv3@N_+)_JI^Y#QSu1x{! z$7&a#>6zHr_QIj|^gl(O#`jx`hG^xz76WEo_zf;9txTxKB!ydnZRTcfji6_Tiz!A% zVse&)hOaZ^pVH=~QRd5^>4g3G1J#O?8dLt`7xa$+YE?N&nR@6SBCg-7eV}K4{)w09 zpq6omxb(De_e#k(mUtFJPneS(H1oWeD<$3K}NK^OmE+LMmNRK0j@yE zNbaVj74jPZfyyLB&gP&Af6tcgHCGF{fiN6X07xuZ#Vzu44mc3y!?@xtONtLcj*|05 zUH{lz;|)W;E)pL8coYUiRc0Xq6?RdsDPLt*8roIK#jWwwPOxNG=LP5-LuZ_f<4KXh zJA+bh;@!nh^d_HPb{BEcEKL{f>7JJ28`l)W>lCO(VyoQ?a79rWL|Znx$|E3RJhG<+ z)z7Vw?sb}n3WU$-)>{GbA}<}3{sRk=JyVE=Hr_y{EBO~@SX0a_Mm)fy zJWK-@s<{mCaH!BIXiL#E!hDcj`TIR@!*hd!<^g81V8`^BJZm%)X@%`$1#sK>c$b}< zhZ+EjVxoa!*(VJG<5DH8hHAD%K1v}mJwyEJUsd)}9-fp|J8UuXyHuTOd|XcFB)|B| zQnkKw{j&g$Yss>y*l!?3P9gT0(v1#j;A(Fswx1^WnA>N0W}7Fd8<2GmJ1gI3q!TgQ zQVy9_I0cdH|2<@R{#M;0?(Mi2P-W;wk+M{EVaf~m99K3Lu*3R$a1J~m`R1reLg;Ds z;~nO6@O%}ih0ZC=zkg_%r@fh5&69k|GrsvLquqE+EXl&UGApGQtMHN7fT{QA_Oy}Z zq6iAdaaS$Rdv}BH1*x65Xq$Qdwn>!b2^9j8ZUy2p)SB2CnK<5AM}R;DZnViBSNZZ> zuD&_upM6+;iUrq}AMNY}^3ybDBX*yh1%FQ|)M=i}bC4{#dQ*gy!<9lel@aG+W#8F` zg}cB>I^3gxX-cTqCAT-TL@0nh{Mewv5z%2CBv-zbBcEE`gEfj)a<_F$$CVMG`Vu9E z(LnSlwRY4tqkl$Z;?e=|!upkvtU0QTz=`hvb?9#*5F3tSai6 zL%#e7pPk{{fXPFd?gjPU*ZxG74=H6`_hHHiLEYSRUjPC*dY<+NYId~V^QSnyl(G&z za~jACvrJYworI!E$I*^GyL^FWwU!l?FCy&TvgJEht#2I9?byW6!e5lsM50ob;_(IM zFE6Ix{>I7iJOe?sM9}DCF6Y!Ptc%oBNrYN+ElYV8!UD4=twnxy2!)U<>mj zH}-bGv2&X4bm$+GmGcp|7cEz_AN{h%&OUDA1JtpBe}7$KKW0UlU#a)Ht&-Y^j%%eM z)8E7lFxC1ZX;lXp&CVKm<|otH!WPC(_@TFcuVykhCr_T$%zDQROjuBDPCu)ev4UQj z#GYNQbnY?ztZrvrU0xkO=_lx|gPzqBuMz<4H#Z7!D9o+ePP8Y*k>U~$T;FF+J0 zEXymwOi9kCHt<0U@WD)Iqw^-@02Sym3aTBlvs@>8v7w-C1CyEGb$|;1iW!vw(-pUzEOge@=$nS^Zi7C?6|FkGe zg>(0W`+r{h1lS}(q|&JYx^mBX)ncP*sRhp75No$h5l2#rXivem-2PO*6VQ^9(B(*I zNSg#NQ!nUy2&*4~&uy!stjHYHMr^LIB*qRTF4woZMHwy&3UYT5L9J4|&uivK0w4|r zV9t;F+p$(x-LX)2ta&fku|S9#Fn0P~fa$8(st!27f?#C}ZiBtE@rCV17a}J!Xazz> zMUhLjN$&-m{$SCttO`C3^c=tb(Ak6CQ{hsfAu};u-=G+jLF{;2Mto)LM*f7q(0jtv z-kpaQfr7HP7_IZ!@$k3Pdi(dA?Uw=%#3@nfh?RR;AM7$s#XQp`Q00J4Nl<3|cx^R( z)2W2LgM?6iRf)gExmi+H;xPyIJH8`vy7O`>us9Vkqoy67oN0mu2OzRHyZ?ne1#=2Z zrLihl1eiv5ZcRx46O-MVN=qw3b#Y7o%anZ2+>X*!#Xi!3h;ksQXlW4fbU1O`j-;Ji#SWM2b`H{s#?NwU_t2wq3ZF%>ZgLM zk6KDMxTS^pf6LWK6bMAD*305=VAIcqxYw%Z%fcn5M%RM?z>3HWn^f4YHg|89?#bbzK^#j5X*&+Nj_G7#cq+h#VDE7>&9Bgf4WU6?>Bpq~6fj)$4VS zqB+<&?16it?r^ubqs_h7|Ec;Od0js^D` zcz(85m?kPaCMsJV0J9P9Ig#X)a|#%2cZqNQ4mN^`h0aB6>iJ@!y#N8~z+m&edsG_u z03D(L6+|L3#B=v-05G9zkT(FfOAHO>K$TBrM2?pEMDz9ZdL}?mjexkO7!OAT4JI1| z4GL(~GBsaV*L{<7%C|kUT4~3v`!C?MfJF=k&o-c@bK?aE{B85c9WdEG4Z!yaa;KFQ zt1Vr`bGad(Zy+TXg|@*(Y;dwz)Z?I)-RAF-2-{~n{U{u?lH_`6cG%@{*3&hc>j30u z%WFrTW{Jh-VzK_)Oy0cGj(>0l_Z%}3(n}!=z~}@(v=j=YgDigRwZIB?OQFs5bW$TV zY!uD@VcthAwQsv}2k@|MUH!MN_|&)oe|j?q5GQ%e=F$fKy;D%yF3IM`X8#7FrR%m2 z*%op+xe=z%(YUji2|8~=wd9`a%TcM?XVsZD)pJiVai`9Tyv`m(G>FxWH0)Y9REF|4 zGP|t{;c8Ujo4@)%tzsK08wB=caf764vaDQ-k?|#8n2e^=BEw8ZFzsxuZCOy@E5X6K zuvB*X!8Uge){>J7Q?Gz<4_TYE0er`=kAAw2DuDd9s7v{iO&is9RQ?$Is0V6a%ktajMe&BrW3td{%(D~f}ME>gM0yOCZ+14`*^ zVD}%GCcP;P5b+KfaggXZskn*Q5~Q!43XJo(n|v>8@u}QV^7c{==-0gdrMhz!G`Tj+ z%?qhFqka7M)aI!_45^gnD%D?`I%-y$YBynM&lHtS=?A z)O15>19<|;5lR2vYvJ@f)9~>)h zunMzU-|dp>?mk(C-$^7&>N@0Z6#YgVp`wUnHztKyXf44ySHuV zu06mr0fb-+({f)tv3P(_^%^So^eqFr4=Zg+-MypEaasw#*p1jDzA>hosYU=N-f~@O z&l)w!YLeXC-R0%F#eDwJr4qu9>3BJPDfdb!*G$|bmcKi1(OFCliRNag(D_@B?zijA z*f*OVH=6z&(0l@I+>WTD5o}`y@6y%*EvK>^7%wMU| z<|qAZ2>00pT&fft`q|(aT~cct%(TruSXmw<*ux3!5wsu4&V8sEMHB*Zr0aw zV4KGDp=6?92Uo#mvT`qg|DGej6oC4jhp0fDUNk@N=ZhfDp=H=41`uuf_uT4bA{S=wy}?_HPTXJwPg{CV|Nf(gUo?$U!{69HRa?5f zEk@`{)4Kiuk;C@VT;~TqQX-d`EjDLXu&)KSpECv1Fc%AJsa^r}~b*1ZPwt!84lxclZQ{ez@I6%*XKm!OEGr(G^5foJmmY(x{+S2Ds%Plln#j%Kp?UAp1?$n{MW!Va}?B8;Nu3rGhfvWgF z+;7{S{r8kh;jE^7*@c(O;eqS8xYFmTs}BpLEve|@-|eba$(Ryi%Tm&d)SOqYPxxrM zlAq$NmbG%>Vx**8?`g}Ipz?IKAQcL>Te|lCh)Oa3YsK;WpV7Oo74K%U_-RnV=`Dq` z-`E9Hge&bwOaXk1EjW+#A(z*Ei}XuZN50_!R4UQICZt`mYB%&svK*XMU=Kk@KzeS#{3_jyl-GjvGgD1a>q* z+L1x6^+(UH14bEtgO~`LpPGOxg6z7d3r6_BA274Yr~jSSTiJUqGwj;Rh4t%8$EwQi zOebcNM_;a~BL_oLZ+d64*E1Ej^L3>F(LbAyzR3N2$i;o1X3cK?&4+M_pFD_)l(Epq z!TiWl)4eZJ_ubbY+06$*Wfr6a3m^|7cES?Hb3JDv>OQff$^Vl_V8Xjkwpo3Wr?Q)D zV~GHe&<9I7W<}H zuW~0nS|AU!-Se|ZWA$Ls6^Em%&%H|qUfEj~N02OwDRTU-8XVfJfF6Kj3L`Q>O51=0 zrrgq~FZVd^-AEbA<**k5y&S~dUk=P+P071o3IZ4cHTdzwJ0~PSkcLR*=}<}Y5=4kW znVO`<54^AUH?Yx2QGG#nN0C+THmY_P?Wdn(?y9hEE_5DD4=$CL7lz=)m8SgSK z0l@Ymn){0OafreaB@VU*I~z1uea^vzSX1N?0f;IKF_Q|)y=aPZ3SI%_ausnq`1(rw zOcEe4H?iDeg^NoMSn*8OMwU1X@yo;P7Ey*XZJz`ck`(HBS>|;VE;mAs6IZ z4!?`#tE{s7{Ewu}J)^-j_~7#x z0eaQ$G)tL>=^fvK4?E6O^~s9{`Zbe_;vHuC((&Cpl+~RE#A!Y zF&WA9b;i;bwwMROEu~B>o2GJ~1-O;H5qx$hw7x*WB$e_(fM#%q72B5qQx^Gk6`BW1 zz}dFEJPAwqak?RmXD12QAxba|mh3D>YierKhC*0&V z*}JE6VT=PpF8pqqIPj)ldheR+!No2xN3GOc++uo{?z;EbpfkC(5OKc^j2&Eu!X;d& zakcT5ItGMn#DURD=J(aKt9BNR7J{&3ql$3P8fr0Qs6gMvH+rv0Nwd0DG{bHidQU>g z^Gu9Hsx-oz>}%M6A?p=$YY%UvH}ophz=Is=RN^CdGBtbQA~42I(v=~TiJ>l@jayfH zoAE1|XR2}|8W4fySHZT_)nAJV`b>US@?32H%OG0u-c^Cw1W_-Asn%RJhjs}k@c7v|ZhW>;&4 zE!lCwPqCPv-M${aNVXo&nmOi9KyVw*H9{;}$?onU7PyjVj^5ggySJAm4s?=x>eh6o z_5vvD>wgVrLN?NMB%iK>u&ah~8`AB4*hBVb{?wApJOcX2~Xu?tp>=k#cy zQ`~3(23*CuOO7PvC^-0DoNvs5+W!#8L|6zeFkzu`qVxlwT+f0E^y^CH3aKHRBsHom zQEJl&fY&SaExY(;QC5X(iaVXj>ewER#~Pme(<_ds7}LtL4&*4r-y4KkLe8j_T5 zC8Wc*U5AbkT5tczNd&NN?HpUXRrw{zJ2o&om;-Lzap`zre!!}(PM3;xhrF2K_7;F0 zE~rT8r@APYg&XX4fnwitTnk2D<|edm)jTC2^wM_NbHo`tqKJv)q8h5zT}q&(yT`7I z@x`RQHF}97?!Mi0Dot9urE;*X@hwBApJ;F4qO0CB@k%GpUFJoJy?5^dl-bZ;x93l| z@7#lTTQrjDwhz-Wuf6z|-D)@O+p2V{NnWTJD6)9=Zs2l{veU=i#+yGb-Q2y!@XF4k zSqnTP`1OBDcCNJ11VKo$1LPE#6t|5)c-}vckemFeNijOZ54WlidG;mAv}!nF)Fm$|~vIG}v# zT#WYdd#&dqO?C4kDas-c(!0g4q>}=nO_e5F%fi(6COiT&Hswn5?rXkhU87iUd`gM_ z#ZSf*Wtt-v%7z`H#MQ^Y#Q(lXH<4!M$U7|RP}-Z+x! z5ZQMcR88IeM>2ZNqz??7aMqRpyN*eLXyt5zh8+oNXbnV>*!3tIDX-vhz`n99Rg%=R<^(rwh^ z(ZXkON?d7$R?ssDeceS%912oeBR;bt3e^L|AZJ21>eAngT>YQfM}vyoTDYbLnRq!; z2M4H>DuslySK%eg(3U4N^9{ndxywm+!+*U`%FnKKa@c*Wo$!v3v*%ru;AJ)Nw#k#bWmk@@k0grZ+Jv-z#N$xdp{Znag#4a zD?Gp}oXdG6%;{-F!>8CsG|N>GxG8s#{OujFCTbm%1(9{0$Z0GV&&LtHpNEiC=HaUE zUvV702^k~;?m5pE!?EzL?1HOqL4+>uI!)!_BhRTgXhW>EA#@qBgRW2pc*)98uLBaQ z&#OzN0!G)}tc?K$wv>V@31r zGFq_pxNI8+Dm=%u-ON;oX+#pFFSW{qK4tz6ud;T|_}5J+c}D~2jIv)Ygg<|tb}LG^ z13CQvvX}>iUAgJs8=>|fqT}J9J4-r#!@3T6={C_l|NQ_`*PsK6AZ#S@QM#1ba8WsE zlo?CJ`kqAvzl8_D&}z!)jWSrVYe}rcw1ZguNDK+oF_XP*SET1jI>C!c#zvC8y5VizS}YJYfuxdDd1m4gZ{QL^ zdm!RJxA0f{0z)yv*2jhx8@@Tx;0M< zuFcWyH*IwE^8*>PnJ?;rJzL0O83imf#>yJC`8doWfX-vPX=`Bt$5sHDlD3PJvtb_JC) z46Bt0c&S#<)gS3uUgE6a#-W`vFYKqXShE*cT!kM$UIZ=m+T;3yBM%CdL+EOrn;p9U zE%*MVLmgnK{^tDKqV&Gpbh8DXjxF}2(xb(7u3SNfG27cFxxqXpb^XW|#&6Jlv97;NOQl z_JLhY3_pm)=OCU`!+G4?eLTG}EGA$Y5TF8rmq_={ha;CrhZ-@3bv)x?E~$=3T9U!E zc-Kq?xdA=7UmoXUo$XD)lt`(*mYi~LsjMGkV57g^sWy^qq)2Rt%&5BJ(tUMA9wsXq zMo8Caz;#2N7R9$we+OkAPudi@&SS4x?Yk>8`iC>REmoqaROWR*@Rn3&X-rrDaDyF- z@4GQ+Lq6!BW$?94!>d*3DID@?6z=5i^!y?GFZ^1NRKe++(0ls7cq|0W1V&WgD;6*$D zrB?rRm4TCVKfh`%G2&$&d6{Swd$=~X>D}6}eC+0YRn(`dWcS^5NwFJW$<2kaF%A0b z&!VW-J&IKRd}nOqJ9b}NnZXQmsEqTYZ%NR99@cUGMjO+!%%7E`OU-#}+tT|l&~=;o zOsi2zt5M&b#t*@SY^GKjv-w(IVtP=k8UI0(A0%?G7l`o_*q(Or#B-N?P`5otpeA9! z-vLytfzHUfqd}veVM_pq1=ATLL|Z&z2`KFpe(#2834vO8lN{|mg{I{xtr=t6hI@+C z#?rmcA5qIjj^{EmcJ#A%E}{sdyb;B2cAB}?5^rcFuE26>=+jc(-=)q^RAK>x1_9@1 z@F+8&acc}A_5cBf0Q^}~Cy8Fa&w+0m4~(#$pCV#ZwM|um*>lZ+awa{iT3Y=$dof+K zU&KfB)?Xe0hOWfq3tyAD@8cAKMC}pPR_;{idxN}r)r2ZD@;h(`6V>q^Y#T-WR*VZAhFX@JH#c<|Fh4%;VA3l2@ zv~;x9xZ^UThbkz@U|4Qa_}T@$2btzk(oy7VULlX?S6Dsv_+wAr(m(i1nDTu)yR#2O zQ+9^;KR5;W$+Wa8{>K{;13dB3YPAdWaW!wH&asDQ-{x#2WmuL>w1Ysz)GX-RU2rSI zT@~TnrB}OPhxqX|&r-+KU%QQGxv3wi$2FFmjpoT!)64o3ed%^mSHJC zov0z7YaU_ODx#8O65n{d`M9WmT^qQC%9lHN+aOY)^L5EB9?;MYGd0sS(7k2-tVrvaO# z{e?F^iaP(n+?$FWF0s{6=GelzMl?y1TiPWbJ1O0O7q@&ZqY1R7N6&BN7cq{Q(~kTb ze@efHzWbOF0HmGzoo>)0_26IhZpEZXo~{p1%v{oNH#|50>Y4N`PBTV%L0X!${8{C4 zSvhY;7&C#>_dPy3m&zke$JkTIvA}o#OXQhVznIJ6&8_8uRK zAyc@4Ew8s8w*1p-wv#iHc9>v%zQyEC-`z=9dY-W+fN$U1twyM$P#AHOUL;8L`4MAo z_w|rHOw%Rq7|dJwB99WJoAM?asC)N;H?zLCcbhKiQqA;B}6`jwS z8#(zvJr`MxgrAvT8{qlIfB(uZjW7b-?4es*w)v4)6>V>Dx*AptDB2IdMsG|5!A;un z=wx{*NJGLikVKmdJn^1^oD*>EbFVfvdHmXdEaO&L6Odm75QLYm z%!hr9wKUwF-iKMZla{}iqj_S5xo;-jFhmnX(vX zM&iz_NKXWGyX)m2NGC47j zSn&Apz=(Vx=ckFBv!U}Ft+bHElg2#pu@mEYXZI?HMt|Lkj}OGpOmtxRqJ$y@p)GX9 z@CB&W!bD&KsG_$1ri^fr*e)==ww$Omt!{MoRzyZNBW}m-wFQtKyIaTQ#V4SPH5vTy z%d&ItY#GlBE1#p{8s23N-i3CkTjyM{FzUp+*uHyoJ{t5Jt#Ncco3U9O)f&Cg8V)uw z^h=o-F1%Wbj}=AeC7hehyKYb47eiY?3Sp3DCoZ7@2%)m&oCtb!X>^%F@P!QE#ug(H zNZV}Rr(}1Y4pf`o_*KjWAVBeN($?R09o~CC{=euGanGBs+)sUF)E0ScyxJlAvzJHf zNFNRg%b|k8^~c;*^rys;FN+FES4@1XB9{i2 z3^zL+%H7`|zrU)r=~({y_v6*?3h)BJpj20xzCNj|>;Q1zbj^c&pLEUxd`;-fv1(}J z*i3=y-gBp;R}-8u&gj`5aHLgpk8EJqCHuW6_-0~*8kt%8#K}sKRn>r)PpuM1WT*v{ zk&skbcoAvd6n(H<|3YK!rK6ps{<44Gaa}OZ0a(2Oq*xq5)GkY%BPbIlteZ;cj2pG z=XTo}IhwNVIuV@bxXvzE4)_-B%z!VlgXN*8l52{Y4U(?gfQYxFcX0MDiEHwQUP^!f z5Op%+#<_tP8RZ_2y3#egxkLf}s3>AUt*sj5u5%R|K6pSJ*)sUg>7VHynl4yZymAXf zGb_Y0yw1l0f_)3L2u&b*TRl0s2^_IiCTEH=VY`+zS#o+`h?8n|gJRuZ9F}elcATo` zruEE!R8VEX8qITxr7FGAg5EBnW>{P;3CEUSPRH05%-06Twi}*h+Ke*G*H7Hj-CZg4 z7zz56`=9I!a!TE9`)Nz+=H#!gqSdLGulE9H?!EStt&u<9gf4-yZX;mk0@9<~u(xL& zmO$n!9zu@x+q{bpR<$4ikVX&}VIu*eK=-+L3Qx2*fX5N5Ngp^AgI`z!Y~+3igYl(m zhAH#%nTclu=s=Jo`kKrmKGGizh1p@5`&ECFs9(g|U)#VA^xsqXGb&du-FUct>(dC! zqcG8Hh_k|nR8?*cY!h+S^hS*53G6eXupjP?VI+(?6C%cB@OI*%SgV(l6Z%z(Q&I6G zCMNqeOjkolM}98?VZBCKJ4=XA?&{uxC~mdx@o>e!Zbq?YbIvwaX5yP5A!35oW{pyu1cjI(eM?P90rkcdZZ>&cO7o z!g+)WW1uo=t74(o!jXR++*Y`5)cg`*s-8ury9Wnmg?2_geFVdDB|QCwS?%l1LcJu( zfP6nwZPFb8Oyg^cxm{HHm{hA9P@*kU5fev;ZcXFP-~1vmJ$yWnLl;Zq77xgrCrkS* zJ?L4RmZRX@J;VDy(HM!82;4Ns%l#n>urq;+5lsS7)l!?M6Q0N#9wxehjnJ#>PdB&r z1+TN5Z;rj&;z4yWDB0k-a3g|{7@x$PTRYF-xDiTEdrp2SMwE5UIVDIV9yipvN&*YB zwtpt8@qM)cBvbkZIhY4>5ku9e^Z;G92Js8AZjd3*ehJo}rfqTfu>)LW`xqK&TZMss zYR-R;?Ut8TD3{k-oBA;ww@E<<$~gZ7u$}eNt3gI+cYM{?Zq1-ocg^@DP2bpu^G$$E zwX&ilcC?V>_d=0}ol^!E>isX3er>${(>a$VzF9fPi>%3$YobJ1T+qm3cg)H74=(y2 z#3zKSQPu3@{D~_aF@mp^0Q%(q`ar(D3&8c>S02<22 z4_#IjSw@c(?8KH>?>qkEall<%#+QX0$JMcj;M=LLn_h{HdrY2;3;;xf5X1}<4@ zw%b8kNrzM=^N}S)2f{ac>g@HfYk|>ToQPZw#QAv;#QACw!Zl(O>`T;;l0DDYTAMW( zz#orZB__&c3V&D^)rnDiX1TYoguohJD(+>QF;zpVDER1CB8v_>T3)PIo^UC9q{jWl zCF4tFSU<1*Zg<76@hi7p+`5J?g{hUH(Ou z|8f{@k3xA%&Tsjq|0Q1E`yx#R1pZu_oc^UsI2e}slzbz(FhE1T1GQhm`~Q-?!bd=l z{geb~Ghr;;HOZxz1V{jYRKTuiCSpz8%|DRIYE$4-buWn&P-XMJF zzFUueSh>)=@N{Lraf~eDHPZjY`iH*$a=7V0l897NS z3G!!5ny#ApT>W8i#tzBTZ>W;V)%)j7ZRE{Ufc>~Gv(`C_@vGH1o;Jr!jS>#Vb}18X zVxyUI5k@LYeS^(Q=={xGH}?w$LIz?!-UL6}KbTnQKYLE1N?>)0E=UBVh( z*L^=Sls!=Qc)=*T7unc9l0D=THQ#sR-CEPU$&=Zv=x^MQqf`dE_sOphPfGf4{#|k;pv3Qvwq@)ONsGmKMC#&|~@oJK~VJq$?n zc}l_cvc^?R^^Gz#=or?OU>=d$FaG_P^ZTz<{r;f~Qg8LaC#rVy3L{-RHJL1lZ~+@?fYe`Fle>Z)2}rL=VyiTlEY`v`=p@TlMh(+akw3RMOH|=m)$SwkSN@SFx`7`GlVo}qhdTnG7)}+?M@90>Ido$0lP{wmUn4eP?{fjGF+_+GXmwrw zeYE_TaHZyuhal`UExatD0I__?!C6kl9py(v`ehgGFh!<8@LOZ#o1_+wa?1a9c@Vl( z`e(l#_Eg!uoD^!HZdsX9r!ZZGNq&ClmlN~YFFKUmRVI!rFHJl~gR-q_NM9Cw>3m#c zAx^$hx8wbE|3B!7S%psN&MFHEwMxN*B-c7d`33lQ+T!)QIC$;Bv{nwB!oURq5Ft|S z7(d)<45rrL%HadM>&V6`hfh}-p>dWZ;L;hRk-g7BkmV|}u4SLcFyuxJ7(M{j2jcu_ z3Tuo_nqDo7bf{gef*Q|mtvpDk_^ib&AEdc^y z6ck;=!Txb(EHr$WHX+;_&6$y>7j}dSia^1|Mj=5lf#OiH7wW_Z7*1&NKNM z&-(8JeRe5kfoMjmmLOLZ-r}$|C6e(u#)ugjDOR$ZQ{jp|{~5XV=aHO~?kyWnt6z_i ze(6fxB&c}S&13mal$7;JADI%4WWPrS=HjnLYT~b1V!E&sVnqZ46KI9&08AB`Rut@< z7G*w2$V42K^V5CCMx?_duA$lt{Z7ZBzfk^!!gosED`Xf@lKhEwWQsP&<{4mfhPXH_ zi6Pim;f0hmJ3W+aQVLD$k}s1-O#%=uwFW~5T{+#< zgabyx19OBAyJ>zwc%0MMvkMMcP6kLbvIj{i8oWGT{fWq*&{Hr$;6HJ&`q7jyGlium zdFpuU!%TS6=s5kY$}ll5=+l}*YO&v*z?8%XW}4{y ziR_iU4+Y~+kzclp{dPR~bYm&6Y2NIEAD;ESsi}AT$%llPhT7QINx9;Tn^7uxwHXq` z$I;6v*i)tM3h&h}S2EB-v}Hf~oiECcf$~cN+ja%WK!@Y4s%fOnU5y;?p1J#9suenk zsv}+79vEx5a8a71XY9xBrHsVsaAXJPE@JLmKi%gUSMi>dkQsw6xS3J~^dsGlQvjVW zjY-W&eBkpx6lhMFNlsKCZ3Xji9p98QivJ8?l+kkvk}==Evx$8qY{*wl>Vc%Lo$Lut zNBn0LdoCHp_q-EHNWG2!MMT!RZ4PJJDe|Df-Ae3nB~Ezk=AjcTkvgSvTN7woLZ7mV z#E2jE?eTpuR>5l5h(|Kn{*)`I?7r zk;glbAYUxQ{;BjlVLrozz|vsKYOuyT5^4YB(cvwf)XXqsb2-zjN?ZYj0C45fQppxT0im67M^-HPyIPp-Ik~HoP+7ERFhnfBh~y# z=+do<(NY1zHN%TTKyi4kmje<-$|d720tpxGm`5!6y)cWlYoDcK5I5AD(Are1YWBc$p6hc_nWVz z9DN%fQ5m?O)G3lus%HNNlQrhYv<^1F+pIPr8S%SPX3?BVK?%=6iFT@$*{wf9yvpCM z<>932_tpu}qrOySG8Cdqh59#`PaLWFMDL%zojDVZ9iXB^fI=oh$NehuPSjw4i`-!b zP9hFxfNfH^uBC@}#nag7_5q#+3Bj{Z1UU|hBM@CUk6YJW~%ZFGmNVq)b z{+*~sC#g8h?dV)+$Aa#6tlq=VA;>&ZD)-L_L$lH1nk$nu57g1DRF|ZxX#y=@hJWqs zQe5vs5$C?ed!NgjRp)TfcL1B<9B8bQ~|GtlRs~E}fp9SB4 zpuX8|RUl!_&KID5Vo%O0fVQYMZX|6IlhnQ|JDl??xu!5~T;#r+HCs|d&nT`mIAUL; zb(U9rmHzruc8Mb|$X^y1LZdzaRqS}`t+V?l9tOrQ8jB-Nzk}~JDyk$F^`w=XwVY4; z&&>>1S5=+P#f!>48>;C=i!FII1yMOjI&PAq?^p9Cmbo0F>jJ0Ow@kSPFQ7*_ipJMD z$hp&6PpooIxH19l;fO7vf{SNj(<|t_ODhMu*GFDAZ0$8_Dk6Qzd7kZ8DlW{3>7d5; zCdqrYRchRe{UvV-UfryEV`+xUJqWbOkUQydtTbZ|sB{^qJ$iVFJ2|SS)pgncI_o_A>58wNb20#ir!2`ro-0>psPsz@ z-#1o|G^BtIq$D#6b$6r1q$8WeA1#JtMAgowLG?u54F%G*O(OQ&56G9){F)i<=X2^r zAg;P2QF$B1Atc2cP65>1Jw4-_1XmvSgjTs7XLqST1k4cbzxE&o-6n#Y{A+j|Os$Fm z$O{fj2A5^Mj;IV`o~TTNHheH3FBfm$LOWv?J8pN!+Xc@5?0|^Zu#ENQq0+#(=~`sRPvMXBi{`WFG8^W>eemj86#~Zk z12pVYI*0y6mMla`>C|rS!TG)?vupgfLpjrpP4n?F-_Rd_tR2{}ul#QRQHYnV=n97S zMyv2iyg$WjwO2SNvU*e*Iv$iuJYG zgFICmo`#U2t{rwB3?_e)j(t(2R-7VJ4;djKZ?#Y9IC$o z!?UlJS*&9l49338ZtQCsvL=k3P-Cem)yx`pzCmfk1+&SEw(Z4o0ghsmNy>(#&e zPiFNnQ@S1a;Ce6N%KnM5{pq`wdnX@nA7!~pJl`*>_%o%W2sg~gbOKtIGXtkCLypX< zc?gajV}P#;a!4t)sXUZ9qAne5@ph!5JVsl}3(OnWmJw@M6>6g>T2d_`LWe;}-D{H% z!A+5`KR4(Q8E zg*}MWeB?bL5L26G`%bdp!L~4McbbGEQJ#}&Ks{Zlb{(gF`ugRF-rSEFzbeZUD=MYE zik$}FZ$@R*orX7+arvX?^iMRLkWKr#a;4&0n1bb#ucVUrs3$FB-`~VMK5HGW`1|a~ z@-udMgR+(quJ0!jmkAFgG{oN5T`9ZwXCwF<=|P27T8G4f>k8j@V7isc^musMm!4Oz zWePi}X`C?iV}VHyC?m&qo}cO*I9uT@PkS+-0`+Vgu|OqLnkM{NkfWdDx7L-tD#dx$ zS(AVd#+(_5H5t%BfB@%E)){WP$8Q|KoQytB;r%}HY35w&N2$+nHry`LA#tFhCG(s0 z=nJ*fcb^rAM{Aol$Ln8j_d|7R7#DneV;H6K;8eezAE%7}DZ@}RTD_BUoW!(Hm(AdckjI)poIJ&oh*1t5;HyH6 z76K89ipxupTJ$Cr)k5QotP#as%Pq@!I=4Bm70t&tRJjz?tvqh>DAQdonWh3in`(V4 zy42HlX4trrcKMC*)9Cz|vwJOfw8Lj7>)p%~P0}DRbk{A4J@_t>0e~UXee@y`er&*z zr|%g6Pnl>RJx>|KLW2~qO_X!D#0|FBoh&D(;1dQ@Bh69K$7Vh4-xxiSX7-jo2xw63 z-%H&0*M5xZxc!%jRtr4;8|AY@M1i*$nMRH(-6nm{(+%8kdg6JrIOUNCYesu zv(gDg(N7JusGd2fL-nR!I*TsYWC<%LACHIxQDYa01;LGM*$#<{hlJg3z(NU&m35iu zo9Qe!O(esf5_?Hgu#f;Q{6Rn{U4S_NiER74xO4^u4!mZ#21L|fc3D)rGIa6TA!)h1 zkb{?g)$dl!k8k|C({E>`s?5{&E?a$x$FlDJUEpR*yoQ?BGeor_1^_Q_?P7@fO86qA zfQ$58lFsvnL8y~B&k>RIyy37I-pmIv7nBZPg2fHy(Zob$be-{~J*jz$^a1SZ4O*szdPe~@v+xE4Mps0J+7t4>@v4X3S2Xz0*wZbBUQ=T7!V|rmG|?xoH06eu z`Sea&&PZP^IA-fRezN32N3hRqJKrHcjoKg{Zjk?d#YiN+yg_^blXI)pG`H(C6E(03 z4jZO2lcHPqNnPd1_L`nnSfil#(d;9us8FkUd-x5$lP+osvL{+q>YyqGWWoc2;{aUFTUC*ezJ$1!mpI4(x$}o*9Q{4a;wh{ z>BQ1oMyx!5h5Lg3FM$H1G0I3YGLS;ofyj}R^Nr;{fu2wG$sSZz)O2H7%dN7Fj!i;z z2J`GJ;#p6tXyEG`@1(|82PaWLwl{LC_qWfnz%5-x{!%7p`=Dqv*>#!ArIaqAaXL{;nX=&Ln5n6_r+vQ3p zP+1?#*hD$(9g_Lsi9JNImy*$m=X?=K^x4FlGL}`4pn56Lt)2SPAMt~;wZaY zxR0&|aJ2aK6yNJrvwF|!e#*CDZpu6c99%MU_hTlCVz#S~>+vAcx_5lj4pXH4 zDQL5dPu4-yGwA8+>@d2Dd`SjSsesD1Gd;8?t?I=WnD48_nwg{4f*OiErsb&AQpWX< zUzqPq3g5{_Kwt=mi=Df2D=}BGf~*BhFywG_CVk*v_a@H%)PQu23~@BP5} zWemY|mhS@fIj^-zYCRTU5-+K<=dV^kwGmOvZ!)j0AyTA_%+r6Af^fLnJQ{bo!biKZ zB4}-M{AE9}{2_z0PR$nmnV~A8E9a`qe~@goA`-Wa;K$Bw8AM$3VI)Yn7@FhPz@1ae zA$VZHWX75~Ia@5$hK{7XTbf+lid2D3by{CAjQz|20|`;k&n-Qyh~%Uzv-`$1>d3ryn|I@++mQrTYz{pzgigo>!$Sk zPcCM+W3(`r9?+J7wI z`L`HtxO-$RhZKniz?#_RwDM?hyVE7fKOlb;2L=AOH2P**%4f#Gp}sa}{D>?JHp7w@ ztP$lX^8k3$`VK#XCuK3u&>&xTG`n&>Pst4sXMPdTbA93A!I!5~0vs6*r%cUGhy?}N zW2=Rb--UOVmcyVQ1H~DU7Ekgwt-dl4Vl7sXZ>^x&=L06*N0vnrjc?voRfY0W-o=++ zpyojQaxQ6tu`O>OzkO(Y{9F8DS5><-iy)|L-Ec*+oFEEW+C6gi?YAscN{g!$n#PvM z?xUwKY}RjDOh!C%uRT6o(dnc%vHb~%86($ZlnsACT+Xw3Z>!_iwjQR=xTXQfS??|4 zLrB(#Ae%OBc0QEw-yd!s*}Sm_7Dt2a+9O7TWU<13y@g#7g1h-U^YVxkfmX{`oPJ;0 z<7vFUJw)VMe$Ix1SmPwf_8b) zZ;ejQ#&M*KvjGWe+sk5#G?<-3t;6Y0Dp>7DFu;FhoA66G~ZOL5u(}wkaAN1 z4i|hd8?k1nT)zeEpeiCG6I~rmVlRP%C?*xq1EtD}SC1U|-JvAPBf5ei<8;|*o~muX z>i8v9r)6_)HuAALX=u~SC-OvPwpZYEawR}~@txYUT(#6mH9BSoQirU2m;bK*px5<+ z_mavUG`CN)=mU{B$r7|G<<#c8J*t9JzbekS?C3-$&p1;~B?w_Ilc%;^Xd8ZXKp@1P zYW3UH%0&pn=cRt+iy1bG-od0bmTK9~r77QxGV76aSC10D2KUlMZF%U4`VxZ?iDjCW zJ}jzk`zw588gCS17cymIy_CL%M_8vs>z7BhmPSYMK+RmXdu4PPJ-Up$*PkOrSI&t8 z=43KasVVF@r#(sBWzA8IYh_iipL4b_;ZgLe$A8N_V6R zXgp}r&P+Qhe~%+PpMd}x9eZ2vR&&JUqYtzO0Mh}O)F*%uAdfTACTC{N;(`<-(|~<4 zr(c$s$@9TCsfcSx_yd2E?eysfailZX;g@No42s2!soHr}wYgxm)=8LvTeP^Hr}pX+ zoF+uoP!4`f3EDrP__0rM*FabR4c;sbN?eCk;zZ$7G#DV_XY45ufV$O9L;X7BLIIJ8 zU=kOWV8C^ubN@cs<4gtXQ^QTE;4dqO()%^=H+Rkfox(>E#!~0jHJ83ou{X5(ms7~5 z5`tw`iU8eB7u9VQ+>q`qDjUm>R+01x};taJr{pGpWz7s2p$cbRl_yWa0f) zq!l0%&|7VKw=G3PSHi@Fj&no^htiI)=!pkeTwq-8~mFYt)H*8G*ZNDvG_0Fn7hnGoC|Eo zOdIdZ?19O$-I-k;E#>az;vC_H*C;zpdLTz7q7^w)r(F8S=Yfkl}{nqZfC zn$c+w1v^Tq9f&NISZm zCH_|rsUe~|)-xRjj`zJ+^q{uJ#yT6SsJb@nw+C6+eNEzIlO|`cKCQ5*aP+GBMtb3| z_OuJW{4m=zk8vVOj%)I%PhmGtB+f0&V=qFPXTsq^j#3eK0uH?2NN_b) zPM*nsj!yiH4f(ub(molJyAoVOtxaq4c!o(DQzwlDlDb7KieF!SuwowJkQ~|h;7)Jy zQ#R7v0Y(tGhRsyH8*Bfde86%*;gFwx%$!0j-0cVrn#e;ea7Djqg*yXm@D}JF{@@)% z@DBcZEqp#3GSNLLc z7-NI$=utju6eB*$D4FM>rN<=5+G2}zQN;jBJ(n_bGra0y?L4}K#ao>9k}qL4d)Oh} z?V?tmz|e1-DRG;2!`D-?NC;o1E>o3+VleZ96j3d zZ&?cw<(2^R8UpTKSNg#^(mfB6=ZcOq9FJ0E&usvYQV9e=U`m0%S2}^f0Cc4oL`*t% zU}2m~qQyT0PW*6YJvBO}7F6h|a25q`20#*jX6jILbpVi?!o+9ku+@u&I_tAKlfiWe z#dg51lYZ{2KKoX$OpL>e)Ps{Bd$_=^yCe{OKoAdIuKeZ`5~g(4owdQE85^xyEx z0Q9t z9n@5nM}Qmm{z`CxK~|;!I!b$w0*x{1Xf^U05X=ad`tb6e>YfotD-EIMcf9R;{9eQV zrW{Mqa|{XjwTK^-sVAzS4W6h9rgMYAl9z`|+(^}2U>Qd=+H!4M7*$RZTAsJgjiCH!pX8x^G|ToIgJ@7?N$w@(0K{wm<2MP& znWvWx<0C%jowzZ0%m$&z6FoiLqT6WnXi4ns?Q_@c0R?T)p)rRwLcvb0(|S9t)^hUL zhhTktgXuUADo4-1I;_xT(fUi-m3|p`P2-N>oUvv0m8hUrc@Ux>y4$7GYf8#1PqlY= zm&K%M+r7J9GyW&aRiE=-D@Sn0>okbU!uiL&wJS_$0MX=qx#C}C{|Y)_*!D#LUHmQp zy~Gs_Wr%%Sw7JH=_jh5>K>_!_u9s^z8k^3*{xqv#B2e4FDide5EwtJ`T>t%{^+p#& z?S`fscKI3+rnzRfZ>o`UF*PU!fs%(`!!#IvY`DG7AU&wp{KZ0LiE6AzoT6ZjZbbAL zq$*yfTJ@ZD+rU9dd+h@yU4!Ctj^wFnzCOv7xW{$2@|N@sxlzVLTW#CU^fsVJ4AWbu z)4eX0S$;x8dF&aG^#0gl%4E*R=Jw+BhkW8NG*z~Od8ry35dPhfl}SkafRHzk={572lO8N(-ync_Na&?OuD$9%coZQQF%^ z9C(QbS0WM$P6p)=k$dj#E*1QgqlUG~ql3jz&$uE(vAwpX&m22XkNmt)6yH)R57(9e=$Uo#-}lTcR%C0t;I zV1nGZhkpFD{!?)XfGAn~{OSHT5ROtWwhqDzt#CwOFxUIzwgmr|6yqxiptDP7d*b>{ z2Z)gx-qtnhO_YvyN@N)i#{X)|oYcn4cm7~>YM+C1F`c3l7e_!A!vAg5nb8yZ2VxkV#(N~CSDfcM*zk;X1EuPCpzwI@Pr!K++&@ecS^Dn$)tk=5APwRt8Y4gq999BHm z6hzx`J--3V12y6VTedQfDR|z+qqKZ~y@M~sDE>Y+`cc_O#cuKI%2a?%h@#y*EGq5X zh+*_mIsoVafk;V-1*Na>4Jmn|a{gr|@5^pe>qEx3%iLQdcb4|@9!cU{mYQq1dXAU0 z<=NNQci^On+QBE#Q@Jn4O?U8~YhT~^rT!51MZNf~Xz}9a&4qyz|0%A&xpDkZw&sbS zW}lv2Il3L@EoV&^vQr*jD8k9bq4M?E_W|Xj_U6-^8T&3EtVe)@>2@?#>|5hEoC`BY zYFx0CNN_3H^_!;Rq&S$d1ciI)I56M^bP7N!RD!}#lQ#ejODbg$U4REN7_Rau62cs$ zI+u|vNy-pHf$|vwphn1kqhXZ_NYu&Y&e#9OiF4lf78<+j~GLoRY|PV3*QERDyTr}jTj=MX*kS2^`A zH~o;3;`=_V;@mJZZ!jr(Q_juBgHz7ZTn|Z8Qc9TQ_tH`?+@-SGEG=lc?348vayZ$( zSt7#I-C;h$j}s|lY@Lrn5TEvppsWta^16%-@whTdzkmde$5F zuIpKCOf-t)trtIf+k8G!yGLw$%V0s)+6Y$OY5ygPUYM7^Dq@85d;VM@&OEZTAf ztp{IJE6zBQpI4p1-@!Q?X5IW&W!`QU40+6G`-c?*~*9CMKZ?Z5fx|z|t1vJ!inxJ*n7Y(_= zojgbjq3FupB2ol9OdhSmo|Uv*HfVsOwN z4and@2khSK!2F>|Tqf6BD@)zv6JsP{ZA|v)Xq55Si9BoJLiVxJA1=+!?_|9nEIg_O znp*4xG^F$)-FX4?#n#2dz1GFa_X3DZI&Rvc4k3#NQp7XaV8@5619HUzh)P+i*Me$E ze&i-YjmLwDQ5@hElORd^gcQA00q$bgxXy)DsD;*)wm<2X4jJjOXS^S>&*@ell9m#o zGPSLfYTv9j!IDl9<|vC{;jHwtzEeHbRa=-MY2hdIj#0j{Gx{zomtPGVdyk4&E82zuSd#YCo^+*1A!=072*@lx)A^-=y^{6{nI4zRfjPeYT#ObykmIL zlyo$1srYE`fI@mp70I#>_A=>{NSyg@$tH@C+;dP!JAV;h&UGd`l-P-v$*KfxPs*7Q z9rSh#X9}f2xap!!oP*V3#xTD2;gONJ<;zjWmcgb|#Zp|VF^O0Tgt?ai5cig}=P$++ zUaY#5_II$w49<^r`HVUwH@YYk8$7qg^!dab({M_iY6?aGkVaqo(x3(`HC<(ovmv-8 zOrRQxKk9z}G^MaBgsn43gPNBL?7TMt{mPnX3*YBwFPbpvvHkZV1#&nSKARGqR{bJr z)LwXEhUz0s|_wyTvvK62N8L2IzI#VeZnJp5{TeB*y7twHAl2Th8)P-5iYq6(H{6(J0 zmSyDX*D^(q7wu;D$5*fLVp-1skRzyI#qZn-o0l6&@^^?VT#^*eXwN^XscJY8 zDdiT_k2_5ZJz{qh8A<7TH7TkVDz$VglCopp`UoHxY!iETms0dkEs)}JAHDR&0W4h> z^1Lt+WnQBYi}6h;TMDu}5;dsvXeHY*{;afACf_2Q8J-&p8pc1uIh1`|HY$1KeFqbt zd(<$kYUKHzJ3VK;);BS_vMV(~shrDteI@m-U;O22W<6q!T5DwbHEw;3FJYR3X(z-3 zd#dZq2}_%GT`L-<3NG{IF}c-m)8SSIG*E;xwBEa=B_YgMWn#y*h|$jW-|Y!nm6yIl z2ILU2;V9tQ9dYnQ!_yXCkvM!nQqXW)2O7;zlc~`gnOxO;tr6h5H^k6((AuKE-X@MTx6B6q@`LR&iI{hthGtAa~k5O!|q2 zQ-~E)596y5NDqveG!!nQaI7`Lz-v!74C+I{-?maqcK8C9dSGN z@W#Q{C*94{AB;LbtrV&FEKBlh8b94I*&QmTo~j>4#kQ1W)sSWA(h zbOaWo%z_e|%`;E5Or!nI2)VZp$5~fk@pH~mkv`|yfhkJwI`R_vaFC2-Ga8xm)v%EV zk*oyr$PBe?OPV3-45;950%W^*tNhXfg^OecEkDOr2E2{5GNx8sp4Z&73KZ+fOHeMB z?}4||nQGG@V_`iB0LI+5y0J>U^Rax53>7OP9LM^ zk!Z}iWl=LKOq7}n0Wb~dIrC(Q4oB8(btmDd63tLC1F)JL*j5~93?N(G1MYYsZoGIe)q{6@`~>NHB6G7qysWLs4Z(XKO}k1{n_4U62az5>$HB=h;A zZWof#mCxOW00wdMJ{`|KV8!ya2U>L1{it( zY^g{I=b1osf}PFKIP5@XQS|gL_vjNrS%1L$<26859M(D$Zw^eHzQ~K>ygXl1D9B;c zIj+IuOd`hx)6dz5JdQ!wO+xY4;mMOZN~a}WB-l3zYp;Q&!V>b8syTn%L8dfh6Ti+E z;Mn2_Rd$5>aB?E%6+7f$_;q%lAKWa+N{7pR%!gPR+zp`tn>_%X0wRQay*1JaI(g?F znCZTbE~7F$d!+A!E4hG5+5SOBhd3o8&&drGv&x=F0L{$7Qo;(zeC6^gfpZJGOOc#G-DQ@8~Y*}^Y2klY(icNIc-S>m+xGh!3ey~vwxP!~SbIh67 z34B%A)jm};jkuBaq%AJ3JI<)=5GE*CR;wSZf6qXDvH0LGZ z6x}bxOk;tS02rlI8qxD0qEy{Neekk+c2bE39l+VN%gds&3P=A-y9m0#fMR^GZ-A*m z?x{4;7dbu2PoP9FJU!lrp5ddu%DU%+m$G8lsMgpqar)tNL|A+zPxjby;{|6Mp#2+X>Y~jBd^4M-Cu}%@$zp!j%#v_pyxWGlTBO zd?qNE3*>Tv6uA#GB;>EXs2y`KchBL~d(@2V|8z=7vNz#~p6|=l?Bme?jx&&>Ajw;S zLr8gehCGsjJAPL__E@Q8N~swA{$Ey!g1bNraZU7L`*wdY3tL&L5(Kct_@oM%Q1aB- zXjZ9{Po=dxylkpOEm#D@(k15g%HIEH$qqJWVi|@RM)oRDr7_v2+BKgVjT2=TB>LU#@uueRoZs1M6CP*D@Wl~UiLb&kM-EIh)>V)B?5MsAo zf6}>o6lK9W0~CMm93|gIPUN_S2$d{B=sdG$2pkK5>dq4i$D{%$Ss^VfV{=)l${|r= zW7re$te@&v`ir;h(6Dlww@FoI22brB#J3k}?rzacqjrnaTD)J#-E;aX&c6(j?x{+A zA?n_0V#N_nloWk&fN^;L9^{6u_cG*gd~Pfah=AO$mh@E=GL;hMAT^9*H4N#|pbg2J z(lqSmVJbcWOUjoA@_YP$0u2_Cj;xaRAk-DLEL8UTBg&U+E3=ol- zf(qtKC+0VYs=b6jTXKmmF}U;vsBCcONL~RZAn^!D@*RY+Ccd{%Ki;h`ejT{uS~MBU zQ8-bCn^+drF`6xR~5mGQrgWzJ7A zc5uVVZftiqh*A8D_gRmA@nNxa)u3-WRt|*3_0Yuls<>$Js3Azyo5szSM`nO93%A@L zitNMV4X+Hs?zW1)i{c#!=6=sIv!cI6^VVKO7v+)inwLkV zQW#PU8cxl92hY4MA|^n;lrv-qH=#><#60Lu;cS$RRdYu@V&j&8rkMuBzj7h;U?#vY zEC^}^?7+|C!q%AuqxZ1}fL$#YnOi-CVh__yiJgZ~LE@O* zTp*LJ^E6WJ_+H)G1EEWGB~_efhOZ;+Lo6h$j7M{AM=xKnWKm@%V(0J0@?bm6=1fM8 z4ZFJ?$rPnY>5_AH_#(T~wC+_F8mxazThOutFh^_}-2o=z2B*EV>W5_@tW5ZS7=jUz z;S`xYo=bF(FRa>-{U}g%SRKr~#7RdF6$4huVOihULWk$be<=y@mc~;sn|5 zRM>XNAi;Y;g{&g_n26DA&4JOl*XwtPS>4BbUekHG;+Gi_9GHrl@7hL9@Oo*bOUviT z=0BeDpHMzn$0_gNfxO>=HsFDsx2csGl=~BstWa0o&&Z&5L?U3xqhSz0(U*{f5b%Y8Mpz z3<`LNiY6Iw-Hdvj%-8TK$z2a?WaB;W_<`?kKDc)9U}r4r*4Skb!RvqlPcQJ!cK6Ex z!)hr{y2+kNBfV7Ur~}|$?71tq(H;^>wsQHlj>v(aUgwJh0S^h=rNX50LK zP`w|$oL$SfF{K!=KdtIgpXsiG%_qYU;f+V0`r_6exlXNJI=`!YzCENS${48{dszYw zo!Ggj81JC$-y$D#zfC3Mv++SUv2<`q_^y9uEEWEHwb7FOl~C~fcQxuo(s@Rz;w!>r z(6P_=nP8l3evpKlJkv!#?#sb1!>b6T(#`T$;YRU&KD2lHZmJl70DCTk9tD3_@HzW} zb-ET%CIJ@do<|zcZTa)kn03JZpP#*7!GHXO_H-jfN(eP}hV$3(J;1Qaob8Zz-FX5z zS~v(S%q2r4m<)?Bge3N+M~L7eQG{&W=W|5_(#S1y6hIvf*s6ArW2FwqIV~*iC3oe$ zFBAI04a*%f^6MKv{PqD~(K9k-I9fp`f<`qv9_1dG`P%WLv%{9RmdL`;V_^lDSNp)X z(x8q>tt8s>Fkq565zywKO!??H01%PxN6wgNAMFuwYlp|qulcq3*CCr8=BfrA6u)y{ zH6iIyM9~>5%cnKx)1UnC_f#U;E&D${pbet1)eUU^Axt|AM~po8Adn20!9ipPM&vTs zOiwRqK>y!qtWu>qm?}J$mK~~b^q*o$f!EE$*|3kdFMM^0JFHl`y`gZC{`5;>x?T1o zmh@#NL!lH-dB=FjLI`eZL2VXCQzft-{j;!;FOgYZZfhU}XbIg0i z6gm&H&WSU-|E*hQOpes!^V&o4zp8o5yM~?=%Wo{(z^EPnAqMTtDfQ*q zg_|obZ*6duLY0O>zvg@uSMw{&ZKcGdw^03}DvB*!6Xq);nw^FUeCz${t}bZ#Wkk7n zn(d+8$oT411l9?sdYUgm7Kh%dZy-CS4s9hv3HMB_g$AN%D?f^ ztTHz0*jqW7b>a1pFW9>yRfoKe1AxC7oZTEuQ0nSrO1kbqw(6c}yIC8K^&IJ*~#{LuCVl!M2yjLehZPSi%Rp@gnl%Q_Uk8&Hy)I@pdvf zS4OvsI1J8`$t0D!1h}n0vN(MM5C<fSmFjP|9 zP?5*|2qg2$suASOp0k|jY`-JHu~nJ)eeu7>UF4Fl+=wkQZT}lWPwX5^kyD38O|QpY z4*teJc-sa%VJ!NE>8d3Uo;EaI2h42`r$%E0EC~zhE{52lguue1FQLLvuyAO|T|beH z_cmLIE| zCn=h8U9H?zDq%S%#+ypIfL<4jl7&5m%8+ zf&g8UPB3(+D={m(RQLRgbbUIm?2RIHq2mjsKe)v`*jyshL$1#hXMjz}{SlF=zQ3eZH7tJTkMHLYh z)wAXkgxlvulbi;so}|Au>lvFrsQ9B=NQR5$%M6-zmxvho!Nk3zbLFQ?#H?3=5*_PK zh}JYFy0zyf~=0-JhGUjO%j*?mq6-GOB)K4vRd6gGsaet zozAw)(4;GSKW|FpJ;RQR4nDv;#m=^JwuBJodS5N-J2_{NNi4n1`D$`QbI-zS!ktNp zZ({YhQW1#iJGlb=r+Hd$?l$>f@eRL1pOx?fIE$8JJ_K#Vhbs%1Ohnx#@IO~c4lOKa+;J@&<;j2`%_E~ae8e6DsRlVPAUWyBC<$Lx5z zFxwIoSqhhhz8#M|pR93Oe=iL@Rav_$0~-H1vrn4%4nVw>aG0LP!1PA%vOV(EQSLU5 zEA>Q4)s4dy*|&BQ(iJ%3@YHdarB21wu6i|tRAeyMH|OQk2jMPm=PnN5*tjQ+pk^in zXz3u`Enw^NJG;A1sG00{ZN6L3cPP;~v_zf(vYl_csX$vH*_G8f+XCE%*r90)PeA`B zSoWSW_L|#ip>wzbZ`(>|Vgpu`)ElYZRyCxY1t{4(@@+3^qgL<3g7t9{N_CDp=B=($ zjQ6@mQd-KMIqe+n#zrD4Fs(^}A!E=f|(5#gY`!R0we!kqs(>Vay|SKL%%m6|U** z)iGXblD<&&#P|4Axs)bg=5IXr!9$Rt(mAzvFr-t`gERDV28CCOgm!>!LSI*$A^Fg6 zBIa5Klq)~U3S`V3VRGEu72j~Ptx?LW-iq#&uLW)qY+?c7m3LAk9)ixnVcLJTinW$C z6V1t8k^HN}n4*ZR`altdT$Ey!4$!Qz!F*;l#32bDlte)l^-TKT`Bcqg#f{gpUP!ysw+H>cN}&LxKQ*-S+dVS zS+-O;fXY?|cB~FMUpEJsUHfijIT`O7(T6;o5!wD}%%j?9YQJr|(9wRH@N6fcQBgQD zBi3`~RZ#SNizE=KY{WA7b2fBHmI--kD?j`1Tb)VJoyS?9#kcG09x8qGIDUP9$U#wg z?k{{w7+{Z#13^A{y#G=k0Fdfv?7%Dz{@JK><;?fwU|CNQgmP-F<5_vN;6}%^r=Nmb z2+FU;NF%Vz_VKTEPIe71+~{a~{Iob*E*3~`Rfv?Da_;ZirhE?Meh_~_3cHh;^7&Bf zIdxNu?c=+f|G7G|Ggq@`DUWvBdQ^`80bQD9N?&FDsA-h&2DW%@%BdMZuR$Kjtx+EP zl|8x&1nFY-dll|D6;wnVb#|uDgZ*xr6j&Gtevk{=Vp@5z;609!mDGud++CCsR79>| zZ8$|F@2vkUT|~qfQ6j(PGHrUgZ0Z6^G7o>tWd;b6C6<%#ur&85_2umKk? zJ39N95$*!mA00+smC4Db3vyEqGoN;f}~I^Cfmv$u;?E z*|-srfcFt|?`=3oLUA1HQRTl6BSS+t=|u{#QM``y>l|Np^g}!`tVuRZ%b746?a+p_ z|4{EpP6w(-Qlx1BO9m1q-=|%VtemXCJdwg*Kq|Goke;5-T^;FaTOw)(k~LL0rv*4F z<+Ib%mBU4POL^FdvAPS4&ET`*fBJN|a`jnr2DJ?FUWeU~exoCJ)7H6N3E{iv9~l;m zQ+pjwym)pp8Fl*w+Zvif&3JXZe$Zl~?yLNE+245-t7gFB50ii(+Kci~2F`=dJC_p% z^7er8&>=$_*yBM+(thzZhF|=Hn^~UYC9^$)^J>q76yvj_X#>4$GK%Yd58q7go#h^+ z!5-hGiib|@4CAf5f-%WnvC%MjE)d^mk77y^TRd?@5QYzw;>gIPC}hmryO!r!Uj<6E zmZ-2<ANQJQp?Wo@+ zD9@xBn}t`@(I|G!fOK!a1GcP{uG;nocx_GGseV9A-czC-=<|cs#U2^%1DVF#>-f=b zo|XWTD8S17<2JAeip!3B$tq*PKb9cMl@ZRn^a(jT3* zV3h<$5C!MmnIZcD)wgEBpcrr;%X)O+5!KO*+>e#FS6T;%H>pGwL~7TMt(A$ct}fK8 zDRJV9ru_)tGPf@(h>6X%R51*~m_gOWq%h&^a-OF`(eOOYU?hZFiLrM>Kz26{5C{8QC{fnUbHCKa)w-hG$r9V2 zl-w9+5^J+j&pk}Y5&@70*SfIBgK|1|db)btj3+E~P~oxXP|&Ej)>i*5ip#0RAVt~= zvU|J9wcQ0{;T5M7EO3)ONDS>;_tY}TV|Pd23m7&d0c7|TeADR%@veZN{=V+>poKmg zYh_atM{^TLvtA%}r5xM~Ix!lrvmpRMo>nl5U(oWw>&znl(#h4(PsG0U@9?Cz3V~X+ z#ry!3+S-J--i(%*_(f%+wS7GOrj};0+>x<1wYMN% zcGPr$bW2#aS|Jz9uHteT6Zyioa!5pP086|av3sa#%M^C!LW z84GKEvQ>j!@!nNg+GCf`|Iz&H+&t3-K=HTN4u4Xw&v>(} z^GS4FtLcxodE0LC?f`T&;v9!r#9_XAIA)qt=d?a{tofYNh3ZCropl6c>qfQXXx*;* z5U0t#Hf<2Os6l=tJ4*rOJ~`e7c@C%ryJhuBukA58-N*W6_)iw<@E7H{A@1grZB3+p z0;$0_86(Uq)GZLutLaYDN8-Gn%HaO{(Jq6VlRgSH7@(uiZjmuSJ?bPZr+;MxQLo5m zk~8`Rk$peg%R^2~Jzxh7q=J0LXSa3K@E9O|ouVGJTkpy8n#hM4grGt=_4ZF>(Rv@m zv2+^qEV+U3q_^f|hT1Hf5K_QDa(5rmV)1?X8^%^Zh+;s6#CC&n%wqgog=aESbxW?~ zg$S!NZWDTS)|VcNTzffd23GggwVY^)afxjklA1lxb~WYG?BYJnX({sU(hHH4vrb|b zSC>H(Zs^TNb%@d~WRyulN6m^i(@$#&0Wc|)OG!cCdfyy=gY33=eb~KD@>F;|5p^eY ze7DB9ebxp6KWKf=fyk4V{*Q7e!in+l>?P}QkNAN%VFQ{B;CT6eY>do0+!_y zplOq-_=3^y~L| zfC*4(L9LvKW^sB9bFGF+x;)pVHj%6LNGtNxiktE;@|mCmO=EyEb)_;X=G7tkTtD6Z zs0w~(SrMD9F1D%Gz!x=6Pl`}aJ`t%sR+|-fv0c{vS>GE30*T{?l6^wHKu~p%&xmza zx1?L$1gNQH&h*y~7Cr9EDpD`<*-z{^Z(Ymmg!CS}aXtAI`8Cj4SWl` zOM$P;%C&Y`Ust=>`LN`Ja-(uWd}lL9kc7`0RhHz?e|B5P?Tast#kwwXB$ivw#SkrS zrpVD7H4U0|fp`fMhdF|II?%$vUW%0;^Z6Sk+wpj|JLOaZ>f`(CdoH%+C$DgIRxT8s z-7M|N2V&he3{JRto-u5?+APP&sN277DC^4JIy_^ryo?hWOMWTFzH*=zqVcSF2D!%K zobB*v!7q6@Z85%h0tZy$E+t1ql*O5}rVaqM%OrzzTDPrL-; zKNSnO`z}l3pqnE)R7zV~%s$1iHVr;Q#J<$`XKLu2#rk&;0<$E)vw+cWJ<^)IX<*+WkjyjMc6OTL|K_ydIqw z)}EiAD!GchHicDK$dgdoI_N99PTO1X>Jd_=@1R~^Ed_Orkmb1P?f_{wE}b(jeN{(k z{23gz8#7)L&E|Y-O14mvb{~ImT>~X^NL=cF44rp8)&C#G@8+8Kn%C^w zn`_Iw*WQGzjB92kD^aR@-HUtez3JMNO%js5l8_{+pOuj6l8Wk^pU30#@B6>c<2}yn zoabpc{BBNGJOH6w*za@*7_S(mzGIi5Z#netplTIg-%9{F7{#xR>NUO{*+1(r24cl6 z5@(7S?1lfvO_3D1D{uO*)u~D}dR)XMnwBg=4)|0rUNHFjHc$3)=d~E$;0;7|k&wtXJW}~If~X(${UA?Q)#?s?B%KPBBmiJq98&1_&#=) z187xrLtHD{<_+b7V&CI#j#dM_i_0>;d!8OYYH>g!m&) zQ3_6l8Gcz=8j@ZN<$=4jnT(xiZH$`HI0yde;IRK~u1I8R*<$U7&fuezYvqkwlgNnpt4>QUbBd`oeUH@UvgN_tl$oSbsa*;tRa&pATVY|+RcPprUNa!Xm zuqcjYEr6H7v-?xF(OmpTc5yF0Ft=!RTuqkDpSYUG_IFiqLh<;8>CD3L2ji)?x2q(} z#JR8lAe76VQQ#T}aZBjtF0r8?RZ&(-%w21BODwS10-9;e0We~=!^1b~OHdVil5+Tt zM6m6W@_9Sp2d(NXAIdI?VqF@wp9J5DvPz#==RhZRk5QPZ62gsD=l3BwU+VCf>HnEHy9{@F#iHNF@?2J)A*{;^5W?{8 zfGU-W1*!!75p8~S<9XDwCx@4nQL@_Z8F#YR?N>o99~6r5pnk%cIu;lc;5oeXW^*v})4HE}D7oOBEzYXGWEA!Jz>mdfVwPNmVn%G%OdatxwVwNUZ> zGTMN(Bx0)oD|wv7%d5#n9};xYb)T%=!bI^he^Krmg+arVvt53V&Zmgop_LkmAa#FZ zYoco-gS^rqbVKIxjs@J6RvCNq=pCw5W&FNZfKY$5v33am*-%ohl6GBdR?u%sqGe1c zypB)7hiD=k@80*Ru58dLrBrALwaa1fcWP89Y+HP*BF401sya>00g4L!0355(ou&J; zN;OYa@s#;95iZSo&cJzYES+64Mso6Nrc%Y?rXb)bo+0cKFZs^Ul4F~pTwCul$LO2x zO2*mNLq`E_xzF!z;X!mk^zyCa)hM*;)1SYc-_C#COkWLontNZ#zX+2U;s%rZkNHL5 zL6lrTU7(V>c~R%@NBkVw=!8UN!$C=kQuyqa`p9`~SMr3Pqut2+U_BW#PbAu7h?>JV zHdqfqD81^u*$&NsoVq0ViJq)!jAe>x&okPUM{`orCgP^H@}YDeMcO9L(Rl*=?GX){ z5RnF+^3zkMrBD;*`7TWOsfcfaczuhZ^Yc?xLP<8}u+B+YvQJjcdu--7!t7n|gvft? ziCOpJ#zhLdA7M|aPn8&bi zh3R7P3E|7ykh9WFg0_f z*zM4UGhwwV%*t?Pe1sn-_B}*LX|b^Dm;$Ew&M&byx*8A)7+1e**N?lqxS=@n#-Fum)jCvK|&e%w9Pz`jnZ}Bl5(>dM?%@tQcnr z9rrHTd8(!Yo%_&rJTtX}>Gpgi_c&!#)CEX0Hi{^&X({JMqO++uc_c zQQ#(H{e3$r)Do>SQK%t8WjrJELm%+MpR0^ij#3il?XLdNLc^$?Sh?Jq zFq7#OZq}wpxhz+LPW?VPFu5!U|IV%f=hrl5u$m35B>7(xn7L8zo?ow#y>_-5hrXf~ z6#8?wH0k8kWKaT-cVP&M&qB1{&^TayL4zIEhdz1kHp12pFx0TeQFX`Z#U5ILZ&`Lub~Ke)RAI$h++nY?CW3>Tf~-kOZ-?r=yNbsh zZJBuIxQHMhWRa^mrdSjrlHj({?(so|nA#cuKeS@zz{(TY{Oi@qPAxrT!;1x6tkgGW z5-HDAiiYe76Tg&a(`B$Fkrg}YepO9bW>cB{7oSWwoUC7}um*{i(MdL6Ph~VBnF#Q7 zzxN%P=Vv6Q7E=Vj5(JAf+;C#S+|;t%u$`g}ll99>*yl0qFU7HE#`e_IS&cHe+Y`~M z_cuVH`dCB7zpg7I;T_Ej6c}fwYple=e<5w?V~_7lYAiC+#%Fw2MD5zmmsEIq%t`Kv z!nM1v2XEob-Zj@1C2PRo!9tNI;@K*HRv`afHGia@EnxzWpT57Ibx&&x4LnU*dnQU3 z`s{R*f2Vi%fuB_26(@yQj?A#*Dz2mUM79yGJ96}+lgv{6qqOU`H_T>}l(|50Joul` z+7jK2;s0H>yo>KxcXAiCj^mdd1Teh24TpNuJRzlY^hy#aIR5aI#ZRHxOm3x`U??w# z*so2cfkW^Ln+yuJsTeJAmEbyDoKUs93HL(Z`3skumry(#xjJEPov^n4-(Qod+|23X z*! zka);HztEE{!+%2z{|For^2c_?jF>V-S@_L+3e@ZS_=!kg_pQBa%%%&NDe6DGUInNs?~D}}P8bWf{bF{uNke(HWJq@4to;{IZTXpytLqrvc2n#&{puP9 zQN1BJV)-PK2BM|$H;P1U6FFP2uT{Tf8wehpdLeOO6+J7?#Z`qDoe(psPvc5u*B4V7 zQ&zVpfP`!AE+%eNqFIkRylpXLuUGQhDRum##N(^-i$eh88n5~VMtih61OT09sPR(V zc94+k1OOj>a;eW7mBqR2N6M{@TsY0y?5Wm~3g5CH0INUwYF_-SP2twUhChQBd6H8X~qz+cF4=LxRD`AZj@XR=w+ z8fEh7P7yGE@^gmLo!4W5otRKFwdvQ+A~}*@u$wS!_zTuz$&f%Bb2YfYvdH5~Nw&g# z)8F;M=O-H4-<;zbWN?I03-WCf!}gM*w@UgI!D#9>rCI93+Cz>?Xe!^vm47`J4#C+e zq*%xLMlDsbjSNGq*I5m#*g?q*jh6m`=ZJLgjN8Jo@IX17@&&^=Gil&UmBYezaSeQR^2LgF}=9R!WBh(S}@d1T!usJy276cxqG$93!h;LR{2h~p^FoSeVyOqa9XT1c@^<-H9z?BD8Q6P zU!Qpyehl@(!KsudnHh1jd%R6JwP9di=W=<%rO>ukt!y!w{Ob1l6j!bJE4$Y{W5W0& zQ#Z~)GmI+gj33TxhLXI@(T<_0Ji_+42&dC0v;9rEx7 z929~%HX-#Chz?(#cBOOG5|d3;Q6y3_SyQ_Q?PK2cAhiSDZ~k{Fq^kSTdcU>7$hL-+(gtM5yio$WXv zZfG3w_(Kq5fT&ul_zjpT9>TH?DK^pH9{p-cFeo-w;3vu2Q-5J4WOg5E22N{!c&$A( ze9<`Xedk6~HA(E_0mU5)2;Ujk9vU`mb-DB7qw}ad{oGGJ&pL0CVBA)us9-jPwdL4msE^9jt%x|m~QXtEAVcl%lCWRY^Wbd1;v zSD;T~4jikpKliu<$l_G)(CkGtt;s!@JCJ5(hD(o`@z%2cFGyZ9SW$fs;;f+bu^ub@ zJUrV+7;BLi6^fKnR)G5R6gwxG4Y6wZ^K?!yIIJ)P;*6YESgk)NMdE0qzIw%wm(G&1 zjiBd-^=cjV$_7a~m7NutK<{DjjjC!wSaVIguRa$zZF~g70d{X*P;^g)@US&(=lyRH z?P$cL)GV>%UZWvB$|$|A6)L8o{hD=iNIfEH`+(gu1kGHmt=+XweIr^8S-*g~*(B7+ zGDFb4_$$2#@Am%Zz3^G*&JhhMZ*dm+mk_RGp z6Mi*1%qP=~By8f<*aLy(55zrH{4tVVz~;0E4Z7_S)Tx;?a}$E=3C47S7{iG$fetaGflX^%A$jxStfWc?KHe2SitA(_9(JH<^ zk_z4dR{K16RN==ved{Bo#8IB$^L6A+W%5U4@cB&O`u24CJ%z%1szm9W&ICIylOL`3 zCV5$TOO2ejVd;2o%AXF&jPJ6=0+^>yZsK^Hhs^}FNW7N>gssAyp8vpV9Z7$Iu>luc z`TtqAr@kMrL$*WJ{2)P4XL`D#%2wqdNcGH8Od-xw*|*jGRo=&4=Y*i|v7xs{UA9KU zZqZh>&JoLGu@CTYk=U)eICwa6&OvYso7X-&j8sh5V`xy2eEE zBDMV}CGSg27leI=>}bZ3p2hBZR2fkS6p4$u$(0Q{#Y6FB2=9_0-JR-#^2D7B0M-{< zFM3CO_tY=hl(p+8QToIrSeNd4qv76!;w*$vk5Pf=p9JrI>Azni^v8W&mb%{2jmNVW zK87tsR#Cig3)?|fHrFz0)!3YuE6$Sqg`J%hg+jSi_T-0?U!z@(nZ(1jLvw#eyi39% z*o_T-o~kLbT}>2nTXS~$Jo(|v9i@@0_fC8z{g?4!3F!?Fbp=o@f}gMU(M8K#HSZ%VT*G(zoOG4=60DqX;y;3A^O20&rtxie ztwXz^FoD3nIAtHnRAyy_&slG_2xsyIog;w7SJ5n}?tGoDS*@L#XbHu*zeL8}R=EUi z_=oSyb5PJH|3OR~(!KlseD2S59?I@RPNzHN>o*!hUTgK+HmBBPgqC0KcWz02%;Yu@ z|9v^vyoRZg#_;Hz7!>|r5w=WJE(V%u8Fw7K%5yb*>r>!9JGM!@- zq-MEhzp|*jP3b(&xFPa93krV`tdVyTX53vSRZZfO@ik6PN1G;2p?z+3hcA^z=VdRr zYf?q+Dh1iAe`_9pcx1)Xj^O?O-Vxa&$j&e>v@~zCt>O2J0t;PP@n@YK!nC8E%k86AR{Rscm~Q+As@~jk?H@@ zWEX*ZWwz_l4KU?P%KvDhX0hZbsr!+O_2R-`5!31tt~ySK617M9_fe;q%a`>bN0w;f z-vyb=PTX50&rlIR9+iNKK4;x2G^d&FVZMo+%Z-+<=d3^15RKcnHhY#Rd4?D8kv5Inog6$AY(a-_r;|COz>xjCjsOD zp5*7C?u2&;p5haw(co=4VI7@CbH5DrK~&}S?Zhq6?=I-Td0ZLfE5_o?>-k`?bot@n z|L}!wur)02;lNJmXO|h_e)+E8=FJIu+!N)rM`G4GBR{ybBxl>>uF19j+Iupb#xj{3 zF_Y%<_@wMfe?ZH(-lH6rXV?1l?@NuMwi0`~+fZ(A_)lgc%9gBb|O$iX86{pFKfMldHsH%|}C=!R(reLu?$zIS?%9ygNosqwNlB znBJM;74G4MEIDVYB{Okq?WZXlB|^qFV$0Fgi{3DzP;1HbmQi~(8KITQCPnlcK`9ha z^%Ss|ZoVqF{T=f}f2@|h=%JtERAi=$@fLT*ND?1+0jJgmh_@od6P+-W#+eZ1*fx~Z zK--Y9ySC`4-#&90Yr*YR%XHblO5)p;L!s76r^^kI=PsqZdV&>_tZOJ|K^DMMKjj@) z6|8f+c{{TvK}u~cE(7=#R6dtY5YTb2e!bEG=Lqu^b}f^fJ~mU_qcq%^j5kDX{1{rI zJ@gVJ7eTkg?u!bV`3-dN&4xLXVnkBzYlP0RBZ5T#(IrDaFR}8+)(qeLc)j|7<>lEw zm7R9vhv(jVvaC416+yD;I#6@lje#p4nlFl}vzr>ZR`}C!ZEVMjg&DL|`&0D#UB?iI z{LQq16_d7t7Bl^Q!?y`!HmN%{j6td)S^FAw{jA~H1pe~!6G$CYE7%Tkk+Qjnl9s#F z6dk2f{=qO2S;XQHu7xwJ#?X77zK)E!$Y-SHn#)<2&ry&PH(o;%nXW4*KMD2-?F__pS|8^Q#V&o69x8-RnF=|D8=2_V0tjap&KsF=~4S*S&IB-61zqw(` zzq#%{53xe^qjx;NZt4ygzhV_b+4L^jt4r3in;u{;aHORPM?ZSyxp6m?grti|*cCe}!r~uVfSr z|DCJtB((rYbKhRIZ$b4aCP$`OZ*pAFc*DseP8GS{?_k&%Q`ffYV&*Fd8J}v`w#%*P~ll9rwWaR`A&WFcKBF!9@ z5Aq!fTBF0H9Qhb8qg*~KvvH3k*ECWxn&;Tlln-%Zf8aha#p_H{)RtuZwuF!Rm!+z zAFA;K9it*^m|jA;|2<))PrMle@wHa%RNQU5_bTLcD9yuN06qK{{=eV31aEPAgMki# z?f8~7h|!ct3<;v~=M#fwJFttzd8J9}DVhfoZ*6G7NtY)-Bt(t>CvlDFd4TVy^4(C{v8C=ktRY`5`A_917Jm0J z{p5LZd~m8gt-zkPzd!JAm)V=GmGj=rLilv5*6%5UAP}d01oB@;qI?VuEQh+8>`cG| z18@OwYZvS_NdVXH`h`o)fm~k(eU3Z)#;&y7bVzEiRZ!duL-*V~&P&boBcLLKpWoc< zqi@MdunEPF0^YP=plj6oN8Fa*o{pz4%X*X9t%pqFr-|DMH7&eTWunzq3yLzYcOF%Opl)79My#w@h1-Ya*A7N8Is$1Kl*t|_#u z6(4!0{CGbs>duPtR7tqs^ohM{M)Hx0jdHnifZ-4QC7Q7uw21JijJ^5Q&zr|YMfkg| zfY=4Pk2l)Scz03odk6Wi1K4im(&t#uKOoh@2G3RQq_ZSt;JA{sU%bE%?B5*B*ym8GX2qbx7Ifzd~tbB)} zmh0rz1=wvDf5~vi(u8W9r1A2mQ!(RR&>QyrDG7yPjqA~8cNLN&EJrl*Pc#%S`8hg> zUw1?r!K7;ZqOzAb4GyKP7C@nafqCkd5j!$fW7=ZBINK0}`Xz15V~d$y%ZGWcFHl!& zx@E)&kWbjex-nw{*HMH*Jbv@w@-238MX5Z4Wr>6))PirPi~28i**LODry||U=BJ!2mxd< zy?urToZ)YSj)V))0I4G-+_938v11?7L~+E%0ep5nR*Kc38|n^qF$3M1Pm!QR-)#=^ zHQ=~Yprv7zNX7eIH>*p1|I3dxC6&)d+-V%)u?XGHmduRiRjCG)Yj;ITkMhfUgwkG+M5X#JgQjs>oZ>b_5)YUl_{D4QXcdUd+m zKR(riBf@pj#FE>^9eQV-0JJ6mckRX3G1Q_hj*krp$0#lCU!2@IuGB|??SAY-zPyq1 zmS&ok<(GMg9n9s`>DPG}#e*`38C|daj4^!^SRrodWm#Gl!$}ITq?MEipJc#JETo3& zW0qZu7h?<=SQYSy-fXzSc@DZGGt%tJtXzy=>c_ud)mc@$VDE;eVvW^&plGbA>B$7hPyWX{dwdy|J92J?AaF~;0r zcA}zRR&hY10xjdMGH<}a7yq{t3p&Q^UQ9j|Z|RB^Z0QJlRh#{B^JWc`C+Jn^Z)N+h zzMdih%`>vMLAmxXL?|$_eZ^HQGbH}sMX$Pr{O2Iy@OjGmF{5i&0&_daNd;)8LiLvc z{_hs`N(76vEabz!xFMi*Zc?23y_Ml1Qp?mdRK_pDu|R)9+{%#6^871A!met)D1+bk zw-f`&+DTak_wZ7xw3}j3RAt`Pb~(95-lv_sj=!1A-UPOgqyI`>y~2N$I&JZhu`?0c zc?tNmtg9~1>C963v&D~wx>i!m!)=#^sd}P2pE*9GWz4lEU;f>Qe1Cgv`;GE(3(`KP zZA^o{lbG$LZunfh;0T=&6W?U1g+0#m{M;SZ`0HkAU!_JJ8De5+))ZbTW#_&t=yekR z;zxFOeMQSmQ!oFenhsD=>`s&0>RZh1Y; z`bA7#mr?BHMH8!$>hL^`ZYyv>vC{`jwhy<=oI0VLXdptlaNE7#i316hYqK+DJ6e_U z=Nixt5%=PdGD@~cJSYq@HkHP)k>fL3AhN|4L?Wh)NJQOt(xC=(bN)$Op;=v8m1~c2 z%_ul47N;<$&>32~w6~-!KlT$$K9zEdLtyQLcgjMa%^dbjGMSJ$GKq%Lq^Tyv%Aa}q9G%Nv*AZOUbE z_&WKEnnARj>bFCP$weRFj%@+&wTBx$e@NuB?}|h@gA5lX_KV8wY7iN9G}r;gi~%BP zw{w7M1%EJ*5y3wTmrhJn{o<5?+XATNVa;40r|(rtA>vmDT6>roCi0zw-kGKPyW|TM%~#} zr_!f&QL_Vh_Hl%6ayRQGGgdNUXe0wDa%&Xm;~(1eJ6_+HNKp2%e$=8bq+dCkv5YtL4%KMRFA@ArDD(Z zu9r`293Uh5f+Hqcy{}-E?8hpH>2D>s>?IsMNTH4!9hi-HQT057f5X{7;g9eC_HvIF z6nP@R=^663D}?n2mma(rm!XArX@i zgi4;L?S)xZGsDH}(FK#VJ4f1g^35eUve%Fphugb|*ccWL+MP*&CR2~dJnc*bIe%F| z8K`4p%C*47#c;2nI_9TKw9PR1(>$Ut4!PcYFX;Unee7&Vg=^lWt3yHD=6Lp>0Ly-w z?2uSpm33WS60_G1xPgVWCD^h_wZ0q9deu3zIbk!Cx?EYCS+*EetLEzXpmZj^gfU^T z)q-1WP$tiz)b#!9c)f=ATCN}eM2n-&JLLdeSYSqn{1YNSM#bY*`OO`21^126Pr9oo zR5CqL$?EN;y#$<{>;0uK$$f|5pml@bVl3+YeZR^bgh|C8zOjQJN(#n?CM$7e50!p% zLEPpIIRR=$uYh-5Va$PJJyKzkBjH~~?avlQ3LBE;EkG})Z4c<>=ErBoo%`?Sug(Ld zKDg%gJ+k^h`aYqiC*gTc!9+u5-*GZ{f=$Yx90(vA5{$%;O%nd6Y0AdniGV6|nGAAe zOLU$?9VtOLLNxdQL3)tlgIQcY@_J}E2#`1|rCZ3hpls;2oSeL<4 zy7e!HBW~0DeV5_gC9y%mO957My_JXG1Ic_kb=%sbEKje0sgt7Jh%MCXD1Re$7lhn| z4J_!v0D@Z83!#%AJ#CeQ{ce5xZvXqQ;1@1~S3dFEf;b*Jn7v{TC5|;2+Y9-+bHDU{ zzYk6Y`*zFW`yOPM?r8jvl+j`y|Q8 z82DdqVnGe{tD_l(=0RtQzV1 z5YA0|ZRI*Chr69w@0PB0GsaXOq_AEZo~nI+Ge$pr`oSxrC;xNzI}Odv>22Lx2wRIg zdX~#4C0(>=eS4O!l1+Uy3zdq30Y2J1)Dy!)GL-q}N94`@4rU7S@faBoN|kz`#$Qy79~uy^xNs`My` zs-$8pF##f54ySGm6Q>U^S}Ahgf3XnZ$pikvojcWd#iL$lH6=?RGWLu1B^@>u|Az-} z-*JB;1OInVlzBqpq~BYWy2(-uQ>I^)BPv|JrL5JNMdkh()U*s_W;y7ZF=HAGF6f zD_=Thvo_zk6I~~z-xpDz=WjVGRhj%dxZ%F-tc(e_+cw;U1GaCGT;Z`#ho^@~S2GIY z@~bE!veOZae2c{vXwj?XktVz)G0M_B@Q_@Hbc1z=WJz+P2y=?IQzMI#%Ss1K#R#+r z0>59EBF_5R1exP)H36xeQP$`q@q^7VtlFZI90Et)DG?!B2R(_d4U8NjBr zxeer88cQiMF21GM_jj5R%4aj8#?Pl0St8)Gh?oF~RjweW$o3*PMpWqNbtw=gG^;AnpJ`-9Jf!Aw0nj% zl1kIol-aC##V#Yvg4eAx8XAJJ0I=PcKAOr46TLlG6&V}#DKXRVDzF}7Zb|8qT=69` zqA1vAYfNKm6JzxV9;7eMSeW8dh19@2I|%|p9S%zij@=w@qTTf8PSZQAIs6_bJ^IU3 z*bL3I_^djs$7|(kby_^6JbWyW;U;bx3fwrcE!OP$7dQMf`K|Nbb&cC0Wnn5olD38G zhexBApSXBYCHbTBZ}(^fzttJmy#D3#H?q;K0DXS&|E%t+|IOKb{VqCw(gW`6dhaW^ z!gp;P0E;Vpk_{;T_1^?QFq$;wJgl6P)a)-+gH2Dy6Gc%#b9IVaaypDd=(3@ibUA0? zNo_6fmxJpDdz_VLQJqq46_l`85|@F)aMAn~fznY~Z0P z`NQn89o^^~jo<)jQlaedxJo|!T7bO|Z>V9S6yrJr90)J#6j$rKgOqcNtCO2R$wrJb}Zy~W-u+1zg5QueQ5E1&z_Ko zqyXW_VozqlAz5+jT!#Xt50I&0QadC|VSZ|C9i$2jiovohmJvd+ z>kF@J4LPctGQ$03!Nvnlm-KeTE%e@&rXEdI(;{kW<@n36S~277DmKAZj{M1q;$tF} zIpS_<)%K0u;ygYUmf&F#;j2qE@}`6|+Jj*xw#^(N;-0Aum}(UZ$$gl6cul1mz;qY$ z3jJctTug+~ww5eaYEBhQ86iOO4Z()o`>69_7*TwAW^h1xMgA{|W?MgUUK1(+sUnt6R z#|`or6Oi+kLFPU!4;SC z&?pgBEA+HQC!K)V4+2tD^C4seSg}BcF}U~}yRUS5btj{p3Z7wEwpo$r;LJ_`Od$wdV{%MLV-cJP zB8RDAK&T~yxpmbJRqmt5(*%qx3ia0LrQRc|{3%oHYB|}|ke;E0hC?1tQ3Awkuo@F5GjVIp zMIgeY{{AO2OfO!hB;l5-*DR=~xkO?<-V>$+Ep2GRy;n%_lv)KitlUWLpyp81g_wm( z1Qs#F`WGp@A2fC+O^?vUZjE5|LM$t4ep%tV+pI_7&^c0nLV$Oesx$n`mVDSHFws7( z2zqBbWC|+%3qLQJ+^{YUbQG-cmo#k@wM{IPBHCx5ub*|EP-BwC#m_N>a&bf4ytz=! zOmmvmggwG(MMU8#3zUa;(NY?-iSFsxV0vsZ;GtJu8k&pKbHcg~pn$@+D~orS9233AS58#8bz+ z9|m~6Hlq*r8DEwD7Uk|NS1tL>#k2hK(D}|{%e7;^6r}&`t#aqKTk2C% zK9FHn=55sdSdsKdu)#Ca2m&Ak0UVT1lDQHHID7i}i`K5{I?5|^h-ttZ#@SG1D$ym9 zYVIt``x?Qj54_?gbnrOi4Bf){ST@H11vwoxmQ;_>vWiQT=yDg$xShk_gv>7{psR^e z=o`J)Dw9x6eMLUZJ*+Pa$mekM;NOTb>{ zcly9YVzR`pa0K7eC8LaqM0H{$Yw($0*5?OV_-dA2tzt5~_ykT~M)&AhKYhR7u=Te8 zDQEHDd3E(AROlYNEi+&4Fj^C!ug?e9Kcxzw9y5vn!y^#PGKp%qRN;I=D?yIGcbpr8 zQK}(wbE(4c0De3GE;y7PEh^2y6i#X92!8fpqp8B7ngb5vNrTG+sY0XAG^(3^kOcmA zA<9m2G@BrmsmQ+%s2QaMVUkJR!jOUt80N|^Xe3%xGARtCmes(sP{X-+YC{S2Z(grj z9ChHLSYjN7Cnz12U2DMK?l%P*yHp;Zrup>>K%>9hx>^hoV!7 zmH^X~T5~0^>8Ot?zx}X&u0D*wc(a_KveHR*VpNHwRBdRf5pwcGwW`)1BY|ZXPKZAQ zQjvR9J-6!8sV1a&E*p-d!9-;@EEQcb6B%x6M)K!NlhoX-<)aw?{SnOuSEWs5wIGS8@M|=Y&jB_KRh=#?UHziWQE>#HOI^*bM86X;ckBFC zROR^+Wj~fNYI(-nMuZ2IW$+GfAzZoh&9 zE%JvW9yf3i5>KNe*xzBZJ*8p$!IELq88_xxJ;3#S#g87xWKCn$Kb!OT`t@inv3nY2 zS*T`j10T5%q=L1HucQmv3Z5^`UkL3RU1*dHe8N1bn#JSVvnSmsfJZT50O`slTBw}N z`0UHQR#~U0Ol3k29i=EV$f#VhDFBCkZ|mtuy;7dajp}PWDNwh{=XOoy-d3sQ?7DKs zvg94}Aa?s1yC@}x#$UcxYDoN0xe9;7Bl$@RzZipI_dxSDrq|vy#TG zHPy1t(&GwfrkOwXRkexl3kYClm<5^RY8l%5EPc92y5_S;@G~t5AT7-;LQ2tEM;rm} zD=wH^?IM(vd1%3x zMO=Y&54avbBL!=rxRJ|-QFUM9Iz48pmecO`01;@Tz!aeR@@H|^@v!!QTHTy_=CBcDxh0dBGqZ@E0J8-#};oxbZC*aXY zgz(fm%LPD>)awN|q}A0(o}Y1V`)NfD9<26Pim=j)BcOVUNP%5$y89M;`o~L4cr@dK zZU~?Lv8SRJjrI5>LjXm5mVnUAc5R79abZBubYF2|RzJ`+vVAZ$;|MsW9Gd|Q9LKex z8NksJ*uL==tRS8X)be5#@4?h6wAA`>l^QV_8(jj0*hz~a$F@09Kx?i&&ct;-p8k?D z8KfHR;-zt)=8(Dl0p%We4R_qdVkQrIn5=?`*tuIti4VjyBvbz2N#2ks%bsYSYbjnx zu4sgiF4$6KB-m8(nCfr%yec(_QUTrVn%KvXBMZu&qxbTN{dAr z{rXWj<tmY|06GZ(-NaB! zyHdN2^QC{1MJ7BrxJHG9DI0AxM`nu3!e&P1>6n@P0gAD)ZNtvrl0Eg{N7#` zZHOP%Tkj<%yCo(^qAFelimm%=+H>1SkwhtYFpa<<0=#Yo{%`e4F@V~t)m87?rE0|V zn7_6Q%hl5BjvrH*)#d74y{h=`6+YdknR~*BN^E<*daH~wv9#H}PkqBO>nJ}YbEE}2 zE@2j#Y&exIL2aS;L{s7>Q%*#MqRp6CR0Pldpw)y$nX}ZG^`vIjP~56CEfQK6pKJd?qM2IY%778z zLg$%B6J*tyr&UOh)%0dMe@^T}zh(_tt!JLv+ZNJ>91BvSz+x10G}6VawyiH=`#KC*PexpFb7MByfK+e}kKmF?d@ zXgQ`_A`OsAy9h9*LqAPkpe@}8%8TG7MuYPev&&OF2yD`xa(`+jP@XDonhPJ(8k5hV z{dF4~U_BicoRxgcsW?+yVyBL|NuiLfwh)}v&|$i3A(AjXI@)8Je(_yjv@aT5)e<`F z^-k3F7A+;TBjrcqquUQ2`Sy-zy|aj`ew(oG21cNsz^+Z(-#=Ml=QGI?%COp>7xv}n z<%%mzccUdQtlad;I@AukbJnCjwv2j*N3BlGifcCKfr822~2Q+JO^8?kw}UmaN-Kfb=2G{YWXXE%srylooC17+n;gz z-5;$tCWS|HMX=A1(<1ldI%Q`#qsW~nu@~#8z?7FqeaQ3KWG+u~u&9J;1k_cflh7Ei^lKW8kx?)QNRc^E>hJ z-?b#CGDnBn=fzU_2LwkqZt;7~HYy*=jD`E+GDLJ0)>FG*w=Y1FnoWd-bOkD;xOJg} zM54_?C@wiZJ1PEbDnr0NEKt7)OEEgKul*|x`)rT8ZWyS-P(+^z`##n1VleG@w&hZ9 z+KLu%6)~V?AGW~Oc+WEXWF^{fvC?KciRe(A+_Ccu7}GyMTS1*~O3BpC?f0?86WW!<8+wyj9D*xN`&T?BiyE`%SWY86w$B<2cmv18b%|!aDRrp?D zBqQtt_`7UKGc9zTlJk3H&ERxRD~?1WB=h?-dl-tl1gAgH>bi0~%R}EEGqmW#e&&fI zTo&15yqbLr}! z-7VvCtZ?MU(V?vU6sG|An(nYw%_tH^Zb|i{6JxgX>k^tEoqg0-#K=bQIjM%KWmJU3wVwPc11Y34v?wVVA;KZa=z}1^z;HF^ z`E0bvNa7PU$?Pkb4FzrZcINvGEAV>=f976Kz|fF#A2cTT{|udnJ6mlShm#0`&?F*N zTg=3cm_>=bh1i?edzMz)2x3<4Rn)G%t906%+B&qKT~u3IZFTybKjB>Gx}Ntu@9%ft zfm%F2?3^Z`N2)d^31>Wyq=NR%P@NuMn3=r%^DYt2DO>znROrvj>yV&JIO?AxUhwS< z{Bu7>Os?0@G!Anbw1L!M^5Mn8J+%&t3DC)>h1yk4s|^;pGog^i1`?FNOf+H()qjkyla>fZ#0^?@#7Nbb}H6h(rR*6|_4VuZ>96 z5opV>5IPssf?2gLv%R9;;m!;lgqXB+8uOgzKri5$rsXfFpG?#3Vx8Z$Pe})~_DzxW zdg0jx+?cc497~H#3fM}>Zii+VW0nXu09Qs1st9f5_*(Gwk>+(RG-`6R4UfYK)p%lL zo@qhjuyGyaw&o(M(&-7Nf*QPS$u(LFcY`TetdEkMoRfHM?6rRAu<|Oyv{@A--3lEM zJVRq{nTE3V&K{AlAxCx-3Mbu4PZU(;PRD~JpOsDpE$G-w>s1t~iLP4zqbRBfjzLpefPdYZr&O8cfIv>+18co=>%o;*1rkO!3l+KSuGYh zoiht<6o0fp5%^3}U5k}N!_5P1)x9F0tIcubi4e(YA1(Fm4ycUO@ z2&I4^sl)mFuQ`R8Eg4c1^SB5?dMv%&0$%q}a4;)g*yd*qNv3vS>24_1ephWFuP?16 zZ1mWh=LvF4Pn*SFA?tz?*}kzrC&9IShe!7;rO}QQXADhrY;MCtJZ#;^Hl4#wQI`=XQG=9*AsOuK4#b0k;?A# z)bXm*HA6~U!py9~03Ezv=A+zCqv&E-{@Vb@26u`_tF@$$sh(q**6O1}#PhS|VNK_g z$wAckJc?0E4|n(&$~Yx^DNhK^589T6CYfW{f#&w^dDl7rd&!qoh%C7 z!33~lgH)%CiuYV%GS+81azydK;@Na4zH+B9W^X(d6&a=nI)XD>904+zJ_cVZ0r1Oq z3nDEv4G^&l8(B28FDF9hoKneKORi>>qPOr%N#It+WSD*OFsbCRDiKw>VBfq+x)YIG z_*yu_{1X+;Fuow^>Opbl?2odL12AWVQbhpF77sw8x<=Mhs$6HS^n3%(1u>TFQgGp` zH>&b3;G!_QW$PRo;tV=zg1J8a4Wf(<MUjDHW?z65M_ z6T9~up$VV;0${}N%Ea0OoqD4#F_AZbI>tnB<^UkvO+Y&P808H$;9P2^x%~8>`Ns(x z^JmJU1R7R&Dlc9M$L}U!b!VQc$rs}?kT=6)St7veO_FXtc4xn)nbpPt$sC6s8j*J9 zsiWolOFqf^`DVe9;t7W}j9#cvdl5TD`Cog7DTX&TuNzbh>&s3z`Mvy)8P*L**?ggO zJ6n^w*2`1vwZSigNZ|Ye(TCQi_*x}YFJ*nN7Fj1i&u-`tz!nwF6}ptxpb!1*4p~WT z%>oQJkU5!+wlK^CO?`d`f~ZC$`uSgRbf6WmRoDxRbEi|urh8p%N-AHdzt`Q( zcF|qaBq6BaTT#I;lgexTthV;0vh^U8P(=l2ExPox?;txr31j>w!=OM~7pZakd~w30 z)8OloUs^*(V~cl>6|0YDDTxQ^3{cJaS~V?H##3UPHsP ztMw&<-aoZ6Di?OXC+sDDuFqAxu2d7$_`|Be!R4z_C%i6kQFNKL?cKqi1hR(yzHUsW zqYOibyDrY*>ZkrI(kC{`J$D#J!d?5$L_UpC6hL0=ZsG#OY#I9Eu~{P8NOYheJo0aPd_(d zoHjJcxk1ZT8H4>{f!b4sm9uB&o!B;#qwABS4@>(g*6e*_LbCI4xp`~e`T3oUSv6|N zA5GSx6c_UR>eK9b^4nL?)c+2bmnh>Inl}8VtaIxx!b#&Um5_M@0MFkc*b0%+VB#&$ ztc0faJ-h>Wd?s)1_SqXrl**CgIpCD0sbg^3FL|1|p#uIo3{>h*Y|Nh!Ej27Q&~9j% z*nX9uUaBbvUX>h%1mSQ6Vt)!&)PrxmqBf_&fau%9A zJe#6@nfFu6xod2VIVp8V4*XwqqMRqSNmid>cXZbhRsCK^>JGD80A3*nmLZ1x4Z0PA z@c;|Tyq(+V@FkuZBKQy8nt>*Z)L7g{dXUQ6porZNs|5&?)#XHcm{IDJ$KOdIr8~3G zKc#H8CcrAq&wbVcNtqWAlFZ6(WTy?nTx!du!lxixS1!J2HKfiQl5u& z>a%I--%!&(i`kxo!LzSTyD^VeuuQIpo=i_e^;x`M38~qk0_PKe>6hbKS2D7lXAEdy zcdkAnqmF5HAAH$j#+D2BxT?Z4^!&8a28T$Z3&J@8K66V&Byj; zdbW7=2}}G4Ys|aN7Q=?8s1g%C#SBZHqYcHM?p-sM=e^O!QX}ad7m3tUfU#TP z%QIRM17iyy!CB8)**T+O%SW~!EY7L#%%G~g#@5gA`}kxK7AHMa_}~6>ZSh8 zIlWMW@hhYhhd@&&XV&>pCukz^6~H*YpU4-V@?ST_MK*6K)LmG8CZpcmY=yFc9B!*H zNHC+Mm=vIA3~+L;{39uu(`7V^2}nPDp`8^$M(j3lmD-s#5JlA2-Aghiu*jjUy?S6X zwLq4CvkxrQ=VEW!Z+ES#CFZrn0}pNmeUNte9#@prLJLPBXf-axF zrG^tJ-^DL}oN+E>)=?6%{hEp?l(v-*S9JqI_w zlv?-9!UA`bhTs)WaSW`SYyN68W&<37r`ey<=?$ zqgTt_Z05zL+AF$L11>=r68hkNY4<9tifKAif=sFN%KhOwEUE9dlbU_+^%Ijr4Or?& zT)xd`fgK(bc9VMmWo9%M2j6ed2)+MSw( zJE(-b9Sqp=)(sq`9S#>v@_tKA zXLjT_&E4p0$U<)xTCS3BKpu@OtDzF3ZyMYS&h||5Zb&P0ftl7uQJf{y>2x*gc8#f0 z2hZOo4R_EU$WjzlA@v6%^{Pu%qVKD3vMi(SFVjlnH|Q5ei(LAVAv7MwpSnxZV|3q< zP%kKSxn&5p_1uoaaPvLvZ%e-5hqfxM8*}eT9n*xB>F8~X_Fs*`?Is1h@Njg{eGQ5W zi3`Ql??{2ngFj{^ru4icKOQZ6L+t-ynB#fl!P=%|C0G1aS46d+Y*r+6m1^Z702;Z-48Ons?I;sbC|WeP*8Db3Ah;A_l|X( z(iAi?S~bZOc!*8hX~+|qm|Q4eexun<34eMhXmjVcqDU06P}JBl!F$npSH#)reE9@YClvoqF) zXLV6%&t;7R#)Tfa1vyvfb@9--*h!sAmvsn^jK2}y%TU`eUxBBMj^9rF}GTBml(FQGlQurLOa;xfDC)@^Q0YhIg8G4H2e!v z&MdhfAQw>olCtsfYBQw`w|(IcX^XGpy0XOf>#?(TRp~3X$LwOkaiXnVRBKf@ehLFd_Vck4I?&>zy5W@_U4bT0+C-3YZd*7z53wzZ%^K0e63r;d2m6?ZcWu)VO z_Z${I8h+mDJFck9N67rL>HCG@TanXj17v8(g&ut)l2`1%tcCLMnPG{MVUeqcc0D`a z{5KnFvu7TE6h3aD({Gul?kV1^*Ln zb-S){|JAzPrn&8>eO`Cr!l3VP=6th@xW zlcKvRN?X1#1Qb&os_s&T(hWBzAiAyk}Q2UU*jhR981xelR-O&TOltBDVhn@ z1&-0Y?COB_9x~@Y5TT~@qvg%kzuk3S)~wAqe;aOE3-Z(Htxx;($y8$nqP8+W=QkP{ z$vdw}%loeq_kJnwK;qI09Sj~EISS*jJgLQA{)?Q>WK<C|)h)-Op<%FP=m1`h;~})zoTK=|cbHVS zW&GQ7jhb}{JP`K-RHRG z8WQ~xEM&1Ho$d&Wt(mQZF7BR5B>BH zuVU&grQT+OUHp6KJpN%fxj290w zne^wEb>R<_@#b$dFYCb1-w3!4^X586_M~$rQmtcJ0Z!?y1o~AN^bd>vqD5ESlChcW z^3fESaS0sv_(2*A;caz0w4$A-omnW|SR~8c@oVON253`W-t!C3E57^ce{_G09rWyC zZlh`MOr?+93Assg&iJSJu=-5x8qC5pd5l~Ub?O=6yB;jmJt8Eb*|om4B32p^cPlUU z)*keQI_TH!(Knqh=qLCqa#&oU8dW_+Po8H}GGpQ70x8CDn#~KgPaf9e`?Dxu@}acs zxA`J8qCIP6%;s~g*lE6h!)bZo^>o#xLd1{;uKw}}38`b9;`aeC3}GG-Uz3fQqr`KM z=!uhJsw*^^4J;}+|EVBg`p1+ppX`Re`{NV>@f)sx8XxYIUs zjjUEeC!sTg0@R&qqAgk4IX^sc*!H)#^y1Hnxmkj%e9V_$3U6^J?D0+KhKKYUgSTM( znWM@j{FCV{KEWGfU-YM{tBL>ivcK`aY|(z+x^}IYo}F*3sGTy365MNDfO)8N+AzI; z`@&T~Yy3AGJNjzbg)1N+Sm1+bT83u0+P<Qb$*r!NLm z_XL?WnHxA0_;B^-S=>kN`5rb}45P8+L^(dr?BcOePIsrIF=aOFK0_vjG=b(I`q$TF z8F*kSbIx4CQ$+H3aDXJi9DKt>R!*el6rRDuuQt1N+OV$Bk|b9L4>V$@7gmj_qCr&m z+>*E))|$0s}$(xc3dO+E>~bKNP(UpC`3QTCC4g+9&M+; ze}+3;zsE}7+kgfl0~d5SjJ+)lrL6I894OB`_!y6;9!;Mw=mc&+r}n6GBhM5MsY#eU z{OOUVnS-mv#A_UnCtBup4%g&nyr$H@Fo~_mu*umcq-XhR4FKh*2Rl3dF-6W!)*TqN zYD)bUlV}w6y%YZe9TTu1Suh#Y^9(;_gw?Utbf43=Z)xmkLVpb)*>=5t*!dmx`9&u| zaLS}!=1+MD}Z zpRVHDtAkJ1$7W_hA8316Jkx0S^#@N!wartW{USHMI2BD9Sl#^@p@HvEY#tE3e5Mra zo|?G)T$-4?wvuIiI_}+6G@l-f?<)C7 z^_JP3zhgx0ZY27Nfq?4;29~&Fr;mPuT9%B(HV?SRg@w2B}R*qEEm3OZQSE531 zbdEGP@ru;em3kCRPun&PF_P{En-(=OgfkaUoYF*dKjO>dPvw3&)@D=2MrebYWRS}FZW12EBV_q=7Xv2E%VxoZ=8_tw_W-DIm@;dld9$bp}9WB<2n4{KpNyR1# zsD7}_S_sAGsDXurEK~HO>lDhCdxZbyiA^PTbgzxVjQN?eldzpCUF30$*dHRBrdEkTgd9aQzd#UrbrH5FtRFrn-%%EGvICL%`*!reeYHK#|;P8LJiB?u@7?d zkWELFRcS=wx8bFZ*?qR;KIhb{HE-_vR)7WC?gFsa;~MWh;SndbSD1$E|eXZ3Lvx3j|x<*b&Vp!86MY%R5RmpNoAo9{de8dA8t zquH5*e0Pu>B&HTtcB)#eW7{_e7_%8+yl#gvt*AjywLW3GqP|F(8? zn`A;}>kb$~-i3qTghJu1=!*@I{Imw`ldzd?cz`_*r>{Kk{9XxMQZKC98!S);Gn(Kq z2%`Vpjl*2r#Vp#tkUW)5bx#pQxkKgwF$3Y7U;XE{rAmLB#0vrx#qWI7q}`i~R8>jx zymmc}CB%A=IMHc9x?&Fn>hjw|~558h&+rKJ%zt({NotEOtWW zV#?x;6`1G3TN*lpeW#`8CcDCu`;o>w>+1fFhD))Afm5Z!DdJyPBh3o`y=04xtwdWi z5ZLbF3dT0Id8_(?ly8PgTNS$u8dtV)>25qw0cc30cvmPY_lh;rL`Fn7;OuZ*D~%-^ z9}4yxb57M;X#X;S0QYu({8>n8tx0HZ-y1r44=D*2t0kqz(JJ<#ZSs5D={5q2E+Uu$ zD#I@M9F}=v)1SW%!aDQ^SyW5-y46YhUvD3@5FZjB91Q6e`{;sQ{YQAdV3r}8uv)j7 zH@w%{43f~hZFfKjIS>Vv1C;B|4hBRek2C3_%dyH_6R$gin)pIgu|PFxKS=qI%D7ka zoUW=y$lFnW$g}(Zt#1LVHsu|H=tcd-r?)UMfs&%wCF~@ws70bX(;ULME{T_-sCnW1;1%cD*&c)KLDW^1m&D_m3; zJANxu_+c<* znS!ej$Qp*YEhc5*&gntPbMkDbgdKoMzEd33y=Oh%`byZ#7UqSOJg~$kWA^kxuQRQS z>R10UDDWjejhVTEJ*$?$de(_+*DM^Rk%i2=$0h5Nj!Qh>bQ?AD?DJtvz8IK{T=P-w z@xS`4(iZ0?k)eR;+7?X`&9=vrK&|B~GfuxAkKkzlq7tA1u6zV}|7 zO&0U+17;U29SkC$QxYp$i-Kj?1X;+T^>j-$?)Xh6FwC>O0z)t187=PdXHMI0TL6|a zoq_um2*&&eoi$@K#?2Z_5Bq{8SZ@kIo`C;F?+Q^FuH3H?oYvVrP4!)8dVwZ+8Nsm$ zpHW@Lj3Eg($M2ivfhzL|pb8KAb#IZVX3QU)JlY~6GSED+@m0o*U<(y1plvJ67#ghm zy!~v9tC0FiC-;u5t|GM-|EDv`cPNEz|Aq($_j=+TKqI9Srmc)wp)-Ry+2t2QKbNt z>xkuO@jWq2iTnj;OKoLxGIjquae^slAm-$4{a&pfdN zuQpLS8IYkOg4qfzzw9D*sanFWvp)+q$5@=!;Ba3hL*WgVjDnR_?s(c;kfI>Fr4yO) zYb$*&ZsKdbtjQ~>%bQKoyH%L)Qh0|JdEwfdc+sWkY|g(2QlG1gXzNPU@kg^&+C@sylKTN4r2@wdKCmDgp$3llDLduXo70nnX zW9tnu*0g>I*aFXJ__|;G=u0Pb@4Y=`$nH$>$$?HtN!S61BSY0qO6?36*ONaoN0iYq z$EvR17FEK9lGvQ=^pJS@ z`;HR^dV{p<@d|d|<|fg`x7tm#sM(>DRZy^ij`ep*1|tvu3N1bf(Fi7by+nco;6}u} zSmj~jpReF>s(@aFw@XclOXAk1z_iP-fL;K{2fr>(Q161GU}C(-w7HJ>HY!p$0U_1`+)0&?Lp>%+9Zems}O&;sOoNovD>PXEpJNOzw{1W9Ui&Uq0RIV-(=RMjL6A7 zUjfE0X0^~Mf4?0_BY|cD=+`_Vq)iKkS#IBjoBb<@+XYvaeC^mCHYF~s``+R7I`1oI zh4zSR@4=XIsM-z{5Ne00X*x2hu4vAxU3n^TeUV~5pw~X2!7e^+SEHk`+#OE4rY1Y0vl5NH}B+yCc5V}la5}L7eh*7oiGLWq158+6lpIFVbeL|`Xu_|hoTKdssEyx znbMKNgAhk+Ovx-r_{eT)w=%6La(9MBJFox1^qIJj++Of=@?1k&8URCwku`a(iap}2 zesxeIPgT~AvH)DIma6m|NKCNKXO4ayUd?UTXvEIy;;F*s=RVTNEbrIYzZ31)1i{9>>p zd_hXwLyI6mJZ0G!V|l7T{3I+)bknH3rl9g`=_1k z5+X8~t@iHAdsJ9wbJ)p0zV2+z(4JIQ=?^h{ef!djX#e-o@@-eIjyZ{%GhdVx5H!!p zQF;BSJ)ozw=0)MH4eUB348h|6Z`zI*}5IHfrwaN44vBy^4XzKJQ%wpK|U?OLA>^weweP#(!7erPrUf{xFAyLvYmi zpF?{fLIjzP2@6QP#Uk8^GU5y}@;`eLXig?1REXXnJnifuUiyT(LabR10NnnT{LMS( z(oV>JRBpWZ?Xs$aauG$)XDO!kj43-a4HP{@sj74YlZalz!@iNaB~q&B%rM%&xGl($ zM9Lt>`_jR)6s1of`wJJMqy$*64cT=WNBb%@wSR0h;BpJOFBNvb@4Eiw-pL&F9e((i zX4H3g{-=(kx(@4fsohnlk&h3s{$cd%{GCo!iaH^Pr006{E0{=z`0M2~-M+G>eLSYHlq&eoPxeeYfI>;d0|;e8C@(xF&TO zZwV`Ln%OMY^n*Xw$*u98U!7;g0mRuiO}p*VRZ^JsF{w8j-hHKBPt|T{hh7)*M>j2mZm6J@Q+C9L`+C*Nd2=(4>sJ`N1Fk_z!AG~ z{SO&5?E!k$&h=d1jB?P@)Z+Ux}Y~AJM4GyOwxoq{03szlUe9 zd=JLHJ8lJzo1i`Td0R_k#)BhtIVY;UVzK_CIuCC-#8y+o8kRz-y- zFli9Y8W0JifOOMQEcAHt%TT7FGzBh)W+FT(9xeneeKOx}Czr2eGh+Y1pRY4kG5aH7 zlwItF^@slSPH05;ojOqSGrB z<3i8wsgFJ!h#i)>xMQ|{$qJU1qCjcBYFVsSO&Ssm#KvWkB^$1gCa z6HVSZ2^rtUiFfAt!H3q@*}OVqKFtXlLzfYr1}Ot>Ngp}jowDOnm7^bC%lZCxA$pHI zACU0<{7-yOJ1v3=P>VtlxBdAvy22Edi*1f${rg@U?Al8Y)hK2(t^AJhD9R$4rG6F;|U8z7- zmzy7E$KiiQzAwYKc#tfj74m5|N*NR?iE>Gy9wAP_O?E|TrxQMkg>=LaiqG#nj+CXP?&5H|V61I72VA2b+Z&KAC6$qt^ ze|fp3V|tV9RfM`^1T-=XB3s@^M!q5xBHLF8xhQL$cmPAua8|8R(#`@=s8nDTGB}J} zG;caAwlcdS$bRRZ-u&{)yFAe-Mdz!vH6&Xej#+q{aoJCk8qC`*fHL}P&W+bbnhwC_z-de~ewK>`Sy^IG{PI1^HiNn$PjAcvuO^MF0 z(G{(^Ww|VeAm|*0`~}f05dUIRl)v?hoDskos=>&3<3&#pZ@i9FGfMk`1Yc@P(7UJl z*FAb zBpLl2x5VJcBF?Wem@m&&p&hGsc~s71Sf1;|7dX~lo)ITny?&&nht25!thS}m%4ouoI z+O-|SwxeyZYp}zFrq`E5S+qG*LM-HK4`Oz&pF2}f*M_SX_84NvtARb8x(S||7YsMj zvcK4@R|(CC##RUB%sVU&-hGhHu)&>jjIcWAgLq$2I4q=3u}&CX`xO20@|{7%=yR~b zxE6Dc9ZYf4*z4BPG7b`y}*`Io%QZFv~WUNyxqkVTARlZ%a%PkInIB4aCK9bNqmVz~(Oy|E6JMi*^+}yG(YCh&kW^|Za59|kk~mhn zz=+hu8J^dvCVg9OquLd$B(ARdN!ZBnf*|YFM*?=sxUo`xIyNrU1bIy5iBMsLO?8_u z(GcHa$QteMIl#-?aBZ1gM53_JXeU)Rf9fu4(Cqss96bvL(mA&?vXEul22&Nw6K}oG zzZoNvcT~Ecxx0UF(IlUV2C6-S3?rE_#P;0cOR!6-GMxFRj>;}K^~J&xn4kr+tntibQvW0avrUvqUN?Z({ZB~sp425*!}UlR)+%AbTu1LjWiq*y)L6`We<##Ig|?b7J| z^B^z4bh9KM?@4SiFpPrwr5RLB)!k8xKNDtANxgMBxu9o>UaE=>>EMfCJbfoz6ZT<3 zZF5CTO5+p^`|f02e%klUVkXotON&SFTi+B=G}1vXujz+f2DD2jJV+vUtJC9M5?^hI zk+x7PUVFy~3{*e8cKc~g!re{>ANp3gu&d35+ngo>${$NKA5%(&)|$g(0e6Tf6T?-~ z>pI0=UAVd{GCuTaPVazycxb5@_nw;IfPXsSA$tbYZ3djATrzDos$&bgIeBHvF($;QL6m?Sl> zz>F{<3PjBfjlcORU)VH`?BhU<|_hAIjQwHd1t20y=B9pHjS?s+Y9@354t9Th%2*HK37-`#fm zTi96%xJdL*se8OqD-cJ!iV)0vMv~z=*@wJ)+qaT)7GEaL&8YG|z~%|IqggbaLG^Mp zG04epyN}xW-vDOFS>I3nkau5qCJguFSYh&@Inee&GOGvv+3(*^*^2qEyolY~lE}Tj z;t5UPmB)~FfzL`Q!qs;sG@`#U(BwTe3N1u3Gv8btrSuSRK)67hB!Wl3(@;-{TdyjF zE(R_n054&1DK7A{s}ivt6g^8$*72khBU?H%!!1g_<6YuKqdSy2^hikvcS8KtC~c*v z+6HrgHt=)c87NEgnH;B_0H?VP_cM9v1rMIL@w{V1x=}j*B%0_dHi<2 z;ud{{Wzl3l(sXuM?xMElp;81aQBXbEhZ$@p5T3@N&GRAxEg=k)SCrrB^3@3!8mQz_ z|8fBs1=IKR6CwJYecS}tYr^d}MegqcKotM(1sE;f-+%%Rs1#A}hFyd!FdTt36;!3& zG4@sZuEK$RGAJ@N zs|(jwbI84d*X0I-=M&UI9uT6%ZG{=6vqt471PQ*76nj8=l@9kv<#oo@5HHw`;Q^bR zrIbJOGB5TtTzmMRkit3e;m3=4#p5P>_itYO3i%Nx2qo}qh?#M8Ml|+nHY(tJ+tHfZ z!ZP9Lvv0$IGk{&!ygBoD`e{!_&UYtNm>)t=q-;QV%np8kg==yTVAcuq6BAVvR;Y=F z3j?Dq7NdD8vpg2hCM3qN-lnug^TdUkZ?dhJxAk3_EgwFA501LcmPJ=t|NQc5vr_eoj zp1mPJ8y(-Du+UK;|3^Wt#AV6P--E^Md1mA|r*b9V#i|)cYfT9m)#e#dUY6H(n@?Vp z_BG&Hd`P|)!~J*wP%p=`Ql;_dn6Ah3a)Q=nI}Upfn(VJ?S=O&6bvq{B`}|5jWJ^*a z&fvmk-)+;_bU{KPZBZTOwsvb{i*|R zi36Du;I3BjmRkx}9-%Cf5a;;YFnvYC5o8pBU9vvltF@BSycjNwPQEhXaVlID;j+$w zsDDMoC%LvW!4Wp1Kfc{9S=1LJ>c<|d=*Q?;h}_|^gK9DtF}wTKGgR9b@n|EsWa-p& znT)%A)Y10f_H2)a_dGQE5aSiKIW3PyBEv#9&#S6yaxm%YZnepHHHmhuNBNH95w`an zxPOreDj;o%*f1x%itS>qWqYQ2K@&?q*-Twd>r{SdyUBMU?|Vh2Qj9*KdZuTRKLpnC znwNiQUMi7`5v4IJtaSvflMP8|P^xZsekN}YLn1`nrFGTc26cca`SzYZssv?l<*7Vb#)Ptjg~{D>&nT!UjD8 zmCbFxu8xouvHGGeYy;Oz09#*lX(7T!W2&7!;YFe_oBF={cNJ_P(N|SO%yA0HySf7O zme)Y-BDw*39T&yoLB^rd+lW4z{^Me|yyBkps(rAn;~*f8E#Yq%!k#lp82 zA>+s)W4^1liUw=#quDN|A2Uf8q#4b7HZUX$i}|U|ehoeYtV$xB@-B%@<%`#`-4=msIJVJ;0xZTk5_ z^s7rc9w8E)3KD4TGflfFU#%bCxj+JdayjX{qgC1IAP}t8bRo(;TfwMPfxR8Z9M4G6q%G zcZK;shS|4i^N#?%L7gCw$CbZ=-Y>vITf&tBTe2WR1U(gZN1z!es}+MXi~bJr(*zB@ z5i+nq=k;(oqjfer`uJ6!*+8muqG9GRI+HPi_F&&9%dS%z4c9$lO@*tzexaiSFS0=} z3&QW>xiQy7UJ+Y4juY>F5d(f;Z*t4D8D>A0W$$4@>I`63t!wUnpp2#?cy3(u7lH=O zi^sAnw-jIl4mf<5vaG@uPh&`z9>9$%Vd!k6O_f6CPdu-Mvn$mw&xWU5F3fK^xcA}o z5%BuR{3XKx-sxEGlilu4vHsVaxwLOKXT*Ty-!*D7Rp$DGK3A8^KNZ{Rpm;Em1O4-p zeJ|C_ZSJA+rrUC9sri#3muKcP1QG`L?APshR>P)h9+DYMg{({~M4HF3Lnwzd{;KB5 zUv!rjgtDi6O&7W|9%qdHys*kebX;tWjg>@JIGIC7vmXonQGr>S43`P=Vt&P61hVEw zi`ZDcV0o(W@|a7_^4a!5tLdg|OoEbJeQ}VQt7SKfZU?iXvp5F$48hyM;I?!p^(wJu zp@ng~=QCJb{(+(y`_=DUx7Q!|vFDGN`_@Q)th#cSmoEO=e^zt6M;D&K zyDo!x3&@`6hI=8pbhc&BM+Bv3Hp|Z1pgq^W+5AT4z9rZmUAl)RHK4m!D)LsQnz?E8 zDLQp(FX=*OJ=KC^!oC3c7Md%L+|_=5?P=S(@$P<8>9QOWp|xt#?7p8Luq(@%mL?YA zgRIV<5u=2>l5Q5{HCB{_L8yP7TC48zIzs1U7Pv&g)eaxHHaiT>GrL8y=ksZg=;W>u zPP+aE@+Nb0NeRTW%?mFSxjGE!S%TYzlf>c{^lo_op0TJb^uGAd8f;PBrqHWgCKN** zLK%TkzH(HQXduU~YxMj`o3qHe^~8?<(*34B9}SV?L-puBbSDvpTj2WKWNQH8f3%u% zXt;==&gQaR>xWlQ#26>N-PRYY5Np(u^}Hph@VSRTo(Pd0LA_Yz`p*O9}8fY?Q5;PWQh<7xG5d; zO2J|ggxV3QQi9tB!lQmwhDnR|bF)8ob0Og~&tU}Rm<0R)Yh-0Yq{IVDZ0tQtq?ZTXBn}gcCjY#f_9DTTpf^(-S?Z*cGWS*arF(17kf@UfpFL$Ll^(?!1*~Q^-jyyzU{+TPM_Yrw?lNv$&G1N^~y~; zI9v>$1p)zrKf(0i^COld;QToOo&b~CFE9t#6R#U%;*A9bcu-f3F+SENFen6tVDq;% zOS4nvx`^*2al=+L&8tNoF}ok?pMP^(B3sY(xaPuj>mH=Ox7vr4zy}RV4V5>~R`$ zLSe32mD$14XwGCyaT5)!@fBGs9ogIj=POctzBZRtq=nLLCEZNrvTa3u?5zTI#IPcK znU2c(N@Agg*l;J3wKgWnRwLAbNVYP%YDbV3N9TCRCYuSlS}0wX$KEuRNOO`8GL}mw z$pkyw5R|a-HaNVDSfGQ3yciE&0!_A5veFREcN9w`$;X;0-muq-v{Fk?jEpgtDoOFG z&WVVzz3go!d)0|#swx(yBXrF~`nHu&x|?#Or#0Eh)=ppEOj{w-S z^JZygkfX6uyoGeJt3;}!Ry+|OZiWx^a;M!039&U+5x;crnt9WmAOlUs+(gePL+L~Z zosz5@NscO(>au-}X?bqaPusHsoE)!_of3wR4K9)Dk73vGG#<*(mBU##W{-@|F{F?B-=o&T#j2=CDgfKdl z(IX{Bhm4X`N=2-V(K$K<9U}x390&>uI#Nm`1re1NOhEK&@5k>?c=z+(z4x7a&OK)p z(L6mJ^=>u>_gX3woYdpoEv_fpI@+AL7HE@kF+9S>>T<9Ljig6DAKqSi-q%#o&(${A z`9xxv@AzOxqMOduxKr1>4I1(yn{LvA2>O0bczZpWOEI2Zt@KD=tEW#|(-H%nk6}XG zEiBb!E+xd#LY!zJC*18#J@jQWA_$!gsTI*?&!65`7v;Ts^UCGZ1bSsol8;?>oO@ch z%h03dm{X34rmCifnz3F+Nztb+Ih&Sd#IYVUT#P&2T$xp!m6Q+_=7Y!4l7eeXs8_@M z%MzSum(OHG2C(i`1R2RJO!mghDHENnBcP;q4Rqkiqqo`>rRe zbr)I|B17BUTccOs?6eN6=HJjazWlSL)Q+d~UR}3;%6%msy22vH=CQCSq%>l7oYM2{ zUGcl;H`v|3<|`9VdtS4n>ec*u-nz*2XFV2DoRko$j!bY(|16>#xzhC4JVTwh=giJc z;8K^Uxfj&plwB={Gew)j!HPtzi&TyrBPp0BolkzF3YMy$zQ$SV z0;7DEEOA!2(lm|4(!Cfd2;JzH3S3fuR{JZ0(s#|uuMm4eE0Bo^&X?Fio%YrJm<^P* z0DKNqAoLQ0RupgO$dF*in)BP%<<3q7vGE&N8L1i6Ns}$hAfh2*xJ1BZOl_mNVPv{< z-qo&jkmy`@B9zx{tAyV|L3f26ILzlJ5jUheR1I*nl|81yuP*y{Zi3mjDr+P6x_fv^ zENY04!{BnOOpe&uzO@&=#+R8R;;rO)d+cC(%7hftL2p>F(6N1p-*ky=hggr@9uL1U z6Xg?rd(D2iw>ECuC$i1?sqLBW80~znSFu_d+SL-Xuha(rT03G{Kfk}yxJRgN*XrSx zfGJH~j(#PfLk5oMtwhj38mPUc6b<4~kQtBUjSLy#gz?}kg#!~;>Xn3bky~cJyF90P z6OzeDG6>#|Nj_Sw0TvFO3~4&S(|YC8%`LES5qT2%*PISBA4S9yoJDl$F%x@k-&D(Xr?YB|1bRqZ<5N__#Ra(Pr-_w`aDJ=)ES{YPrga@mlc6rIwztj%>M z2`nZ3p(2LOc|Q-gM1@iC-WTf4Or#tkVZzwLix11ByfopbAF&3^X@yqm9k*bAT{tPi zom?QZPxvsw0aXkng>MrNk0wtegO04r7XTKTO)n=WwPAu;qcFJ?UySBDS63HCI3ddH zq9Q+6H+%ze+4s7Y@J~7EpN=u@w3&tRbc4f?ef(V<~-qInCOHMX!2Jo({l7M?}~qa9#dOO6wriBH{+;3Z4N&cpg;@ckkx}94ZRx zuB@qPZFBEzZU?LHMa1|4(hVV?tk+}jwQ)dfTM&pw?i~;}&Kh?1350~=3nm?@#zeIn}?EzV6oYa6D0 z;JkT3etcB+onql6sKbU|rQq$=MLB#xUKH>9Y08w!c$M=f;j&D#k1EWvk6q)(=B$Cil6|CT%!p zCDZGi;!~_8G*EB+U@rNy#~o_!^{xL7Xj$5wI`3pB&_JVbgq%zDo{7Af$4b*3W;8iZ ze(@Fborgc1xuZ)9|ATk!&wzo>kc=)yFCea8)f3IJAdk8?go85Z;~R6V0V1xs^sXtJLx zU#26xMVS+E0I_hzxJ2n~=MD?MJ^jKXir5YzOkhIX`!^ zRL(Zc32OQ8sz%L66Ct9o^jfWRd+>j?dadMYm*zSZMX_*2|v!u`(W*{24>361wHF%>Qz8mI9H@ z-)I46Q;+EP$|XoGXYzQ3LC9oRmt+t%Nczx6dbp6sJgM|%RG-3Y*_e_TX+O0vc_bHM zcJ>%*WMx3i!^jz8ol!j_6tp%e`l(O$#INb_yMQ`AUH*0fvHmCv$8fHuzGqh)K5}|J ztJRwC;MLe0y0aiZp;h_WA#hgrj;qao>M?k9FY|SKL4>r@*80ntGq1Evqxf`DBU~E> z6#YLD!g=NOH?coQ_J@uZ-sxwY*RL$$ zdpu2$)sm;5(_`ow+`50h9>!q~;K(}XVaT2ii`lj&R)C#mZ5#EGy&>>tfbbeQBQJ0}z=2go|Ds~x+Fse^&c zonZ`W@-vKd8Pi^E+D{Guy70~VYNDRJEV9@NI)IZa5|WeUm*GFK)L!Q*{m(`ZAR~SLmLs1dc9+NQVZyfMdrGq0ycIe^6oXyCF3w;=ojfrf+*PJ{W!ewv z%=NTW3TlSxa)YhW;M0mR(Fr*k+GUq5Otx2{@yyG;vTASd3oDs%z9mZ*yI$EYIn(HT zd?^^QvmMaPpVtBs`evnUjRQA*1??_^|GYTK15uxhaQxLR+`4z|RxI*iEaCuL!|fTz zz}1kaYhGMLoW%t3uoF!`lGWdL0N-mt2h*U!WQ6()in^|t_ORH2t61AAQcKvpxjz^o zP>3Bhf6hlVSJJdhlh6+dks9M3M@gO>#&wg#)%p`xfUskgT4n?|YL7?TP&RlEA)y7! zp9a-T!>x}LFm_NQM-%#37|`53ZfgaM$| z^NNp%Q2HvoGNDu#pje7iY!L7Z1?he7Om~;{qSi(S9r_i1ML5ym{&a_=1LM9q*(Ga{ zx2sq=c7N4r3YT&%|62fT{y+Ye;Onn9t~AmFDi{G{JJ&bBErreqak7zn;E_)^$R-y2 zdlUNQoy!^n+ebHHfw+^WNeA6ET<>b0PQ!!)YYwpBXbj>DwqSAkS{##On#}bd2lo)h zRbHr7^*icIu6@zPH2 zP*%0WtO84M$kJ)#jcFu3*uEB>{a`aPm|-{^YNJWM^iKwL`Iq!be*f|#f#LCL+x*c3RV><3-G?yIl07S>I{bUta1vf!myF;SQUowD>%SGit)Yp zv=C1W6jV#p4luBbp7tWkI4fshp1IY2!B-pX1(vCKXYKl-BR{jwGp9V$Dl(rFWd0K&jxqF@MefN&2hT=5 z+y=Vfkn{?Be^+SFw?<7GpoSIT}nx!Z6I2A*tMKJ|cI#H}=~x)y)!MdGz}kg{kg z`04Vak;u5P$Qld|{Ng3-76$PqB96)A0C}jr#`SU&%-%6>c+t45X`m8}2C|a!GFd~S zoA#e_@S%BdTWz0`f!0GA`eT>L73Yw9d%VLgnw$3AQ%v&*6gMy!woK9jeV}@V)@tlJ z3W}#SKo~(dMh&WcU2SR!-@Y^rvJQj@w0j1(gaw_GQcuP>8kB@>GWbA*o7)#LC~%IU zG!Z_1CAKsgVB-%ixb*HG{`T{N7ZI|I22yB4{1|Mr)=T5E?iQcTSsU%lVIK>V$P1-M zLrH@xP>?v@%lTqy8FG>BOL%Bs45WfN1<2)gTasxX&8$w9 zTB8|6b$Oi8Y2eJAx%bLfjc#a~?JLWk1o0SyWA@Gq99W;YblQ;IV0j&!TKA@`URT?K zgMsVfi$c3}Z=9urL+*QxXCf6nMGYIldchs` zz8$N&%;8>orN^2dhl@0*A9kbJ_{H#ppfTfPEUwf-+y>2LvKUMIWuk=R@Y#0Edv(0z zwi6v81WuW+^ap0Rr8q8kAx=lO(yPwn%tYWwn(+^;7@46N_UuB=^H z=5S_clXb*5#^D{0FcBWhjtq3GZtN}leG*)Qxq9DvK-fgjxA@H#pNw5pgHlRac!X`V zh%A2*I2y0#KL-nzg9ykXVoJP-NnLpck@e~2xL40{#$LboLG3WalZE~+OU2rysEA97 zx4{qKdq!2Zr~nD2AMMsXG+C7tc#PZnEoFt?-BB^feeZA>>_6`bxn zWELP%bE!=yHBf^G+z+^Be>FjO1YXPX*z*I#KQ8 zR~W}oQ%{b;iq$-xEU)FLaEg6nSwD=J*}@%;KWJfi%D z$Mo)Tnfw2Ic;i2?ThF6XBE_!gKI56q@s#qXmdA{g!0&iOq!|$Sp{BI-s_^PlT1LU^ zQAnOSfg8TxLR=SUm!K5w6Mgd50{;Hahz2~Qf$5M`%(uWq#H{e8V!CBr_y<7h8{htH zFu5P#mG`HgerA_mskpvJopd_17IfK1>K(PHM5w?u_1B!apv4rRLO=k3hwH&L#cKzE zBgqn$Ecqf<>OKt6&atQvKC}dUct2!JFb&~dGL~lRV}iTscV(bLb7R<4*f?NvX-3q< zbG#%je(U}99GtpeeEe?El!R0Dz)H2nsJCq5lct}4zG({0^g4*n?qJoG438p<2Wfi3@zaJ@8BRQ03;^`(lwV1cSK^W%hm8CP za?-F@`xH{?DJPe3d}z=aib)ywve^*8W&3G?m^;zS$A4=C#;ay>_+Q`&;j2HMKVkoh za)b(b1(K+cj+Y7p(lmJEfSt-pgL$T1RyyKTQ+U6KxDP!AA&!qW;|?I(4ojGi>~9yj zB=>CI3`iDDqYF5pGiYbGIpwA7#=S=bO>=cRs^fD_;%!ZDgdEr^|7D&YS82$#8IM;~ zwo0FGF_4{9&hNJ7HB?tt5sZvQhK`6wn2>m511V{e#$Q6-2^xPhX?X6IXb2TP{@+k> zO)&dB23BPstJzeW&L4zDh&vZH%I3N*kILp5{n{)jQMVhb%vs&Hp3fclA&;!^>wznZ z1TLo+8~Uto(kz=Qx61u0#h{XP-EalvGeuQtVu?ElrF~&re~o zBa=4MY<>l3#Flx=?7yFW0qm1?muT_4SKAIV^ z-eXmlOIRK)E$DbUN<97esm_9FMQfIa_w!rd7A%r~4J$R0byd7r&7$}Jp5l+&zU*D> zpCyZ_g%0XHo)x<5^}%Op-GjvXhTy5@64%R|@BtN*aM4C zg7(m(gmgowfcM3{Qmz@l3_SRknZmC@x>yqnQ4nY*9<)dGFOkuD#g{S)3)57XuH|N6 zgT$9twJMq&;4y&~;_(7vWoDWuLQw-qlpgLw(`iVQxW&co^VNKr*5NDtBAjoiRVT19 z5pG`#u5AloZhgB2b1|64No|WkU*iRtJLzf32KVo|ubYP^Ss4V?B6z!61s9IklqUoq zM4C`VnhC>tk2Ui*{(h3y;$MN&+vUtB*MxNp>c0-pzLTMd8_F)}s?nfg-`p?4KOfn4RL>N7 z_25ObyvBcDWhmCGZHepz+Wn3%%`6^`R8a>|9Xdei5fN6Z6y^&XUhFZk3I%9n97OoM zW_rTw23!qJL~lp7Jianh+jA-Rw9#DUB$K51AuAU};}Z=@od(6?PO2~AMaKl)IoATg z^V>TVEuyU6zcD9K;rm=6O?(i@bsq7QrRv>On0_sZ8a|G#E`ElGX_22#@(Y81bbk!e ziE?0#Z)h7ey*qB6$txDB*ohAO1br4hBA2eI%e&%|&o*tUT5w9x{f%vcL9`%XkZ5gS zs$+EUZD)fiYMuk`z!JkhBQMj#ASU(4cK>*awIm7e)o|JIx?xVDtGa|YL_>_vhb?}2oRnjh-4f98(^+339_IT`_ZuYXKZ zRD0p<=yjPa`FOQa|LOI-k6Q21i)6vC!UG_SO-9XkrWC=C(&y-1f1?d2l=9@3a#-FM zf$F9=cR~(S%9tTMZ@S%o@4PFn157Bfe`L8PTIIO_66u1C<3uAnFwkjYSPaeOwO-g7 z9WjvL>8xxoLqND1qzKoOF@B$eM7mj9jn!jph;Q`x?3Yct*572L@PJHZZvYAAGc>?E zjuWdXW#(EK=glK>hNXD&+JwN-KG(?C-I_E>7&;YMLh!7ViH2w`v5-I9z-kBz-ZY8# zvRdro2qB%v6K(Mf%}fd;8j+7gV5u3w` zcbfiO5J?iX%g;>1X_V`ReuXW&ZJv?=KvDp8e7N+7kuNNwf zd-8OK<{k^ilJfk~drst4?FYZN>Q&i&HiDX7&O^jNLdTW!6JFDm=gbLG3fOJj`*wUx zeOnD+>?s)W-a^}>j{g6SIcf8-G3o~{vpMjY-l|oa<@3S7YRAJ;RE(lQUAF6J6|aI_ zs|Kc-;V&>C&PGe*hnKMHLi73KQdh%OlstPpTtAoERfTp|{*~XdvlZCFLWRYAq3@;p z%U|mSFuP^Wgp7NftBZyj9PXv-)<3#1Ke_#ACg;sz*<#<9fI%rIJk7c$;R*8nS>2T} zWygz~ZfglJumID(oih|~;L)CiuxEKH-}J6TIK`%a+lS{TAavIZ>hT#31Oy$cdA*|a zIxVaGdRCr~W-BLGzKU{mkFecpSqKwokOVbY1xU|fY_Pd~UJxI;vM-CPZ#qYwtKwn= zS6p1`L^l#G2$DD9c@>y@sa>S`HgbcL=U@-1)q)m9fqpin{=`zTNmcCW4%50kG{*Rc z!6V6E6mBvv`_~S&zLI=bW*YB4ia#rDjvsbvHO*%U93Tft zs^Amn(<5;yNKrDp`8XVtPB> zPd;z7#a%*3@ZC4K2tK&fXh7BoPk|n%7aNeDoNh_~=0nxpOVbBroEe49|CHaPr2P>q zR@ft3-br=HOwYn}+rw|#!&(0*ETtwdeVF=z+uhV<(XMktB@N2VN{)TBq3FF zUx=N^B;@>oXF-}gdYW3o%St|ABZ1ToZ1X*=G84dMR{@D0rFbSmrRdPgAlsV>DIAd$ zsXC}QEk{bnNIVu&f7|?koVyWtTa_ge3zysGthgWEd95(_DF&@&)Tv54{!$>lr~RQn zU2Ia7b|R_QvR(xkVX5Ia#NMH*&`g|*hr|r-NfjZvx;s8%cujRGG_e(jEG?X`w%!t; z8w9)^SqD#|I0w@-R&;n-y(9zFRIw#{8tP3;WBvpVPom7tuX~QB&Ss1A{-hC7`lcq+ zUo6ASr`ulVw2D!R-@ZzZL2;~IQgA~TE#_9QCi5?sG;A{NFW+$_jzZL$c-cZ|0Zl__ ztbss?*3^%S*@4R0_L-6ga!;dksl0?IN3s8wtjI?Oc`s3|aT$*M_XF@Zcma?!FR6o) zDy>n-g(kr(#BL|U?&O1~>>L&(3MaCo^gB1JJSyui5`5YjmbjW_iJBH~C%gnr2ULz) zqO&dV@X{aPif(Lfd|9wKJ!(I@cxbu^0kKQU=#8~4lhv|JNKqw&rR&nHmU4Omjif`k zc#KTAoO4^g@(5|=-!SB>n^R7HDS9G==gSiMOOmcQ3U;E&PM{Y2qe8}@1=38(6X(<& zi5D|?SS8k!Do(2scc$5}c-wZt4_i`AF7e)t*Zx94Usf*Yp3G;Xv5#~dD)e?H^jPm;DCYB9-`jY@NUWH~dD5oo||_TExJ8F~t9W%r9_-J0(I6{N8T z3q@CZd`z?O5_CyPm--1yHpn6RNQ{a;jud|!cDvf=VfAuiLwph}8cXE{6o;6kNF9~& z$|phDH~qI3TnvC3fDB!h^^W|Dq)UzZP3f|k`mw8BPHE{U`Lpepz?UXE> z)7&#Tl2I)7UIY z7|%swvI(H4F{BZ;@0i z@iRkmA9j`?k<7`jFgE;DbV0sZBGlr9TZP}&D=Vc4i3!_u2Qqc!A%#DQqKtAN$wGA2 z7W_kNbSN;xy_EeqaO`DjP~YqL&e!iD8EhgA>{8`P53a$@l_@WoPZQ`F1vt+g-NoI$%r9_O&dx0-@5PS|2!xwZPO(t=4;K7-_Ng0L za4zcE(a?Y!xcp>8+5v)Z6N}cyh^mf-kO39u>1YY?!%W*Um6-@3v$7+!Fz2z1$T#Q*(|qPE|bS4X+ZeB z#cts5ZlJ0M)hBR$OpDn5tsoFfwQ}v#bOQKpOjZiHI^c(`cXV}t&&AjJTvYDAuA;sO zk#PIAG2yP~dQM?0=l;6IW7|=P#Se;2(}D;=$wJ+6abe*{T+NrN*UQIjNavhL$6q8( z_>-U+$1$lV`5V5}^J&nJ+K~qegdt0ZlYO;HSNuYqMWYiWZ{h;qe(Y=FJa+S=V_EUW z7AH5=6|oJ^`G_!9po!VG?qpNbG)p>9amRX{9qaK#gEe;`(L11=PlmF0yrf^*HWtV5 z{1Ubyy!_%^;D=XXtEkG2dR5d8FbMYMN{Qw=b>uoWoxn){G?M$uqD}Y97@7)DnqvTr03AcM) zRT&r28CeunSi0c(D&y8#F@7IjRGL$7ifhvY*G-4phfs&`H2fZ@Rw`3>q;SdThU8k| zSpc|E&wZ?Q#U%fI(~D}=EdJ4p3Z0`6nWSD3K%-O=Wwu|Rp|1Es^udp@R_k!+aT+vs z{&oE2&FJ0{eGElL1n5mjJDb^hS}6PV^%s$j0SD2U|FU6yFQvda0lVCb1OH4>xzoc_ z0iJOb4(L~QGYdX(HTsi#bg+|Qfom9OJsMiBtY7v~!OQVYS!hZuBep;grAogeTmzF(0!qMf2%vfZ+SKYwI! zd!@9%Y5!gQqBFnqz|~m(8?VR91$1C|W1T{^XTN{^uLdW}dD7TA3aO9nUrqDcIMNf#~ zo08K`$w@4Mb7Lzt@niK(I7;VKuyxNU>?@q=EHEF9mP^iWaV{D!i?O9djC|$o$`MBf z=Ds$Rwk#LCf)VCUj2rnR>Rxc^W!lr}7CU@;$IZl!Q?7RJTKs99qM1-%f%J{G{12J- z+C`O*iGzWQJHdC+t$9F$E4TB*fFkLn@k_Pcu5y*i)LK67);i-m4Ywc6c05^JHjsoZ zpd6mhX$hm#wU{O+Lc7?ky88=X#C`$&lXgYU>lT%gUGJVCkQ*#v2m1A8#~&Z=Lja-_}+iGvvRc4i*{}^X&r72{j5# zFMPJ!{1TaZW}0fF2#mG-5Cu8dzqu)MOT*B)#J>AD3-~-j^7)p_E!W2s(#~D?=~Uv? zfB_h%Cy0aR&)2a9vlIyXPy2<5VQG;BKj*}_D28R0fKqxK-}c7QRMu!p*}Hvn#4ymw z6(W#1VlSqn!^#yUfE}x~;$VzJwP9@^2^w5fuF*|Nb4kK5MLR6FPSfy4(4cirww$;_ zrPG!7I`8M&3`dH(aO*%v2q{;$%1K0X3KE0bgj=c15aPXY4ikkbyLxNSF*25NH z6fFB^st6tL7=A4vjl;t3vP+M71rv7)7QLe6H<2R}U%mQFq1JD9g3^~857L>g#!cy~ zWC4` z0c_tP|9HWPe`o~F^n0eg-0Pj%r{Ym}+T{hKbapq3&f=OgN|Hl1hS~7)RjqN;H-`q} z3YRhgnL=)U_lEf0)Z)PBdypNx$KhAvZj>P^UaOyTK;idz^yDzqG?$vdIjC2#cVNF)kKY>S zv7o>%A-9u1jf5obZR1tFL*beOoWiMJ%TNi!!ybO;OI^yN?)J)B2DPIx4p$%U@N-E< z@5DO~H%|;j50uA_xGS#2ikB3&9Vs1H zl>xO8r){+hbd+A7n?LSptDyELRwQ6>emVnjCDxP5kw|poVqc;s6?Ke%bY$I#%y8l3 zx|XUOmxdz}6K-;|V=P7uAit8}mF4JXtJJsA!BeSH$w_P35-Hsb>0>@WbmkQr!x_p> zr?K2Bm4W;1LX(bhb@}mv1o=YKcx`za{$IiX{8Gr+rg(>!@>WUGmt$N8U8BHf<>mOV z&nb1eL5djt`mybo5&tksHCJU1Pv59G-G;hxZ6!YEczzfWyDR8VY_iR5>FX{2qbw5m*iv|xyB8}z?zWu0=v!4U=fCeipkpn!GNG=| zR4fe^ThfqPp@EbaKPL0viU>!u`qOa>oW*2~0rcK6wj_f#Vcjj9DLeis|C^AyWC#n! z^?z(L5Gfwdf{BvxFw^O&2)wnHFq3X1l<+L{pNtqXg{}un$d5X)K|#l+r>ZrjC@g&i zE#%Vlg$FA*ViVeP&Gz`Nc&+gUt>qAt7#Sy`MK4upQf!!Xq*1;1pQb>n@Q)PJ0jAm2 zmO@YhWc_r;2P;`bDh8q%E|K~peKy>=w#_@-%JUH|LuX!BsJYNmnKAvM+nT|vP{(E3 z1dNb!Ns~L0wsr@KW3E6(opT4w;+<#3I|{jv56SSHP1=;Jl;?VE7{UE|2PUJw&+E6E zf)9BQRi8}c*6-Vex#1L^Y!Z2r|4>d|XD4iJ?So2sXLW*D|y&s2eq?LdyvNUeT48oWVxsn*^D#4X}kWG5YZ z$l@1L(y>zUx0PxiiB~fSl<|%*^zdmbE$bxXId`v9P--;kQy(WqtgxW&k>QG=odMk8 zJ!wEGPTfdi18ui-^O4v_1)52RxhI)PKNis1OYG!-@pCccj-14_Nk)j!SG*u$hlO5Y z={&>sbKMxcp60%n#`xLHK|F$>vymqrvLG5&`-4*atD(EVVYxGV;JDqO6NVe2q>pot ziGWeAR5FaxP3dMlduFylo@VQb@o^^qFcT1BOlM5*$TW|2nV&hdFmVmxMKoih!atge zUX8U-JzRzAH<>SYq6yvH5rW-hYdO}M{*yi#o}!`I;;O^ZGo3*a)?e$V&DXd!G6NlB zd{Q`^cB{FWB%vyRg{nsRt<=%;WgQFEc!$$PHtivat;Gb#$O}1U`yKLp%Om=*E^I+; z5D$hF_6gQNiNVh)=v3O8gmnmT6TqOh4q;(6XOo7*NaVc~utb0+$N;12ZOD{TZW&O$ z+bhE3cJZv+$EbjN(~*!UQ6Oh#{INYBF34$3u4E1@`V@qMv)U^ojfR?@%~KVANVc*X zbDv4KOT2b#PgvZXJ!$Il@k8*W%zN$=up1wv>BE}1=J4;|3aI4W?Fc zr{4}(8ZqE0$7mRq6;B&{&_D9Aje&sL#KnUBmF0CWc^#8dG3U$vh0yIOt3x-iQUY)9 z%}9N8u{om4Vsao4WV^UKE_w04JuFjrW~a;(wU=1hyZX|eGcWvv*e7uZ<1}UGy^=w+ zwYsw=_tG0-nX=z=X5rU+s2^=HH-Af54(wsyd>1~aU2BQJnmuda3*tFk8nIMyUs*gi zq@Z7E;TRS#z`oQQLSz)d6?E|MhM)mbPyaUgjs6|7}Vdw!3TRQ=-mT_VHA zX*bPEB{=+Uaz-dHN^$~qly#%zwNu_hiD*W53J^(8kED?yR;Urn{R>DJiF9AdTi^${ z&#l3M6%2vn)@q{)q9{B{S7sqtT5-vR*O>`Zm+VNm>U>=+vvF8_RV3zouVmBJTO(RK zP2nmo;C%mg9HmWDeaH`+(iL~a-L6)SFVD@xz1I13Y0rki8#$^=fsoGPh1G6Qku`VdZ_mOdCc z*Lv>n_{q*9nOSvzYo~X<`GidcWda4(gyu9)8We9^O;tBESGFF@hpEiqF=r@$Yep>} zJ#y3Qm~iAudU{>i{o=UL=I$xroEzuU@qmkOy{F#iZ7H{dgfA6*d+V7B=2=g;cJHH` z!8GOCGr+$gP>?)XjR*!`#{U@tb;nMWyIb&G#`B>8YMd5)Ixc^lmo)Jeq)hX_^*P|Y zkqcyGL`6EI&^mIpaJJtDIlZF3v%)*M#7VKv@weel;vl;BbC>l$^QIMD#EW`OPAss= z3WvaFk`{WMAZ$g(M7PZ)!53UaGr`zuo|jLj2&?Y{v z(}Qo%!MElCWX`=%sT>`mgJekQzGhI3}JM&u3OQNh%;9Pj_pW& z-k19F*Nm}}@(v(whelYM*nKd89QNSv=?>77`t|>S*X7M+|1cP-sahjE+P0pKL)64R z%Ln^TUrfMy{nwK37Zf6321OkOq!5vIs$0oqdkMiWFc(N5V`KKN84ttSP!oFh@ zbV)b0iFt?`urE%JId8t_(Gn2kf)4i zRbP#wBxl^DRvoBK$m_;zf`uo`@)8hnkY+CP>9xDoVLLdH<3GJ(pMI;h^n z$`cf>+a;7EU4y&03k5lg{HoqM+rG&J$QzEynE}f>&E@cH>I^o2=8hEhlUYlv7}p9Q zex5AE#N#$o#Hy(VL&9oT^YJSI+oPnL-2=zsLF!gRTFFD!EFP=a6m1q&I|;0W0ud%D z?`y$Ev7q=;OY~*xhj7aVo#I=gmY;|sx_RSGL{7b+LDf6ie~%3xc6##-4K~T{ zG^GQ&r@^l0Hw;&DdwKE}sG~luw*l&MIM-ED7lK~>muapjNUI;{=6FVI~U1ub-@%^2K4ZUKX0@=Ds#uASmxKO^IGnY=|U_FP!yfsu(oH4^m!FE_0-QV8Senn@0;>* zX~6hos#ap7eno%=?pFAinHvj%I zpd+-s1|T|HT=*?635t+fzozA`5WR*Q6R5jHesOiJO#K6#F11qAQ1f_K4F}eqG+%C^ zzS5!^?SQp+ELLV)Ppo`;)$*x^0oJDv%0N{7N(a||_G?9RrgvxZZ4Tg4bB7LDd?n<% z@qX-(Bj#9HidR)}65B`aipsk)pSO+Ridkb#=M`sGQ$*-3*DLtr)qqfL3xjTpPd~HN zUS~;wTDDia}7?p z#z;%9v_DnB)k}Z@^K8tdHq9RQn}t!;0QqW(R8F1wbDRry>XgZJ^aDWpgXr*}j|JDd z%tA)-f|kG!XYhgm4hOAi7+@fTJQ6kygSV>1BmoLUn-vV2!9bbwhaq`y_Gs>ucgS8bi^oR4)1c!35?4D@utwlj1F9wUQwUWUsO>A- z7+fdi^$^f_2xQX%R!eHzzPkMGbH6^`VulQ_s90-mv;Ej379Z}Qj_NmLy3)}Q`VfDN zCd9m3_VBg2+1{I2Zr@!skzM>-iP#jyT8kSDs>0V8EPc>`fM8@o>ImcXQK$1QmfDUQ zIuRll^2H%0V4?a&zg&PDwvc~G%=v@g~Es?6Nu4)<#z#3#dae9ScGsMP2@%bBpjl9W08xT=P{zyL>d%ok@sws z)^C$E1d!aLh;^D{l7MwW9&jBYC#x7<^g8!a;;8K^3GebJ*ZVhMaQW}#Qqd>j|3LaB z%CG*VylJsOKmFuUurKoT=z3ddd}!8A;<@?r<-B0qVu^)`iG=}@8qte~f1Kfy02<>} zf`1a45&RYSvLYQ~niSZWNyX_Lyo*LTCOU#^^jG67g+(`G5|^+V+YDJOK&{Rt4Igw=MET*_Dcu~bdpl*G1`tx z`LJ$F6+h3W*wfl>1379|NoA)z>hBT{1q>%%neCaivoECYLSdRg~-ERnDGQo%{TGdHt>x zMB`VRO*fB{j))RwLo9(Bef_9#v{WCfYe-5xXXSP{%kzDB=yRs;g}?c7z2*vnX;;$j zUEGHzfB&GGOG%dGGW@*wti;#)~|M#JH*2U<4An_gW@k>)BWXJ z8T|B`m!y!Z6_pY=>Gu~0%Xi`%CzP+Zd|IAL@wlWralL)~wuyG&FPT`-TT)(WIo0O@ zK_2&p2jMQn|3a{OKnhRAL=+X4w66c5Qj^T|SRhHJg;mXfvc%xLeP5t<2kEJqY7)>S ze6~D^n&QwV*Vd1%DT}DO^Pvcc8;!hal3FXBth}NAc}mf8Qs#>f+titC<{ZpNR8n}c-puo1_{v^%EFz9-2EqkgxO@z-P~ zkk#b|PehECJqDuH}zwemlT?)tB( zzKsL;x(A~NL)ggCog(+>#*q$0+-MM$5)o0CFv$^8qK*(oh=d{_>IiWn>cqkxihzxI zW8Cua`~&BQ?>VpYy3Td3&->b_5V$+=rj`^yfq3Yvx4NerCW4xqquj*22nVpunZ}L@ z9#=QDA>K1B#1lgq2?_~Bh!)sq+ZyJ*S3-_79l!>s7PXwQEwMhb&+?yrGH+~sxI(eF zDaB3cfxF2c45gqtEYAI?8WdT(lG%4nSJgV_(CX`e`m-h~Mkpn3{n|c=cGn{+?xz&+eS9zsW|1op4QhoAF?_vpi|*KI=}N{}eKCEbsN7HLHj{>U!<%wdQ7q zu=SE6SBt)2(U*xCBMYhb_tAR+@BLy7Elx0DyDPL5N6X(R?t4FPbn$)g{qqM=wJetnESu-_|&6>8TQ797j>5 zkIlz}K>lPF6KRk)UWHUOPARs|TFlbfne(P9psdSJ|JasA;uK+Tu*T~)sv!GKT@?LN zs^N)m4d){d#qlDX(!5#As;x6f`>w=VMw)t9sh@$m`8xDK~*jHoufmrw;%WTPO18%`_$f`oa1FX zh*S3E`<{Fp`*g0l0rqKnQf~9-mzCa12xoOLIw99>>6n77j|l(e+=IKU@OM|gD^~ug zpt$=iJwNrd8MV;^H>(V~j9MH49fwovVtOuH@MfzmqQH0 zU^e4PA%)t%hTWj@kC`T#>R+Y?ja9#tPJmsvWPII2I#N$!?nxu9%R*kO@svqDw0Vkh zuHGEEm1gkEHavu2blx&%>X+eS<@PM22MuXAjsDxdyV0*bD%MPW)81lZL#@-b#*s(j zZulitna5g{8iy|&KsDE%mx3+_WW3uxyy~s{Lx5Gp15~x?;*q(l<`>VoDL1pPb!LQ^ zrQh#XfB~iy+xsA@rfxjb;c?3>H%RKdhYiw%Hm(@y@nD|Hun(M1p((-2p6S@Ngv^^M z=Auc()A8A|DSbs&`DhnEA7#E-L`OX`JZ=(n2;bKHXQjwvd+UTsmf-xadmXz}`^FEg<86hg$}_H}itZTOb1YOU z0)1Ax_2FVXP=6&3MzpMIOrSe!yyn%gZlE`>J%D+v-o&kopPl+>z)_!)WfB|iMIB}%8n zKR3I4Ur^ZF#n#s%ff)&{syB53=ZBP9LGyRQP{aPYAgPJikHc(RaF&!NpFSR z&>j1|>+6Evp`ogXz{6h@tIF6)SDgL(O8YU9Yb13+hQ9`pfl0*!(RPlAHcTAFBK)29 zup`>dLo#=sVo4~T2ZB9US$2F`Os)~g`fJ=eD~!UBXCq0rLy1+fLX280LX3=%SSaV) z&~2X1%586*?1@$Bc8IA*^H&J9{iYaG&itMnrNX_#v+AVZMaoB5O|NEe5Hy6ICS$AE zceB7VJ21Cz0=A$0P`Z5fC8^c+9)guj_n`d8pdPAf=YsBk_P~Ii=tkLcdRa18r#_&H z+#-pTvl}A(fGg>v!m#&~!66sZOT<6UUDG0~WtQ;B6w6Zj*dDMoKj30qvu^f(W#%Q5 z$I)l{jwN@}c_DldxgrD`%V(-R;!hq!!Apu)Lm?MqCgE|JrE?#Am2EPf9@wPDbBF?l zI`AiKqLf`We>QJ=@+V9sZI$n{PMc6Xlrp&($~c;5jS8*Edam0;4~U;`M!;5TYF)h) zj@zv}G{L};WEB|0E)Q6HORhnvZfF+R3WFQi>-FAh z6IK}CO?s#GQzo?@#GR=%z^UzuLX#Hz;8_9+M2A1AQC}Lkqi@oG?>wt!9}vIy+&AqG z{|H*6F^ICZhxa$G`O3tCT_ue+%Z>vBw~r=rqCnB;CL#(8RJu;C4ynD!(k=nU6H%6eoa zt%esW@bG|_F-L56EX;?P=W;oL0ii(Poktjdw&%1{YRw2Ka}YIa zq){i~^mC+;)gahz%30g{H`uB9Fr=cR7gbQ)xBlB~75aa9yHwXtB_up`rC&;?Z)*z$J-QiEUVze$q8i`2bx+7!}k_z=H{R zu}K)Nh04*);l+fT!7d+8I7}P?&asJbe{h?Kbnmx9jN6{3o`kehL+8{>vFYp7-RiU4 zQ%-4BKl_E815u8pDLz1*ji$oEGe&56PU*H)#hzzUjA<#)xNxt zk*o#q$wlm2vkp{|&)Rgunwf9D32LSc`ePS}(!5aB2E+Bjp6a0tz+2bajh zs?j7B4ThZgtZ7eT=il)NN+}}oG-Cg)jk?I`usJ3L&|k%()!y1T5kdt}g{D zhcVTr7=&{v&Us*a@yhq;v)?U0xXkF{`Z$VYxBkBIAHLY?yw4~=Dug0*7q{QPm9z^m z?3vk^L*}1D--c>QR;#=##J(XU1Zz`7%a??z?UYTG66T18X)=qic)M-A?XaJBj zf5g3zaLp&W%~xa%d5e1vC!4=5gFIjYtByX}2;@E%!wOh+m;nvP*LX)?O9#l8!x zNnnG?wpgK#b~9{s3BE`6$VCYIMbS+*%v~|glR{M@0B>C#-!=)o<(r&hpn5~(LO)hILx!d)xR)nE1Pc_v8x*Cxd<}7) zc(iiVARPr!@-*9R3#n?l3tFF;b|m>}bXM<|#OkNCD?Ok&<^g z6gufqn@{ifF?%8G1nXzCK{ncUNf5aUdHRcO|)bRHZSTSd4V z)!XX_7>hQp@Y?e#dXYl@U~75ibVQYhUr~lpg<$HIkCUvZ*k!BnL%vZ}aMTpvgFTMnM`!Xk{fm+jUa?=b#8ckS;gjz&tGB6WLf^YGa_X_v5 z_fu@SZdpQTZJ~!U67tgEp}i-f*XkeJ60cQ6kYvg69Y-zf%$mzC%wd@ZO^7NNTlKo- zN69p%B4C(tTeTriaK$)SHQMdf^Xp<1 zCUkw+u7++Y_qGg>Z3Jk6(C_xD{19u7F>$iy8?nZwhga3utCN&KgTVC2rqE3pwN6XR z+uEBo1Km*Fw(4^f4IPrAElIJRg&tZpHkd;b5|QHZx5J5&>2rY}!Yyy*V5^bdix-YO z@lv}dGeoj*8ViJ>W))wd>JV$|MgwXJ!~3C6-AUGjjd#)F?S|j-H4N;EeQ(*mI?ES@ zZnX*vIj^`M9FfckZst$YCJH@B+lNSYgAxaS`G{rl++UQ#HO_ay4f}YN$o)!$Xc>)! zgvbE*hbvK@XBTHLpc>z!}GhK43+pH@eJ zP9oUibME=Y#MW#-)vg;^OcKHT?bKcKh{p`uKvw3fH`5=yYHUajE3vEDnbHNlB{ z&#>1EsW!S}w|P3zzF(<=qc0^Dn==T^X4P4@YNvhKhR6Dxx804s-|Ii(k~;|PV_;sk zr+Nzy9J-@WLsTtI5wsC?v(t};c|Y5U;^EnE-Z9nfElJXN?z;laX^$84k*Z8wo zPi0dk_WcAdn)_ntD$dsOF9M<5xS&J2w>7_LYfF%?-aSQ~Ay32CummB(ygcDmYKYDd zq=&MD&Zuvhf*i?`Urh`DL7#|dR}hE4^Bjy&^l#3g8La#FMEds_CQKC8aco(8v~`+~ z7`R16x}YlM&@V`$BF4*LR49dkdLT0y$YT}1hYT%TyF`?5#t8G*x!;AVqs`9~fT%oH z>(_pjG?Gd*OO3mLcNM<%|F!+^0%Vkd?RBGWdIg)=og2$S&tv$1>#g2BrvLe&WH5d6 z)k2cqXGV?0eyvyGXlsgU>GsS=o;Bz!1=&FAi~H3ifT9&`MfGL(*Wr9K&0jrU+$!A7V1 zU+O*#y*ap}PHr9m(F%s<)0sVWJpL9`@4p)B39@`UoZctBtf9`FIKY1!KkF^su(@K` z;?mw6H{!7~8hR;pUE{xh)sYtN{+a3aNl8V9%M=VKPhG|pC7<>%eSBA^_S=40=pjO$ z9J(U8t=6J>QZ?~ptWb=OL%Jgy5Y`0mtC|J{^5N5Ha*y;1b5S79MQ*)!FLYLu#71 z2%o4cc%Ig*I)7AR)uEDg^hD%1L|vvFCqAV?D!V`NI+%&41F82LcNR9oCdOyOI7P6E z)urg0yHi51lC+5Z4x(0PgPZY==WnlDX!Xl~utkzoeEXHP}g^)YJrSpLyx+mG%zc;>*Pf4=N5zM0x|6C8=et%waCil5Vtf=6!qX75=Q)Z-T1 zlE7%WMOD!tc7=XzoW{C4zV!ai>_S{1kC*EnYF4!W@AOkS=o zth`>jWh#FUp{Rt~6}eX^py~z$nZ7N8#V0o@Zvd@S3ZO&aj4ywf#se$hmPK$ zDC^g|pX!?m${JKZMb>XW_3Q6X^xEwXF$cUpvhUp|nbQUFJh~0oUvk}iX?WPqDG(pb zu-=~A{OGCu-ruW6YLWwtPHgAVb7Q>d6I|#-v>eAYO9> zcI;ESqlVSQ1fJwHU7EQlIbcfAjU!I7UmwHa?qt*WKzH8;g>5y|>y`0b!3r^se!bb# z7xO7mx*cB`i}`g^y5rFLx*MDgb&|SWQ&~q^6++*RhhJ6LI|6;UJz!%5G=|{404)0F zkC+Wl%v;@c-lmt=;yg?{&M{cfmV}lPMgO%pirsyLZmIcblY5b3iv-6h zu#x~k)q(j^Qs7Nm))Y2G0*F&K&zGHsPh!G68np6O-5Zr?aemq%{Fi8pGqxU-6G}Mh z1SJ3Qqf!;7Cb$HcmJk+TT-ZL~(^8zzs*TF3NzzDb{xi7|@ZI4Y;vskQrNCLqiYw^A z+;qp;tq5(39ag85D*P*(7AyUxO>K5r<1!R(St_;toCrJ+%)EQ*8b2$Lz!ZOhMe+0R zNF1NXfGV8C>p8js_G7-sg6z{`0^9+kvwh==g`VS}7y+RGI zo+8SdA?+(t%bvXxm3XWSGp#8Gc8qIYxkJ&+oPJ|K$g~UqkK4?b0(TTVh}!R!J5R3$ zBWOfj&%3XXm8}PWw=%r$+mq{K_b)l|W7TQ*F1)~ey=i#rFp>>FNm3O9wGXFL&Oi|6 zKNvg})tRe|!ho3Un=mF`2V70WXX&655mp`q8_dV^q=mJXX+aJ@i~huEO$V(U6H(o3 zes$g0?OK^N|4Raf@c!RP*tag~RmdSz1utbg2zaS3mc0T*oBp9BHCFUJDn z`ye&>BxEIOV(fjN5*+y`=Ps({fnjk<%WR7mKee?$J8Kt6rAFY<$nHuNt=NR&+#omS z9+S+axZBp(edFi+raO>WdV4*Aj?(@ge1ea^|21<-8+Y$ zCBoLDG!Y;!)Z4p*^7W@i&L0HG(bLB*U7|qj7hWWYDE~(?mF+WTuI#43(tVrAk24%6 zj*ai4)$6Oqg_La!x%x=^#h9!qm~&rq-ewUD=}OnrH~M(21ppyNrjBYiNx%_(6W9c4 zVOW6|tTTSRK6e?YQk&vwDsoeam5#pH5|?+i8~SDSTlZ!&i=Z&_q}4o$osih7LI~uk z&Gr|)Ppt^?{dw(QT9^^5^i!yU)r+6r5w5(Lw`%uNu+DM{g?WMN9{L*{JH5Hp$_o6e zh`YXp3ZRr*wm)Z>kQ!uAR|ZN^(qcHvp4Pv-jP*83>*y3gooeS$QOa(xLwTMmUE)!- zr@}^u`T)!uJ1BlS17$o z{=0(O-hKz!L@PGL^?Dp^la>?PUi72Rip{eu)WZ-~ z*t8Dvr?SVm;fTNSU;^FkQVS7dmlCY1odW*&OE|$0ieSleJFxUP-H;&9n`8}7KFswK2YG!y5&8v zKW8EZ5i-ubnv>YDeciDXzg>z4MN%#OzWjf5`4!c_2DQ;lz$Kz!ch@ytZQT2~A;=6+ zGs~8ZxVa1uu*;6|Myg#7VnH{60K0i;5^`9(CQVMIj#|H;xHMqHwMYxO6isnc-Z{6oA!E5kqecX~ z0v=GIiD5ev0wjp%)hG63peO}u#=m<`QjEsJEF^Bdt%!!^f0~SVw&9z8WvqBm5(WQ; zq)jq#K0Z2Dp_R|`j->KE_0~%T-L9e>1Ih6XtyP8uvtKGxeQ!XJz%nJ=OVcbFl5)lM z^%h*0;Tf6-!G-u)!;~=_chbGK>&*|F?Kb9-qH*&Sg$kb4lBK){Mj>;s@3Qi=7WZe} z2{6mE@KNjS-CNZ06OtA0T}g5WiBp3Hi7Dj`X(eLBRB~vfdc#c?>RV!}t)M zHTsaz`EhHXq0zMqQE*7e@VKt2-qQoSR}dNfNiS|)nvE&g{@)1Vnn`u{U-g0~YW;L@ z9)-EbIcIx)ztqdFz-?V`6z41}jI|Ls9vuV@dYZR{L$`yV_a>F70uwDM{MTvFpM3`E z_~2Ti(iUcLBo&rUfrMnbT=@bDdI%OCbXJ~@^!#0pUas6qc-Hmt;;zUQka_* zt7z+HC`CM>6ao{;*8IXP=eGv*XE+FRKiWaJWQ6bIB!%vSsC9rGsK^j0%v)^i1F?eW zdip>j*IPWGul7@>EXylBa|}E5{SkF)Jq+MmDYL449McQR0XgRIYoS4`+I`|yb_U^*Q%2qAEuK{=N_&HfepMeoy zEd&U8T0vF2h<%RT-|ha%i0-&w;Lp)k00k}xAwSMTl3;54vmuNn7Umo<9VfQ{fPbh% zr_;e1bVNF{{OvW^HbBLDHMyl+M}INw$&$Yf4m|;2PTNm!wBE!;G{ILi8U{^?w*C^0Jk!&?oqE%|guTMyu_#Vvp<}&*Yq|(shN? z0+U)g51wFXA$ImX58O?Kq-8J+W!UGIa>;&(h$M0%i@c>DeqLZUU>PQ+$aN7>uYQ1k zvvRv8L78==pwpm`M5u8_CW(*gsoI}4}mO*wI!RCe0UjT=20BDs7?=8 zXUU*-MBE9Q0?iY9bQOCZfGPQ*p52E%O}1%wuo(uXium5Cp2(Lnf@>Y9-qJRny6JN2 zrs0R6$pg0A0Go8hJ_U=mH~h5H@7nLWyXlVKuJ2ah&bxYjURt*v+Qx#TgTJB%;qZ8| z{nviL|3h{~GbOi~&F><2g{)tPT#H6pj=wIN#0c}hg@k)~R%LSi} zc%!?nPn8PTD8WH%n6e7lYQu1_UOoByDD)x@b5WqRfZOvS`P6&EiUSShT|~^E6vSLk z0K^D0;{`AI3YI#8sT2DB9%?OIcWbJPdRvAyJy?xqFez}i0~96bP<)ujELIT@^iW<@ zdft~{;=!tYbKn43iykq|N)+~}y6|aySZ9Che!CEg#1;FQV{NG5IpgZ~)|CV{3q%3? z6wief$8S0YTj)b8pbfTLgTE)W=wI6^rmmxx>EtJU*s?zOHt)n#{+7a*?cxl%oM^NN zIn-^bLJdKuQ6Mk@z>B@dIs@CG2Y#Kynxw*lGyeGoh5^BmQQt)s61ALMJj2qn{1@R` zh8!fRX z=->yRQDVcB}{KKOqVnKOhb`eVWi6ZcW5Y z+YFfa0k*6L-+WcH=>~uky5-N*`RF87DP5!U4tik{dx9VLPuCzuCC-kZP^W`~yJ5S4 zW=pbceY+ejU?}Mr=!5`W&MD8K2uA4uH=UhS3OO{OPGm&g-B4JhNEK66?}w>|2VOl$ zJU=EzZ+n7>4NwVbhPjCW-;eLsQ|y-zVZqUIuVo@X<<5je`IaJr<5YdFK_^gmv{Kc3 zU%Gn6P-pd>#*?41tzs;MrNA9Bdb)}oqsX1QF~WX2Jl0Ie&d|Kvyzx-y*Q&~E5V2#p zgO`j>jvufcda{nZu-SHQd(T-r`xlI!pw@yp2W_*eIxC@m)>m-wM8QU*PNaHiSA6<$ zJ?(#lp!nSJ`4U6C%hri%bU#jRjSoF0>>Z~f$NPGPn+KY>F?XK3D*t@2)hYn%d}D%GIEV&v;dfjIDH}&0_Jo3Tt_9h_ zczUlD?pk7RGg0r7064{px1@$}&YNq2?+*;A#z%ytKMy zxNjZ%QZ^-W8oJj4lh`^XQjv!Ma1j-AHWahB$+LXMc#bG{;b`)=)ub0VE5WGR5u$gw zzs<2s&*lq_`?shcI@a}yNxhVTe2zouPfJ9p*2a_Sy+;W>zQ+e<2u0o3C+~P~xaND5 z52jDrZ+xtt1;EQ?-II@yam0gn2`96aZNHGTt{X!NxIIBFx9G2gR>X{MZ1kD$cEMr0 z*8A`%WslO8ELd98_ZB^3IP820;m`PWC$LtN;&uV6dk6v(k}*>Ud#1oW{7v@sNZ zP24e&fdLc1E9bRoV!e+s78T-SpJT`=A-9)uGE+M?TUbHIzJm;FPA?}QD~RAez&eyv zV(24&p*ja+y=0G3stvB4|I1Yt0S67oS_whM|M+ojZy0$xD|kfXielz`SFlg0a&y$s zf5YPcel?z>VsL=FMmVSY*3*?XWEZo#nD-KeEghl}xAk+%(FPg-X!tUf+z_dH>R?z` zXyO><>X-l|mZBhXn5@*g`?Eli09MUZ+f(Z4G^V&AH+N^qT`-axtwcQmBwtv?p8SRF z85v15KVHsKr$Zi|+6-^XP(QqFiDCJFN!gGHHxy{B<51mq(Auh~C(K5=tn>VMrz9l* zBCxwB@A~;hZ(Ey37mW@s{2ZNU*qSksW=yQ${KdHv-)oO-)3ckT!1d@s6|k1fcHS!xAECTUHYRXlFtV4oO{;Q}pH&X8uiP+0m-D zUA1+?BS~Gg>i5E|);2aA);<>_ccD5mkoN*^ZG9<(jmp;TVl0u~$vK~ia$aqtx22#b zZh|jO*ttGbc%Yn^f($PBTGWzq5Qlh4mmBD`Zc@KqjnI%ppe!P3yBam-Mu+CuvxL4O{wx?_e@WveitPlpCb*ZWP$&FODy3*+RHfVfu~Wg7Rpw~@Kc z!y&JxKRle=IaqIXTtBnLt60S*`revA-7e=|{tos9v*)&m>+PrgvVC5kOZeRO**|kn zba6yl+Gm}*Gm-YO=b8I%zyfO<(+Bd*ppo4XCKj}D-xXgu^yF{UpcevpO2;739mrt|_LdubziKg3K0!!x|HnDjUmg{0O-4YF5P zgiT-TV}jiPJ1{aVDDr%C#wk#%+pF3Vr_SYJKx^(kyc*+j8OPz)5B->p1jbu0fx|?| z5Rymw7G(1No;j@*YU9Hx9KRiK*YS4@7`yKOU@(kLNu1s9=;U3BeY7I9jPH8TbLr%6eoNP!?j9;@hfTqqgbhby6{a8(V z5`e}$<+L|R=@(W(SK-s&VMmKV=i{~nz#@e%++?1V@sO<81 z{KlkER06AaWIQFcF*$3y z$xY|Cz18NMvD~X|msVrdU#XaK*KR4Ki&hrX{(V3B>6oTmX@%SBpMzUno#*=~yGUHz z*@?9|Es{6-nv%uXI{Hd)23I>_Rg6 z!PZ{JtN4~R5=u23@fk{u?PFu3n2)BnyCw*p zp;RAh76u&%UnSx_!b6?|oF{&cZ`7@S-0Bql>zPc&zE5s<-kzbgV0ZWu-D>xU{9hXd zbo}zZd&C=g^}pD^e&m*-$SudhlfsXs94Y^JYU9rTiFargH8@SNs`dx!Ku2Ddz+RGi zQky(9B+*c6fxDW2M4EhVR5A5rR_AVfvf~pboK&n-c&rP0ahIq(+COltK^NZSS{uz zH3u=9EFt=+bJ!Yr&A|J5$#Se=DBgc)@OYVy!ci*1VNneQINU|rSMWkJ0w#Y(LWvHB9kEkYvF`9Zy@%UTeTf&)|8C-#}CbG{8ne- zuAdtShrZc>OeKciU&iHCO%Dty+$*a2*<1lFFgmTiF;A;IJZ)Ettt+xWOUMj;! zG9=`*W(-`j7jQEsp(SggkK(w}hc}K^fY=DZZHg3j#tZ<`!gs?QmX4Cea-K%(I%3Ek zD#Who6(P&gN1>ntq!J#7@6W!AX5T4da6Jw*Ft3&!rotScZ{hbZ#{Tf{yW~N73*U|N z20f>^emx`z?3^#8Jbq}jmDF}vB?NAxGuKs8V;u3Ty7ND+EFTcaTFZ#Zw92}w2=(_c zxGUq?@*B${q*pmBlf~zv8Fl{W{4Gmp4m$8~||L760n89~a z$eZ3`%u;VRds=IbYyT@Xbd{-t&E%@>LZcO3U!@8-&vR!2zZklFO_eX;UmW@@n5(rm z_Hay(IyD$2PL(~ojR+g| zJ!GYk6?aD$>QTD%4a|Zc+dNRT1@ujr|LEe$e;)(TV4R!bLBULTdlhP<{TBw!jB-~7 ziR)_9bk&X;tw)b`{deDj*-W`c8dE5&ZcEVkM3Xl*SSN)CQ2?O367S;+`pOrZ&Kj4= zI?mnlNwBb;h;FFjXd)_&_V%nQsw}|#daG1DHK=F!e;$1t>nj@;fMcHtYbJ6&seW7Qo++5!Ab03F`QXkxmJ|J4+Q>V$hYc)Tz z2ev=^H*foJyyNkLU%Dru&x%}>K^vw-130GeOO*^Mf5aOHCe0Erz27}~0C^Z?&v%E> z^5V>uHRMQKc-A(l0pa9u5P=@32IY7O?im%YLm;xI^`N}XiGZ#1yM=xI2gf0?6sGP9 zdonzT2b>YIeKdhWJ7T8FFJc8lipc7hTwR4lgPR;)y}p0rGbFFz4$|`y+*Bq4`Z8-G z={$+ggTeOvulo!+#Tr*6l76$e^n}5g!>3!OP2Uf{4yj3C7m#esRPP*3Rxa!C4Veig zk1x^pwW^d^E`+!FAFKA#?>2B@?G9eeOUVPPo!0lmOZTZ{roZZ0b}v!u$v4ZKN2_ef zDGJitq3W;s*`m?XE4G@!4NS?LSO>5$Evqzf1Nr1b{<5NL4o5!*j7yi)1G#QRY5doN z=EGNq{S5}3xZ~(HvnW zX`H)94L6m7gFEG}(aR0gx6rEBLoF{iO8Wl@}#!`w;GiwKYt-EnpY$M77M|vr#c}y-> z(SYv3AALLz!&(+;I@g`GEZTw~<*gQG${_Nsb*ULesl={BGG4aSWJBinrCy4Ya#M(- z`-@o_%zAr4m5H(Aj#ZuxtuLNl6eg%*$HQrBOh-#5R*#jQQD*C0Rvl7qISdWPo!Mz= zMn2ftzu9No_&^c=qNNP-IY8Glu5B3rek1m2xl_JOYg#tZ{c)h8gF3sruBS)$$E@;bvg@Tvypp554XW+25)_7D>d*nkI{p-D2bs({ zGCw}=#IEh$q$=cC9WCBMFS3?S$S)A&MQr;6T*5pLPd!OPz)cF2%+5hdXGDa_lcMYq zBOH{m#{6ugO~)H`l8s{WCSR;#w?4YOj#Md=Axxi4PICFq_pE`xJoaV)HHN)B&aw7p zbCZP-1`A-f0`rYQ#a}Os6ruzCppnYxDc$NDx?Jy-;_1OM6^){hJdeaYmjM){O9<%~ zDQDJ$-I+z(AbP~&!U%rhcGE3c0_0&MSh#QDc2--81fo9A)UK}&qXQT?;OTW!_9h}I zimGad0Fap=1u0aA$=g-Raa@IdPv_7iqm(t+%|_04T5If+2G&|p6a^mg^OUR0wfk|i zR6j^E-zQjNCu}BpySELBu6YiWKl9&6{gfOE zQ~>qvL}ZB}IRxZ5NNwL8IOO!8I>+xVNLO5_i3m8MRCx_`Sz=7k?_*B>)HE*8d3ghJ zD4V-$hNm>kopvl;PjfTws!#aSE?=*skAvDT|WJz`0MXQx1{jo1rs zI2sbdlZ!0HcN`y4bqp2iEC3$JtM*wN&j)!}BsjlgIKPhq`KX_!S$0q2rI;57hQ041l`bWqeHd!U!YI1W_H^zf~- z3;=m`+KG&{6m4}Yc1@(fUGfpme|SekmpNznekXXJ2D`vPWq0v)D|BfJq3$h{T6UV7 zoWlA-?0q{b_>#dk7q^)M1l#EC=Q=Osp>YS+k5|N?DimbQ^}e$^G~_QFFyC&?-9~|x zJ%tLr#0N{{4T-#;Q@S6of<~ZeimN&rd;!~hDyB_q<_)K z!=6v4Z7s7dJi+_1Fc3?HS$YuDeLVwPK2Rrr2# zp)W4i&Q)G6w0O(#*t184okUdB#soeM45Qw5Tw{KX14L2KZpF&hunI5d+80z@J`d;RUZg z?jDEX1ShmsfQoNI6perp@vOJuO?%i)ntX%3#qvc5n`nUTGiywmly`c{Oi^%ppws-iUwnbA(44@t zKyxD^@d($a|6H*y1NEIehtQD3z_g;d)D z*ZbseZa;8Dr?i&mbEWU;3-Fg_FRbHa8XOH(PS8Z**?ka0rar!#Yn>9poCIxZ zXg}W|Z!gmyNxcB=zo5w<)<4YKvBq+(2Q=*pLmuG$%+QF&aV{!x+$3@K^E_REZu`wk zx+_2>2J7Ptz292mL-U-N$364F_mKSlXgX zm+y@0Tbg#IkGSq&10gm zVkW<+C>LH(56?fD>?eR|4Hq4d79N%s`end_R=lnNwjoxQ1BWZ^n>jiNW=cCeTL^!( zwpx^wvN06)*^^G=gL458XC??=#c6!ra35d#FoP99L8Kv|KS{J2=e_^l$&@>c$e|X3 z_W@6+s~2CrreUp7da1l+!K}@Xl`NM2#e-YRe1Eq>TlTPmGaxv~V^EATbGYOE$Sz2jI`uy*vdsjr*&KKY<$Md7J36)oQ$ ze|$sdSU|gxw8b#4vil}$Do^Lybp?C=a9}+oJjKS22JTmh*+I{Q`7&?RkH^oi+|BH> z@G_A7KG8LHQlHFz{k=AxTCD)o4GCirRslFEbS8vHG$FiT1Uw$M`VgWFN*O_}un3Mk z9X@Z9r1U!NVFaLXuW-r1^rM5zM|MzP#@W{uBGJS6%#kOIl) zt0~gJ&{}Vr0P3Goes4uxeQzrvRBf;DvOtpgJmpklO+xBjb-s%oxbpeIlKr8rD>wlR$=qGqFg8cYL-LS{j z)VHV0S^j;$UFW}Q%!fIdakLR}dtbE$zXYY%v$WTM3npMnJ!^d8)njz~0Hd;pr>Rc| z|A!BmM4k>5Lp*Q7>{6DnkeTSo07V3IQwm-k07+_8gipRVZQJb;1)}AFb>0=){#2B| zGj7nsvhHKjWSmewOqXA1o>D3Iv&wxP2u<8pgQ5U{vb%rqJ&jn7GA0(6x50sQ=q z{rPh*%WCase<>W}CO&huHG8+6e?+Lu-mTrJ4n6j!R+%Qx6nkI2gZDWRI&~KS46}y#vu}$tiW!qb-hee&o?_1Aa*xfnxVbUmA z@A}?vwj+}dKRv&+&$HsklSS`pgZ$UUSMQ$Nde=65KI_2B-DEI7P;~2#jnCfqmj@D0 zm)q?xZ|VVP(+|<%=m*A`*&3{o^EaQHZFs8b7x(Rw!~c(>Gx3LN|Nr>x#w?6w82gNU zZ|rND#ZIz?B+b}DXe^ba?#$TN#!@8p9ZN#0AxW-kX6!aZ}r@!0oL@SmSK6Dp$K>{STtE*e!rYK)8B zale&ebsW9%mY%u{Kdcwp9$BGjm0Owc+Jtw+ARsRIx@TYhi{_~t=^M)fr!N8tk$o4; z|ITLe{eQcacwnycO14mQ5Ars)kx&gbnylKAyVy!kK& z4^Lz=#?i*kyY2FOdVN7a2U$MbG(N4-RYk6sNXA;bE(nN2*w{Xc=Cn<3z3To5IP#px z9iT5K9)1rAV|Of7$?@=gVUqcHidAypT~CvDqLdMx(h~DkLe_sw)QXcjvpOPS{Pm_sKcZIVT0a zZuHj&_9aydD|O4{E%uv_cuyB-*{x5zVGR>7PYmfLE>91|Ty}YG_hwkgmq_NNB8)pA zpjWEjn83lKrucPG`2FmKHP5@*n+Nt(i5ABtN5ad8uRYxAkhQtXSn|fbhOAKdsVHw{ zoHBF4C63h?R0C_i2WxGLES@UB%}sW=Cv;|#ixLC9TP&=#Xns3=rf&|`&uc{!k7Oav zt=F?pi!QQqoedz&)cLb|5&K5o|Sv{jUvR5ofTdFnP#1WdQ@6= z7R~gp)lCuH0yj;QLay+E+1515L|bz!nIhAJ6J4s&Zd*V;uIE>OhcUyz!!SP{aS81r z%YTnlQH=zE9ptBArF-TXeX^;04!N3I`)-UG-t@kUFzm$Dhhbe5MxsAP7TU!~`sJHf z0cU%|_glqRQ-^t{C+rZWl_N@pjd?J{+E6Z{XKqjFf17Li4f+-Cnqs4hWX+x8l>9%N zdMO?_0~*B|qLg*RQjGDYQ37RT4k8-K1%bnKsj`h7iixjI(fSzz_8p_(x~xmdxNW5T zyl{-dB3&=iCXt3PuYMxtg>pBu+iGE21Fcj@9|uCb;4gC~en z1*eCHK(FY6(;*@jujGcT&_l{#uWxP}9q91fp!7v^SLbr|4aW&qbTWz#x8;=_>*PE= zk;2Tud9O+&ZV$=6n#@Jl)}wQW2iQ9zUoIqW`qKSuaJDgNJfw0lqjxyh__iD9Xr>6P zjVD_@#{gtFB-{{@V|KW|{;%a5X+gGctAw5QN7k7TUknT%6P!bz5lN(~ltL3|5Up+u zc7*S&i%QON>lMk!=?gCD@^no02kvTNWNBRn*m_q|ejZuT9GVOY!yNZN@q36Me&L_A z?%|salD7iPKB`Gs(wM=}|5Pp%60-%+T8yEd(*i1571UB-oBdl{?RK`23%x6FCF3Xg zdB#MjL4N*~P(PW^XBuSgaj<)q0VRR!bXDz*T=VRc`QPqg)z|cAVtRoW0;95|w?#ty zJV7dBZB(xYC%nfyxg+g?(E(GpFR7RkFA%&V(!Y*CR7S=5F(LKRt(^MaZ-kOF7hS@m zl%CkJF#vG3oPvwU#Skt>Bx1&BaO$C>wydEmDN7*_`8nnc8MoLp>A%j}%iBoNU%)5m zMB26%LE28DN+!iB-y5w%(b3qw^a>?m*_oSlm-i z7fD!Dla(UGPjJ7vaVurYM!zse|8o+`cBP)s;c>fEMI8Zq8_a7f+PuqL@Afu0wauzdl*_Gzbx#sLQ6iybQ)gnv~_$Ll_ zp%xASNZ95oI7Sg9=VPpAn39LYi>zDj(7m83#G!Sra)*0P$Z@jt|N4v6vu(W(44NV0 z#qqg|aD)>qIqy#Kd+B{j2&tkJX}PT)jnmbmn3Xb##9hY4Q~jJh2{e`lWKK3WHsTP2 za%b|C_3Wa^1><|OC~_?VdkYTHc+x#@0n&Xyw{=cR{k!`6nSV4)H(Uod!&WaAN9?F- z2Yk_xOdY0zQhILHWm^VPCBdHe@Qc_it?VGqHP-3gz}ZrLG9XW5%-v|drC)EnfsGs6 zb!Z=>X0>*i9xC^LJi0;B`TYcho#)zTA3tC_D1!J;Qc#_1@8J=cqiupz$sZg1%$MAv zkl$#^9SrCcPh4omTOhalQu4PM4{b<5chghthiMDQ*@^eKoy!vrU*(|KvAZz4j%L|g z#HddXqrqFfWO-|DgJd7FASaab zYIUs*PV^qQp6@YcEEOFv5Y^OV^`F~L`A1YmT@&q_=*h=lA7wyYI|}Jui$lI zUwlZ;-yqDwOxuQob$%2Zc`-$E4HlE;52lYA+;CHNA4;faSGG za|63pwT#`fi3>@Bj=_8Y%NEq?>(lU4z`REsm_-9BvXIU@UcuF7{&%Gc=Hp$ zd?;v4+aIAC<~T`0IA$d{hoyfAn<%58J!`e#f>fTH68y*=|&@^v=BmaGVeScgncRg#U#Gu43t*N3E!V-wZqMr2j{B;Eu0CuyEqs@nI)I@d2El^1w;u&TpIo$#@~>I&X8i~k z53qm`Tac+;K@f1V5-=hK*Vss|Xlo{0MP1$f#kK~3zwSkbOsY6mQ?*Ea`rDEoA|dKx zj&(?ub*z`;WR{UUm9FOU!=TYGp=d{UUa;5L-Eu6_Qj}#l$&v_U>4Z>}!~#o8uO$73G89Gu1^mDauK@kVDjvJnt@_?V)? zZvdRhf3m-1mOqU-??30rmb)GNjEiPFUp@6N%whC?8k%suh~iS*ZIjc|uff`yK| z(r>28p=TKIgbT37Y6^`-A-d9wN^h$f@}(hcZ2qoX#s>BS%kBwb zBP06Wo{_$K*M>bb18LVNfPE)5Pk8_zOEC-avbPoBYr(`?uo7+;JLrZDq?rDmFkGjC>92Q68f`m4 zRBs=Um}1f>M98`(NalX}FULj|Hr$pSvFx#Xglp>s+s1|M)!8mw@07A*g0bRJ?jWKA zKhA^g=uDFAwEGU`DkfGJrPHS!+FCl41USHr%zMD|52}Kq=*A}{kdrL^ncxbYl9yY+ z!=`S@VIx{0E5(-MGd$9Be8{56n{3O{nj;xhvSr#pHnZ%O{Ltn8YGQu1K3t%Z5@MVE zQN_vKbeY5&8xMKTFXjRC3@;lC=QG}q&-u+si|686nHLPoWcRWO`ed1PrfC8bG3KLz zqAKCoZ{BC=TfrrIDXsot49d)_a~_g3qmN<7-Apc{)+oCiWXVhtD0VVR^t3krLT&Bw z11m~(;*tN&1nSQ$6QZ)?;r>=ZOBsuwOahlox+%N$= z?7K`Eyq4u4W*HLePI;@yaKN@4@E-51yVY4Cfo1M*L3v75A*7rN&5z}XLv*Zc<%pKXbadaKd(W;{gtl{T92Wg^rNb~Tv0Ve za%q&yp0(*ODS9|+IyPZm^g`O1eC{9!=}0&v^v@U$HtnTE21z96y9J@@QjEvXUe0?N zd--K03)AZ+6;dIG$EilKWuqttG@)feg>qE<#sB^P%p5dcK_($q;{`$ZuGJO ztQ5v4YaRW~qKQ6%EW{cNkRD)Nj|l`Ex`?V53SGfUwS6s(Asa1A1tXM!={38_W(VI! z2SCt3t+L}5#XD^DcG{&bl*>L8d2F-Bk9YLPy{spm1NeEc^jMaIEmEeLlKq>Rp3inZ zMK!AKYnt`c%3uCI1-|E<9BL~C&W7tYQeIyw#@~zD39t3TlqJkE&qP_ZUEJ5x=h(JxKP=8M)%G)va#$xiRSx*h zO*;l^s|Ak8^eL)zgNf^c$r`ZxcJ_f{>QH$$Z*0;gz&x1zA`(~N#L&@mwk&c26#lk6Y=0WqC8-k)Vj(y38kSIT$PT#GY@ zcj^3)tt-FIz$am?^Ug%o=|9LHUQk2=DraI{&LnL1FyF6dkDp}nMImyng(ptLfUqHI z#@c)-p0z{x1?6Un@mR+W)OSUJ9ir|ZVCy^lu2N9mtX8_k!fuhUTl{2Ab)fb?h0EiG z@ccI(CoZTxMT!mee>0DND=PimdQ2iPIs*XrHZ%m>>h{i(>1RJZ2)xw0=#ul&LSiWe|!*2-2r1CQ1?Olh|5$bNLKW#s}h`Y3?4s{*;nvC&bMwM>K5bT z_kNq|EZZ5%j`5@b1}9r_FG>a_0u{&j?vuC!T%U8kbBo^xe+#b}kM0|z>;cRU&rxx> z;?ab-xJHrM9dFHBgBC>9nnzhWFGcC?EgkgT0R=GQGh3pcB)|M(-d2LN7lcH)ts=hupSmwU!rg3=kJ0|1VGmCcs{O`OqcQ=)^@>0J$Uqe`r$4KS;7auSu z#&+>R%b<_(?$%N8i`Az$17|iJQ(eYOvGdH!@?-#BWV5BIwjljy>;mJE>rk_mw~6bj zS>X=qI=i|icW>5cpc9@kfWfvke z(snHllZ&KX^UQu*Zww>ayYg3G-n?~T-&4V;#TY_iY&75Xe-b_1EQHQBP?rwgT?zVW zwhZ8Qjpk%veEBn=o`SDia$wVU?d8WS# zB=^!qVS>g>2%8p%jl@~|puT12Hw$wq3(VUV@4HB9Noa=V_sweTq{Eh+O<8jl3P1@4 zRWSJ#(S80gUh*ADB$-+Y8kO}~Jcn+6lA$A+rk#)OB#A@(jmH3Q^wlpk-@RZWZT*-p zBg&U>md*n>o20%Lo#}i3`JGN!x9noi)`=PYw8zc={mL<|9QbR8;-~UHv9>CZmc$~A zj11h`ci-vU&%O^q9%q|=&)r?kYcjZco-y4P_mA8SyT5947PchD9;IGiiF^J1vx;Mj zc<@XE0$u7Ce=_rdslIH*6nQxEJ1c_MCT`L42J23O&z;fH<1m;U?^!`o)jsRM`x8 zy>Eu3i)Q@!1>J_?KXow*N!JfxOEQeHK@iDFCC{MBO~TYLZ80=qbM*|r~lg5%Yw7%%nmSbop4 z@|>vvqOyMb@$)!69v|_y=$glsxWV$mqrB{hGlq7$fYVJoELu(KM44-iJgHQ-ChnTd z57p$bQ$JLXIs?V`FHg>HkUrq>#n@ zU7IP5tym9eL@NPaGJhTKJ}S&|f42_ya)`#b{Qr~20V;&dy`&ZBpBL{g_c4r4mGv4o ztHfe&Y;gv#Qz*~BpG`MrK@ZXdNKX>LcV49Yf{3*pTgch%9yE%5IZTbg7QWTb)9Mgn zZ=aWuIg!qionDhduLE=U&1vb5u?crGIx4O#L-ZUE4NC17wCx)s95yAP_cog4pW%3^ zZdH%kwsv4{6>Ay!vtW*6yJ`@IrFy_1pDd+Ww#a|44(Tg8`emxr{LA&?GfLjHKPl3g z9H7R(9ErgiAfj+eCh`)OYT#KWNsrQ0XdQM|2V5>qB*M1qBBkmy}$Gvk9kU zz}7UjZ2zl4>*9MRS5I!LyoUEpCj48pbJ!({us%w2wjVB8*;Qa`@!6{Gi|mh`9N6Hd za$=8#$|h|!CaM9b^L7cIAg`&EboK&nF~H{j!zHCToB*Ef3+ulZ12gB4nNhyi!3ROL zZ`*((Wj9;7gOVu(!?Gsp;4FyGDEN^IxgscVcZN4CbCn5z__yaJ#~U>Y13L*u!8InvG&Bq$l~JZ)ZEOk0kPyOrNIBb!SCfN>ilALI#OU(d8y*Jgd> zZb~=msy0>c3DMOq!M{B5>Wampey+?x8FYuc%MpnYgxnsWEGM%;<`;e_q;lKzCI_pw zga;q_=vO7!e!nMmp-AaPh;;6()}H_JT~$BWn)%L6$(@4a=xqAs(k%6H(L`q@-XwQX zvjLSi2GO+eLmccE!XEG`jF+OKP~Rcd5U}!OF&3LP2I{@Lozv#Jn{lU25+0J=PSh-o zUJ$QW*nicTeDuufOaAAWh_9vR<9tg`&j%!qFag@{=*S;45`w2BvBTz9ME}7EMa&>b zA%?dP>HVT+N02X&KL0u|xn~e#`fpR7OlNOZd_FWJ0?1b102_zkU5M)BqCrNE;!A)v z0}9g#bCc|TUXSH7nZz|NgrNNat3{eN63X;=8^6H#eFM0*ghd-g#hx*arK)%uJ~N4i zQ!iZpm}9<`PoKx*^?-8@f7432s4o;)}O_uRIvFb)eka`pX-*dGU#-|OcY zj1tgS+iGT~3?;PRmVpAgc{%U82jXZeQklOqG(2k&!GGF0ZKSM%)zNi3Hu}Qoqfhvs zJv;q2u2)=pvi;~Uop8diCqxScn8t_ZkfcxPuV24^>C5YRi>Jup95 zzqI$3Kh|3*C0OWNXkq_g_#g<9HViCGAuVB*QpG4;uYKC|@m?TR!Zi_&>Qxx!$|ws1UX!ugP09+lk^F zt5?rbgJA~EUXctDXDTziZpj0;0Hq1T6dCRAyR{7%!;gpR(6f^nT90I4?#BlRu}aXx z(OBW)R)ih(@Wj4llG>k?yct`+duG2!sX;eG^+xin+N#P>t1tQw&xelO|5jIdR54fG z?`3_|yZ4OSnM)RL*4EuNtp?cqEcNY>1|^^c?g4MZwE2kJeI;F7;|0Cqf-cXU+w1{uN16(l{3GgVT zQ$QreP12a=PNJQto~$t>!!7|B**8&b{HyppsDJT?l?jAtR7xwYmWNl2Y5VAU3>6Ad z2_>P^$;uhp>pgWUY-6`jz%E??t&2i(2-5Fvjc6mDu0W`5OlTVk^UxV%m4o@`Hri)F ze%|yrC7#O+{n$Wj0L801JE()YWZ{_|O{ZTGZ9~I;z0c}>8u&@{n%bkUvqdDr;vA;G4RFnlO@H#^{ z@OOu_I)iK|1^Qjyj329``5^b825CM<#OJYp*8Wld@>FewEPLsRbSfFUCc>o%nni$m zVV#wJG1?eV=E;)66rinXIAgMU4cXj(TGmnzbm&+@<{eN=uQAYXb}79kGCge zLb=d%f*Z39n*zBlYhlJBqc7bQhW6DI?ufC2d=AA8-VwLwW`b7_2V+%;IRwD?kYV+B zD&9=?3gK(>v5cV953fYv_&SwR;Q8CYGxZ|*w7N;+KhQv$w`#M}r9yx^Nrq3C?&bfl zgA8$EZa7AR0!i{t0M&d#Wo8*hSXflvj=RvbSeVsYh}MRa?fG2m}P4Wfl(b))#+sA+b1--1nN`QjrbOh$a)#dk_sHy z(FXdjGD71|@*dFvx|Ba%2!E~I^k*yIh1*SBp*Is32|_Jgc^+GwN1LPm!eW^UmU(vLdgj6Ne63UjkQ zPJo6IpbTg09I|4{DmI+ny+u5*R~F(7^lZ!?gUL`)W|o)Tyh4~VUhx>CXpr)7sj(ST zyDXSm2(@OaZE@k(y)ggC*YoZ4xDqgFd1IFhI2S@=0e#=wKPGTH1OOBXCc6ep zt5f#h(WzW!sy_e~a+czVCB21y53h9^4U!#KMd6RLtgQnoTqIGeGEk`;*1Kt&QD)iH{!t(EGn z4Z0y1>KC{_%sQT=h3O}t!g5Sz$ZED?*%VpFXE!eg?s(0iZcZ{n_g6SS5CKPrg zz%Rj$EUVSr+3!$?sWcF*fD-}E^)bv7G}K@=KGV1#tUI=fVxcol8M~i+@S)_69fq&% zv#TrR(BCPLtv=9~5tlHCN+unBF5y%t{ZtPDx`?^mAy7=C$8bfWgZd*fx*fE;pGDv9$^j8{|D+lOG~+yVl{PC*hOflmJP>gMSSnsn z9etsieamQjgZz0zr5bHXQTT0Xq�|@vTlH>?Yo(@}8$riBt@|cW3-R%GL|m!VAwz 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�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%0D^KKPT{a>-T^@_1@30V-P@B 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

y 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

k9dbIp@)!pzDs?ev=d$NHKa4eZc`jxvPl+q2H5{ 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%0AYdpUb}s#^8~Y*_RL7LcaTtEL>@f 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

TzBWfT&Y#h9c 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

I`)j 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 - - - - - - - - - - diff --git a/crates/gpui_pre/examples/image/exif-orientation-rotate-180.jpg b/crates/gpui_pre/examples/image/exif-orientation-rotate-180.jpg deleted file mode 100644 index 9c095652a1697a63ce5b6eff0526baa4b51bafaf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 378 zcmex=M1EUawo9GqO-Km}U`7?>ED znVDFaSy@?FfU0YO@(e73tU`*0j%>n#iR?;+B1Vl97jh^&Z9FI%bn%0VaZ*teCzqJG zgrtbvx}>nyN9P&a7buactm7Wa!P7idPZheaY<=ec|~Pab4zPm zdq-#2q{&mJPMbbs=B!1Fmn>bje8tLDn>KIRx^4T8ox2VlK63Qf@e?OcUAlbb>b2`P zZr*zM=<$=M&z`?{`Re1R&tJZN`~KtSum86gI6y&b&+wnY{+Gt0{r4YrX{`T$698_d BZD;@h diff --git a/crates/gpui_pre/examples/image/image.rs b/crates/gpui_pre/examples/image/image.rs deleted file mode 100644 index 30160c65..00000000 --- a/crates/gpui_pre/examples/image/image.rs +++ /dev/null @@ -1,223 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "../example_support/fonts.rs"] -mod example_support; - -use std::fs; -use std::path::PathBuf; -use std::sync::Arc; - -use anyhow::Result; -use gpui::{ - App, AppContext, AssetSource, Bounds, Context, ImageSource, KeyBinding, Menu, MenuItem, Point, - SharedString, SharedUri, TitlebarOptions, Window, WindowBounds, WindowOptions, actions, div, - img, prelude::*, px, rgb, size, -}; -#[cfg(not(target_family = "wasm"))] -use reqwest_client::ReqwestClient; - -struct Assets { - base: PathBuf, -} - -impl AssetSource for Assets { - fn load(&self, path: &str) -> Result>> { - fs::read(self.base.join(path)) - .map(|data| Some(std::borrow::Cow::Owned(data))) - .map_err(|e| e.into()) - } - - fn list(&self, path: &str) -> Result> { - fs::read_dir(self.base.join(path)) - .map(|entries| { - entries - .filter_map(|entry| { - entry - .ok() - .and_then(|entry| entry.file_name().into_string().ok()) - .map(SharedString::from) - }) - .collect() - }) - .map_err(|e| e.into()) - } -} - -#[derive(IntoElement)] -struct ImageContainer { - text: SharedString, - src: ImageSource, -} - -impl ImageContainer { - pub fn new(text: impl Into, src: impl Into) -> Self { - Self { - text: text.into(), - src: src.into(), - } - } -} - -impl RenderOnce for ImageContainer { - fn render(self, _window: &mut Window, _: &mut App) -> impl IntoElement { - div().child( - div() - .flex_row() - .size_full() - .gap_4() - .child(self.text) - .child(img(self.src).size(px(256.0))), - ) - } -} - -struct ImageShowcase { - local_resource: Arc, - remote_resource: SharedUri, - asset_resource: SharedString, -} - -impl Render for ImageShowcase { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .id("main") - .bg(gpui::white()) - .overflow_y_scroll() - .p_5() - .size_full() - .child( - div() - .flex() - .flex_col() - .justify_center() - .items_center() - .gap_8() - .child(img( - "https://github.com/zed-industries/zed/actions/workflows/ci.yml/badge.svg", - )) - .child( - div() - .flex() - .flex_row() - .justify_center() - .items_center() - .gap_8() - .child(ImageContainer::new( - "Image loaded from a local file with EXIF orientation", - self.local_resource.clone(), - )) - .child(ImageContainer::new( - "Image loaded from a remote resource", - self.remote_resource.clone(), - )) - .child(ImageContainer::new( - "Image loaded from an asset", - self.asset_resource.clone(), - )), - ) - .child( - div() - .flex() - .flex_row() - .gap_8() - .child( - div() - .flex_col() - .child("Auto Width") - .child(img("https://picsum.photos/800/400").h(px(180.))), - ) - .child( - div() - .flex_col() - .child("Auto Height") - .child(img("https://picsum.photos/800/400").w(px(180.))), - ), - ) - .child( - div() - .flex() - .flex_col() - .justify_center() - .items_center() - .w_full() - .border_1() - .border_color(rgb(0xC0C0C0)) - .child("image with max width 100%") - .child(img("https://picsum.photos/800/400").max_w_full()), - ), - ) - } -} - -actions!(image, [Quit]); - -fn run_example() { - let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - - #[cfg(not(target_family = "wasm"))] - let app = gpui_platform::application(); - #[cfg(target_family = "wasm")] - let app = gpui_platform::application(); - app.with_assets(Assets { - base: manifest_dir.join("examples"), - }) - .run(move |cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - #[cfg(not(target_family = "wasm"))] - { - let http_client = ReqwestClient::user_agent("gpui example").unwrap(); - cx.set_http_client(Arc::new(http_client)); - } - - cx.activate(true); - cx.on_action(|_: &Quit, cx| cx.quit()); - cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]); - cx.set_menus(vec![Menu { - name: "Image".into(), - items: vec![MenuItem::action("Quit", Quit)], - disabled: false, - }]); - - let window_options = WindowOptions { - titlebar: Some(TitlebarOptions { - title: Some(SharedString::from("Image Example")), - appears_transparent: false, - ..Default::default() - }), - - window_bounds: Some(WindowBounds::Windowed(Bounds { - size: size(px(1100.), px(600.)), - origin: Point::new(px(200.), px(200.)), - })), - - ..Default::default() - }; - - cx.open_window(window_options, |_, cx| { - cx.new(|_| ImageShowcase { - // Relative path to your root project path - local_resource: manifest_dir - .join("examples/image/exif-orientation-rotate-180.jpg") - .into(), - remote_resource: "https://picsum.photos/800/400".into(), - asset_resource: "image/color.svg".into(), - }) - }) - .unwrap(); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - env_logger::init(); - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/image_gallery.rs b/crates/gpui_pre/examples/image_gallery.rs deleted file mode 100644 index 16b9b17a..00000000 --- a/crates/gpui_pre/examples/image_gallery.rs +++ /dev/null @@ -1,313 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use futures::FutureExt; -use gpui::{ - App, AppContext, Asset as _, AssetLogger, Bounds, ClickEvent, Context, ElementId, Entity, - ImageAssetLoader, ImageCache, ImageCacheProvider, KeyBinding, Menu, MenuItem, - RetainAllImageCache, SharedString, TitlebarOptions, Window, WindowBounds, WindowOptions, - actions, div, hash, image_cache, img, prelude::*, px, rgb, size, -}; -#[cfg(not(target_family = "wasm"))] -use reqwest_client::ReqwestClient; -use std::{collections::HashMap, sync::Arc}; - -const IMAGES_IN_GALLERY: usize = 30; - -struct ImageGallery { - image_key: String, - items_count: usize, - total_count: usize, - image_cache: Entity, -} - -impl ImageGallery { - fn on_next_image(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context) { - self.image_cache - .update(cx, |image_cache, cx| image_cache.clear(window, cx)); - - let t = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis(); - - self.image_key = format!("{}", t); - self.total_count += self.items_count; - cx.notify(); - } -} - -impl Render for ImageGallery { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let image_url: SharedString = - format!("https://picsum.photos/400/200?t={}", self.image_key).into(); - - div() - .flex() - .flex_col() - .text_color(gpui::white()) - .child("Manually managed image cache:") - .child( - div() - .image_cache(self.image_cache.clone()) - .id("main") - .text_color(gpui::black()) - .bg(rgb(0xE9E9E9)) - .overflow_y_scroll() - .p_4() - .size_full() - .flex() - .flex_col() - .items_center() - .gap_2() - .child( - div() - .w_full() - .flex() - .flex_row() - .justify_between() - .child(format!( - "Example to show images and test memory usage (Rendered: {} images).", - self.total_count - )) - .child( - div() - .id("btn") - .py_1() - .px_4() - .bg(gpui::black()) - .hover(|this| this.opacity(0.8)) - .text_color(gpui::white()) - .text_center() - .w_40() - .child("Next Photos") - .on_click(cx.listener(Self::on_next_image)), - ), - ) - .child( - div() - .id("image-gallery") - .flex() - .flex_row() - .flex_wrap() - .gap_x_4() - .gap_y_2() - .justify_around() - .children( - (0..self.items_count) - .map(|ix| img(format!("{}-{}", image_url, ix)).size_20()), - ), - ), - ) - .child( - "Automatically managed image cache:" - ) - .child(image_cache(simple_lru_cache("lru-cache", IMAGES_IN_GALLERY)).child( - div() - .id("main") - .bg(rgb(0xE9E9E9)) - .text_color(gpui::black()) - .overflow_y_scroll() - .p_4() - .size_full() - .flex() - .flex_col() - .items_center() - .gap_2() - .child( - div() - .id("image-gallery") - .flex() - .flex_row() - .flex_wrap() - .gap_x_4() - .gap_y_2() - .justify_around() - .children( - (0..self.items_count) - .map(|ix| img(format!("{}-{}", image_url, ix)).size_20()), - ), - ) - )) - } -} - -fn simple_lru_cache(id: impl Into, max_items: usize) -> SimpleLruCacheProvider { - SimpleLruCacheProvider { - id: id.into(), - max_items, - } -} - -struct SimpleLruCacheProvider { - id: ElementId, - max_items: usize, -} - -impl ImageCacheProvider for SimpleLruCacheProvider { - fn provide(&mut self, window: &mut Window, cx: &mut App) -> gpui::AnyImageCache { - window - .with_global_id(self.id.clone(), |global_id, window| { - window.with_element_state::, _>( - global_id, - |lru_cache, _window| { - let mut lru_cache = lru_cache.unwrap_or_else(|| { - cx.new(|cx| SimpleLruCache::new(self.max_items, cx)) - }); - if lru_cache.read(cx).max_items != self.max_items { - lru_cache = cx.new(|cx| SimpleLruCache::new(self.max_items, cx)); - } - (lru_cache.clone(), lru_cache) - }, - ) - }) - .into() - } -} - -struct SimpleLruCache { - max_items: usize, - usages: Vec, - cache: HashMap, -} - -impl SimpleLruCache { - fn new(max_items: usize, cx: &mut Context) -> Self { - cx.on_release(|simple_cache, cx| { - for (_, mut item) in std::mem::take(&mut simple_cache.cache) { - if let Some(Ok(image)) = item.get() { - cx.drop_image(image, None); - } - } - }) - .detach(); - - Self { - max_items, - usages: Vec::with_capacity(max_items), - cache: HashMap::with_capacity(max_items), - } - } -} - -impl ImageCache for SimpleLruCache { - fn load( - &mut self, - resource: &gpui::Resource, - window: &mut Window, - cx: &mut App, - ) -> Option, gpui::ImageCacheError>> { - assert_eq!(self.usages.len(), self.cache.len()); - assert!(self.cache.len() <= self.max_items); - - let hash = hash(resource); - - if let Some(item) = self.cache.get_mut(&hash) { - let current_ix = self - .usages - .iter() - .position(|item| *item == hash) - .expect("cache and usages must stay in sync"); - self.usages.remove(current_ix); - self.usages.insert(0, hash); - - return item.get(); - } - - let fut = AssetLogger::::load(resource.clone(), cx); - let task = cx.background_executor().spawn(fut).shared(); - if self.usages.len() == self.max_items { - let oldest = self.usages.pop().unwrap(); - let mut image = self - .cache - .remove(&oldest) - .expect("cache and usages must be in sync"); - if let Some(Ok(image)) = image.get() { - cx.drop_image(image, Some(window)); - } - } - self.cache - .insert(hash, gpui::ImageCacheItem::Loading(task.clone())); - self.usages.insert(0, hash); - - let entity = window.current_view(); - window - .spawn(cx, { - async move |cx| { - _ = task.await; - cx.on_next_frame(move |_, cx| { - cx.notify(entity); - }); - } - }) - .detach(); - - None - } -} - -actions!(image, [Quit]); - -fn run_example() { - #[cfg(not(target_family = "wasm"))] - let app = gpui_platform::application(); - #[cfg(target_family = "wasm")] - let app = gpui_platform::single_threaded_web(); - - app.run(move |cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - #[cfg(not(target_family = "wasm"))] - { - let http_client = ReqwestClient::user_agent("gpui example").unwrap(); - cx.set_http_client(Arc::new(http_client)); - } - - cx.activate(true); - cx.on_action(|_: &Quit, cx| cx.quit()); - cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]); - cx.set_menus([Menu::new("Image Gallery").items([MenuItem::action("Quit", Quit)])]); - - let window_options = WindowOptions { - titlebar: Some(TitlebarOptions { - title: Some(SharedString::from("Image Gallery")), - appears_transparent: false, - ..Default::default() - }), - - window_bounds: Some(WindowBounds::Windowed(Bounds::centered( - None, - size(px(1100.), px(860.)), - cx, - ))), - - ..Default::default() - }; - - cx.open_window(window_options, |_, cx| { - cx.new(|ctx| ImageGallery { - image_key: "".into(), - items_count: IMAGES_IN_GALLERY, - total_count: 0, - image_cache: RetainAllImageCache::new(ctx), - }) - }) - .unwrap(); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - env_logger::init(); - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/image_loading.rs b/crates/gpui_pre/examples/image_loading.rs deleted file mode 100644 index b6828915..00000000 --- a/crates/gpui_pre/examples/image_loading.rs +++ /dev/null @@ -1,232 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use std::{path::Path, sync::Arc, time::Duration}; - -use gpui::{ - Animation, AnimationExt, App, Asset, AssetLogger, AssetSource, Bounds, Context, Hsla, - ImageAssetLoader, ImageCacheError, ImgResourceLoader, LOADING_DELAY, Length, RenderImage, - Resource, SharedString, Window, WindowBounds, WindowOptions, black, div, img, prelude::*, - pulsating_between, px, red, size, -}; -use gpui_platform::application; - -struct Assets {} - -impl AssetSource for Assets { - fn load(&self, path: &str) -> anyhow::Result>> { - std::fs::read(path) - .map(Into::into) - .map_err(Into::into) - .map(Some) - } - - fn list(&self, path: &str) -> anyhow::Result> { - Ok(std::fs::read_dir(path)? - .filter_map(|entry| { - Some(SharedString::from( - entry.ok()?.path().to_string_lossy().into_owned(), - )) - }) - .collect::>()) - } -} - -const IMAGE: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/image/app-icon.png"); - -#[derive(Copy, Clone, Hash)] -struct LoadImageParameters { - timeout: Duration, - fail: bool, -} - -struct LoadImageWithParameters {} - -impl Asset for LoadImageWithParameters { - type Source = LoadImageParameters; - - type Output = Result, ImageCacheError>; - - fn load( - parameters: Self::Source, - cx: &mut App, - ) -> impl std::future::Future + Send + 'static { - let timer = cx.background_executor().timer(parameters.timeout); - let data = AssetLogger::::load( - Resource::Path(Path::new(IMAGE).to_path_buf().into()), - cx, - ); - async move { - timer.await; - if parameters.fail { - log::error!("Intentionally failed to load image"); - Err(anyhow::anyhow!("Failed to load image").into()) - } else { - data.await - } - } - } -} - -struct ImageLoadingExample {} - -impl ImageLoadingExample { - fn loading_element() -> impl IntoElement { - div().size_full().flex_none().p_0p5().rounded_xs().child( - div().size_full().with_animation( - "loading-bg", - Animation::new(Duration::from_secs(3)) - .repeat() - .with_easing(pulsating_between(0.04, 0.24)), - move |this, delta| this.bg(black().opacity(delta)), - ), - ) - } - - fn fallback_element() -> impl IntoElement { - let fallback_color: Hsla = black().opacity(0.5); - - div().size_full().flex_none().p_0p5().child( - div() - .size_full() - .flex() - .items_center() - .justify_center() - .rounded_xs() - .text_sm() - .text_color(fallback_color) - .border_1() - .border_color(fallback_color) - .child("?"), - ) - } -} - -impl Render for ImageLoadingExample { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div().flex().flex_col().size_full().justify_around().child( - div().flex().flex_row().w_full().justify_around().child( - div() - .flex() - .bg(gpui::white()) - .size(Length::Definite(px(300.0).into())) - .justify_center() - .items_center() - .child({ - let image_source = LoadImageParameters { - timeout: LOADING_DELAY.saturating_sub(Duration::from_millis(25)), - fail: false, - }; - - // Load within the 'loading delay', should not show loading fallback - img(move |window: &mut Window, cx: &mut App| { - window.use_asset::(&image_source, cx) - }) - .id("image-1") - .border_1() - .size_12() - .with_fallback(|| Self::fallback_element().into_any_element()) - .border_color(red()) - .with_loading(|| Self::loading_element().into_any_element()) - .on_click(move |_, _, cx| { - cx.remove_asset::(&image_source); - }) - }) - .child({ - // Load after a long delay - let image_source = LoadImageParameters { - timeout: Duration::from_secs(5), - fail: false, - }; - - img(move |window: &mut Window, cx: &mut App| { - window.use_asset::(&image_source, cx) - }) - .id("image-2") - .with_fallback(|| Self::fallback_element().into_any_element()) - .with_loading(|| Self::loading_element().into_any_element()) - .size_12() - .border_1() - .border_color(red()) - .on_click(move |_, _, cx| { - cx.remove_asset::(&image_source); - }) - }) - .child({ - // Fail to load image after a long delay - let image_source = LoadImageParameters { - timeout: Duration::from_secs(5), - fail: true, - }; - - // Fail to load after a long delay - img(move |window: &mut Window, cx: &mut App| { - window.use_asset::(&image_source, cx) - }) - .id("image-3") - .with_fallback(|| Self::fallback_element().into_any_element()) - .with_loading(|| Self::loading_element().into_any_element()) - .size_12() - .border_1() - .border_color(red()) - .on_click(move |_, _, cx| { - cx.remove_asset::(&image_source); - }) - }) - .child({ - // Ensure that the normal image loader doesn't spam logs - let image_source = Path::new( - "this/file/really/shouldn't/exist/or/won't/be/an/image/I/hope", - ) - .to_path_buf(); - img(image_source.clone()) - .id("image-4") - .border_1() - .size_12() - .with_fallback(|| Self::fallback_element().into_any_element()) - .border_color(red()) - .with_loading(|| Self::loading_element().into_any_element()) - .on_click(move |_, _, cx| { - cx.remove_asset::(&image_source.clone().into()); - }) - }), - ), - ) - } -} - -fn run_example() { - application().with_assets(Assets {}).run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let options = WindowOptions { - window_bounds: Some(WindowBounds::Windowed(Bounds::centered( - None, - size(px(300.), px(300.)), - cx, - ))), - ..Default::default() - }; - cx.open_window(options, |_, cx| { - cx.activate(false); - cx.new(|_| ImageLoadingExample {}) - }) - .unwrap(); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - env_logger::init(); - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/input.rs b/crates/gpui_pre/examples/input.rs deleted file mode 100644 index eadc2d2f..00000000 --- a/crates/gpui_pre/examples/input.rs +++ /dev/null @@ -1,784 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use std::ops::Range; - -use gpui::{ - App, Bounds, ClipboardItem, Context, CursorStyle, ElementId, ElementInputHandler, Entity, - EntityInputHandler, FocusHandle, Focusable, GlobalElementId, KeyBinding, Keystroke, LayoutId, - MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point, - ShapedLine, SharedString, Style, TextRun, UTF16Selection, UnderlineStyle, Window, WindowBounds, - WindowOptions, actions, black, div, fill, hsla, opaque_grey, point, prelude::*, px, relative, - rgb, rgba, size, white, yellow, -}; -use gpui_platform::application; -use unicode_segmentation::*; - -actions!( - text_input, - [ - Backspace, - Delete, - Left, - Right, - SelectLeft, - SelectRight, - SelectAll, - Home, - End, - ShowCharacterPalette, - Paste, - Cut, - Copy, - Quit, - ] -); - -struct TextInput { - focus_handle: FocusHandle, - content: SharedString, - placeholder: SharedString, - selected_range: Range, - selection_reversed: bool, - marked_range: Option>, - last_layout: Option, - last_bounds: Option>, - is_selecting: bool, -} - -impl TextInput { - fn left(&mut self, _: &Left, _: &mut Window, cx: &mut Context) { - if self.selected_range.is_empty() { - self.move_to(self.previous_boundary(self.cursor_offset()), cx); - } else { - self.move_to(self.selected_range.start, cx) - } - } - - fn right(&mut self, _: &Right, _: &mut Window, cx: &mut Context) { - if self.selected_range.is_empty() { - self.move_to(self.next_boundary(self.selected_range.end), cx); - } else { - self.move_to(self.selected_range.end, cx) - } - } - - fn select_left(&mut self, _: &SelectLeft, _: &mut Window, cx: &mut Context) { - self.select_to(self.previous_boundary(self.cursor_offset()), cx); - } - - fn select_right(&mut self, _: &SelectRight, _: &mut Window, cx: &mut Context) { - self.select_to(self.next_boundary(self.cursor_offset()), cx); - } - - fn select_all(&mut self, _: &SelectAll, _: &mut Window, cx: &mut Context) { - self.move_to(0, cx); - self.select_to(self.content.len(), cx) - } - - fn home(&mut self, _: &Home, _: &mut Window, cx: &mut Context) { - self.move_to(0, cx); - } - - fn end(&mut self, _: &End, _: &mut Window, cx: &mut Context) { - self.move_to(self.content.len(), cx); - } - - fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context) { - if self.selected_range.is_empty() { - let prev = self.previous_boundary(self.cursor_offset()); - if self.cursor_offset() == prev { - window.play_system_bell(); - return; - } - self.select_to(prev, cx) - } - self.replace_text_in_range(None, "", window, cx) - } - - fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context) { - if self.selected_range.is_empty() { - let next = self.next_boundary(self.cursor_offset()); - if self.cursor_offset() == next { - window.play_system_bell(); - return; - } - self.select_to(next, cx) - } - self.replace_text_in_range(None, "", window, cx) - } - - fn on_mouse_down( - &mut self, - event: &MouseDownEvent, - _window: &mut Window, - cx: &mut Context, - ) { - self.is_selecting = true; - - if event.modifiers.shift { - self.select_to(self.index_for_mouse_position(event.position), cx); - } else { - self.move_to(self.index_for_mouse_position(event.position), cx) - } - } - - fn on_mouse_up(&mut self, _: &MouseUpEvent, _window: &mut Window, _: &mut Context) { - self.is_selecting = false; - } - - fn on_mouse_move(&mut self, event: &MouseMoveEvent, _: &mut Window, cx: &mut Context) { - if self.is_selecting { - self.select_to(self.index_for_mouse_position(event.position), cx); - } - } - - fn show_character_palette( - &mut self, - _: &ShowCharacterPalette, - window: &mut Window, - _: &mut Context, - ) { - window.show_character_palette(); - } - - fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context) { - if let Some(text) = cx.read_from_clipboard().and_then(|item| item.text()) { - self.replace_text_in_range(None, &text.replace("\n", " "), window, cx); - } - } - - fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context) { - if !self.selected_range.is_empty() { - cx.write_to_clipboard(ClipboardItem::new_string( - self.content[self.selected_range.clone()].to_string(), - )); - } - } - fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context) { - if !self.selected_range.is_empty() { - cx.write_to_clipboard(ClipboardItem::new_string( - self.content[self.selected_range.clone()].to_string(), - )); - self.replace_text_in_range(None, "", window, cx) - } - } - - fn move_to(&mut self, offset: usize, cx: &mut Context) { - self.selected_range = offset..offset; - cx.notify() - } - - fn cursor_offset(&self) -> usize { - if self.selection_reversed { - self.selected_range.start - } else { - self.selected_range.end - } - } - - fn index_for_mouse_position(&self, position: Point) -> usize { - if self.content.is_empty() { - return 0; - } - - let (Some(bounds), Some(line)) = (self.last_bounds.as_ref(), self.last_layout.as_ref()) - else { - return 0; - }; - if position.y < bounds.top() { - return 0; - } - if position.y > bounds.bottom() { - return self.content.len(); - } - line.closest_index_for_x(position.x - bounds.left()) - } - - fn select_to(&mut self, offset: usize, cx: &mut Context) { - if self.selection_reversed { - self.selected_range.start = offset - } else { - self.selected_range.end = offset - }; - if self.selected_range.end < self.selected_range.start { - self.selection_reversed = !self.selection_reversed; - self.selected_range = self.selected_range.end..self.selected_range.start; - } - cx.notify() - } - - fn offset_from_utf16(&self, offset: usize) -> usize { - let mut utf8_offset = 0; - let mut utf16_count = 0; - - for ch in self.content.chars() { - if utf16_count >= offset { - break; - } - utf16_count += ch.len_utf16(); - utf8_offset += ch.len_utf8(); - } - - utf8_offset - } - - fn offset_to_utf16(&self, offset: usize) -> usize { - let mut utf16_offset = 0; - let mut utf8_count = 0; - - for ch in self.content.chars() { - if utf8_count >= offset { - break; - } - utf8_count += ch.len_utf8(); - utf16_offset += ch.len_utf16(); - } - - utf16_offset - } - - fn range_to_utf16(&self, range: &Range) -> Range { - self.offset_to_utf16(range.start)..self.offset_to_utf16(range.end) - } - - fn range_from_utf16(&self, range_utf16: &Range) -> Range { - self.offset_from_utf16(range_utf16.start)..self.offset_from_utf16(range_utf16.end) - } - - fn previous_boundary(&self, offset: usize) -> usize { - self.content - .grapheme_indices(true) - .rev() - .find_map(|(idx, _)| (idx < offset).then_some(idx)) - .unwrap_or(0) - } - - fn next_boundary(&self, offset: usize) -> usize { - self.content - .grapheme_indices(true) - .find_map(|(idx, _)| (idx > offset).then_some(idx)) - .unwrap_or(self.content.len()) - } - - fn reset(&mut self) { - self.content = "".into(); - self.selected_range = 0..0; - self.selection_reversed = false; - self.marked_range = None; - self.last_layout = None; - self.last_bounds = None; - self.is_selecting = false; - } -} - -impl EntityInputHandler for TextInput { - fn text_for_range( - &mut self, - range_utf16: Range, - actual_range: &mut Option>, - _window: &mut Window, - _cx: &mut Context, - ) -> Option { - let range = self.range_from_utf16(&range_utf16); - actual_range.replace(self.range_to_utf16(&range)); - Some(self.content[range].to_string()) - } - - fn selected_text_range( - &mut self, - _ignore_disabled_input: bool, - _window: &mut Window, - _cx: &mut Context, - ) -> Option { - Some(UTF16Selection { - range: self.range_to_utf16(&self.selected_range), - reversed: self.selection_reversed, - }) - } - - fn marked_text_range( - &self, - _window: &mut Window, - _cx: &mut Context, - ) -> Option> { - self.marked_range - .as_ref() - .map(|range| self.range_to_utf16(range)) - } - - fn unmark_text(&mut self, _window: &mut Window, _cx: &mut Context) { - self.marked_range = None; - } - - fn replace_text_in_range( - &mut self, - range_utf16: Option>, - new_text: &str, - _: &mut Window, - cx: &mut Context, - ) { - let range = range_utf16 - .as_ref() - .map(|range_utf16| self.range_from_utf16(range_utf16)) - .or(self.marked_range.clone()) - .unwrap_or(self.selected_range.clone()); - - self.content = - (self.content[0..range.start].to_owned() + new_text + &self.content[range.end..]) - .into(); - self.selected_range = range.start + new_text.len()..range.start + new_text.len(); - self.marked_range.take(); - cx.notify(); - } - - fn replace_and_mark_text_in_range( - &mut self, - range_utf16: Option>, - new_text: &str, - new_selected_range_utf16: Option>, - _window: &mut Window, - cx: &mut Context, - ) { - let range = range_utf16 - .as_ref() - .map(|range_utf16| self.range_from_utf16(range_utf16)) - .or(self.marked_range.clone()) - .unwrap_or(self.selected_range.clone()); - - self.content = - (self.content[0..range.start].to_owned() + new_text + &self.content[range.end..]) - .into(); - if !new_text.is_empty() { - self.marked_range = Some(range.start..range.start + new_text.len()); - } else { - self.marked_range = None; - } - self.selected_range = new_selected_range_utf16 - .as_ref() - .map(|range_utf16| self.range_from_utf16(range_utf16)) - .map(|new_range| new_range.start + range.start..new_range.end + range.end) - .unwrap_or_else(|| range.start + new_text.len()..range.start + new_text.len()); - - cx.notify(); - } - - fn bounds_for_range( - &mut self, - range_utf16: Range, - bounds: Bounds, - _window: &mut Window, - _cx: &mut Context, - ) -> Option> { - let last_layout = self.last_layout.as_ref()?; - let range = self.range_from_utf16(&range_utf16); - Some(Bounds::from_corners( - point( - bounds.left() + last_layout.x_for_index(range.start), - bounds.top(), - ), - point( - bounds.left() + last_layout.x_for_index(range.end), - bounds.bottom(), - ), - )) - } - - fn character_index_for_point( - &mut self, - point: gpui::Point, - _window: &mut Window, - _cx: &mut Context, - ) -> Option { - let line_point = self.last_bounds?.localize(&point)?; - let last_layout = self.last_layout.as_ref()?; - - assert_eq!(last_layout.text, self.content); - let utf8_index = last_layout.index_for_x(point.x - line_point.x)?; - Some(self.offset_to_utf16(utf8_index)) - } -} - -struct TextElement { - input: Entity, -} - -struct PrepaintState { - line: Option, - cursor: Option, - selection: Option, -} - -impl IntoElement for TextElement { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -impl Element for TextElement { - type RequestLayoutState = (); - type PrepaintState = PrepaintState; - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&gpui::InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - let mut style = Style::default(); - style.size.width = relative(1.).into(); - style.size.height = window.line_height().into(); - (window.request_layout(style, [], cx), ()) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&gpui::InspectorElementId>, - bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Self::PrepaintState { - let input = self.input.read(cx); - let content = input.content.clone(); - let selected_range = input.selected_range.clone(); - let cursor = input.cursor_offset(); - let style = window.text_style(); - - let (display_text, text_color) = if content.is_empty() { - (input.placeholder.clone(), hsla(0., 0., 0., 0.2)) - } else { - (content, style.color) - }; - - let run = TextRun { - len: display_text.len(), - font: style.font(), - color: text_color, - background_color: None, - underline: None, - strikethrough: None, - }; - let runs = if let Some(marked_range) = input.marked_range.as_ref() { - vec![ - TextRun { - len: marked_range.start, - ..run.clone() - }, - TextRun { - len: marked_range.end - marked_range.start, - underline: Some(UnderlineStyle { - color: Some(run.color), - thickness: px(1.0), - wavy: false, - }), - ..run.clone() - }, - TextRun { - len: display_text.len() - marked_range.end, - ..run - }, - ] - .into_iter() - .filter(|run| run.len > 0) - .collect() - } else { - vec![run] - }; - - let font_size = style.font_size.to_pixels(window.rem_size()); - let line = window - .text_system() - .shape_line(display_text, font_size, &runs, None); - - let cursor_pos = line.x_for_index(cursor); - let (selection, cursor) = if selected_range.is_empty() { - ( - None, - Some(fill( - Bounds::new( - point(bounds.left() + cursor_pos, bounds.top()), - size(px(2.), bounds.bottom() - bounds.top()), - ), - gpui::blue(), - )), - ) - } else { - ( - Some(fill( - Bounds::from_corners( - point( - bounds.left() + line.x_for_index(selected_range.start), - bounds.top(), - ), - point( - bounds.left() + line.x_for_index(selected_range.end), - bounds.bottom(), - ), - ), - rgba(0x3311ff30), - )), - None, - ) - }; - PrepaintState { - line: Some(line), - cursor, - selection, - } - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&gpui::InspectorElementId>, - bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - prepaint: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - let focus_handle = self.input.read(cx).focus_handle.clone(); - window.handle_input( - &focus_handle, - ElementInputHandler::new(bounds, self.input.clone()), - cx, - ); - if let Some(selection) = prepaint.selection.take() { - window.paint_quad(selection) - } - let line = prepaint.line.take().unwrap(); - line.paint( - bounds.origin, - window.line_height(), - gpui::TextAlign::Left, - None, - window, - cx, - ) - .unwrap(); - - if focus_handle.is_focused(window) - && let Some(cursor) = prepaint.cursor.take() - { - window.paint_quad(cursor); - } - - self.input.update(cx, |input, _cx| { - input.last_layout = Some(line); - input.last_bounds = Some(bounds); - }); - } -} - -impl Render for TextInput { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .flex() - .key_context("TextInput") - .track_focus(&self.focus_handle(cx)) - .cursor(CursorStyle::IBeam) - .on_action(cx.listener(Self::backspace)) - .on_action(cx.listener(Self::delete)) - .on_action(cx.listener(Self::left)) - .on_action(cx.listener(Self::right)) - .on_action(cx.listener(Self::select_left)) - .on_action(cx.listener(Self::select_right)) - .on_action(cx.listener(Self::select_all)) - .on_action(cx.listener(Self::home)) - .on_action(cx.listener(Self::end)) - .on_action(cx.listener(Self::show_character_palette)) - .on_action(cx.listener(Self::paste)) - .on_action(cx.listener(Self::cut)) - .on_action(cx.listener(Self::copy)) - .on_mouse_down(MouseButton::Left, cx.listener(Self::on_mouse_down)) - .on_mouse_up(MouseButton::Left, cx.listener(Self::on_mouse_up)) - .on_mouse_up_out(MouseButton::Left, cx.listener(Self::on_mouse_up)) - .on_mouse_move(cx.listener(Self::on_mouse_move)) - .bg(rgb(0xeeeeee)) - .line_height(px(30.)) - .text_size(px(24.)) - .child( - div() - .h(px(30. + 4. * 2.)) - .w_full() - .p(px(4.)) - .bg(white()) - .child(TextElement { input: cx.entity() }), - ) - } -} - -impl Focusable for TextInput { - fn focus_handle(&self, _: &App) -> FocusHandle { - self.focus_handle.clone() - } -} - -struct InputExample { - text_input: Entity, - recent_keystrokes: Vec, - focus_handle: FocusHandle, -} - -impl Focusable for InputExample { - fn focus_handle(&self, _: &App) -> FocusHandle { - self.focus_handle.clone() - } -} - -impl InputExample { - fn on_reset_click(&mut self, _: &MouseUpEvent, _window: &mut Window, cx: &mut Context) { - self.recent_keystrokes.clear(); - self.text_input - .update(cx, |text_input, _cx| text_input.reset()); - cx.notify(); - } -} - -impl Render for InputExample { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .bg(rgb(0xaaaaaa)) - .track_focus(&self.focus_handle(cx)) - .flex() - .flex_col() - .size_full() - .child( - div() - .bg(white()) - .border_b_1() - .border_color(black()) - .flex() - .flex_row() - .justify_between() - .child(format!("Keyboard {}", cx.keyboard_layout().name())) - .child( - div() - .border_1() - .border_color(black()) - .px_2() - .bg(yellow()) - .child("Reset") - .hover(|style| { - style - .bg(yellow().blend(opaque_grey(0.5, 0.5))) - .cursor_pointer() - }) - .on_mouse_up(MouseButton::Left, cx.listener(Self::on_reset_click)), - ), - ) - .child(self.text_input.clone()) - .children(self.recent_keystrokes.iter().rev().map(|ks| { - format!( - "{:} {}", - ks.unparse(), - if let Some(key_char) = ks.key_char.as_ref() { - format!("-> {:?}", key_char) - } else { - "".to_owned() - } - ) - })) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(300.0), px(300.0)), cx); - cx.bind_keys([ - KeyBinding::new("backspace", Backspace, None), - KeyBinding::new("delete", Delete, None), - KeyBinding::new("left", Left, None), - KeyBinding::new("right", Right, None), - KeyBinding::new("shift-left", SelectLeft, None), - KeyBinding::new("shift-right", SelectRight, None), - KeyBinding::new("cmd-a", SelectAll, None), - KeyBinding::new("cmd-v", Paste, None), - KeyBinding::new("cmd-c", Copy, None), - KeyBinding::new("cmd-x", Cut, None), - KeyBinding::new("home", Home, None), - KeyBinding::new("end", End, None), - KeyBinding::new("ctrl-cmd-space", ShowCharacterPalette, None), - ]); - - let window = cx - .open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| { - let text_input = cx.new(|cx| TextInput { - focus_handle: cx.focus_handle(), - content: "".into(), - placeholder: "Type here...".into(), - selected_range: 0..0, - selection_reversed: false, - marked_range: None, - last_layout: None, - last_bounds: None, - is_selecting: false, - }); - cx.new(|cx| InputExample { - text_input, - recent_keystrokes: vec![], - focus_handle: cx.focus_handle(), - }) - }, - ) - .unwrap(); - let view = window.update(cx, |_, _, cx| cx.entity()).unwrap(); - cx.observe_keystrokes(move |ev, _, cx| { - view.update(cx, |view, cx| { - view.recent_keystrokes.push(ev.keystroke.clone()); - cx.notify(); - }) - }) - .detach(); - cx.on_keyboard_layout_change({ - move |cx| { - window.update(cx, |_, _, cx| cx.notify()).ok(); - } - }) - .detach(); - - window - .update(cx, |view, window, cx| { - window.focus(&view.text_input.focus_handle(cx), cx); - cx.activate(true); - }) - .unwrap(); - cx.on_action(|_: &Quit, cx| cx.quit()); - cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/layer_shell.rs b/crates/gpui_pre/examples/layer_shell.rs deleted file mode 100644 index 1437b05b..00000000 --- a/crates/gpui_pre/examples/layer_shell.rs +++ /dev/null @@ -1,101 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -fn run_example() { - #[cfg(all(target_os = "linux", feature = "wayland"))] - example::main(); - - #[cfg(not(all(target_os = "linux", feature = "wayland")))] - panic!("This example requires the `wayland` feature and a linux system."); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} - -#[cfg(all(target_os = "linux", feature = "wayland"))] -mod example { - use std::time::{Duration, SystemTime, UNIX_EPOCH}; - - use gpui::{ - App, Bounds, Context, FontWeight, Size, Window, WindowBackgroundAppearance, WindowBounds, - WindowKind, WindowOptions, div, layer_shell::*, point, prelude::*, px, rems, rgba, white, - }; - use gpui_platform::application; - - struct LayerShellExample; - - impl LayerShellExample { - fn new(cx: &mut Context) -> Self { - cx.spawn(async move |this, cx| { - loop { - let _ = this.update(cx, |_, cx| cx.notify()); - cx.background_executor() - .timer(Duration::from_millis(500)) - .await; - } - }) - .detach(); - - LayerShellExample - } - } - - impl Render for LayerShellExample { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(); - - let hours = (now / 3600) % 24; - let minutes = (now / 60) % 60; - let seconds = now % 60; - - div() - .size_full() - .flex() - .items_center() - .justify_center() - .text_size(rems(4.5)) - .font_weight(FontWeight::EXTRA_BOLD) - .text_color(white()) - .bg(rgba(0x0000044)) - .rounded_xl() - .child(format!("{:02}:{:02}:{:02}", hours, minutes, seconds)) - } - } - - pub fn main() { - application().run(|cx: &mut App| { - cx.open_window( - WindowOptions { - titlebar: None, - window_bounds: Some(WindowBounds::Windowed(Bounds { - origin: point(px(0.), px(0.)), - size: Size::new(px(500.), px(200.)), - })), - app_id: Some("gpui-layer-shell-example".to_string()), - window_background: WindowBackgroundAppearance::Transparent, - kind: WindowKind::LayerShell(LayerShellOptions { - namespace: "gpui".to_string(), - anchor: Anchor::LEFT | Anchor::RIGHT | Anchor::BOTTOM, - margin: Some((px(0.), px(0.), px(40.), px(0.))), - keyboard_interactivity: KeyboardInteractivity::None, - ..Default::default() - }), - ..Default::default() - }, - |_, cx| cx.new(LayerShellExample::new), - ) - .unwrap(); - }); - } -} diff --git a/crates/gpui_pre/examples/list_example.rs b/crates/gpui_pre/examples/list_example.rs deleted file mode 100644 index 542b7e32..00000000 --- a/crates/gpui_pre/examples/list_example.rs +++ /dev/null @@ -1,176 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, ListAlignment, ListState, Render, Window, WindowBounds, WindowOptions, - div, list, prelude::*, px, rgb, size, -}; -use gpui_platform::application; - -const ITEM_COUNT: usize = 40; -const SCROLLBAR_WIDTH: f32 = 12.; - -struct BottomListDemo { - list_state: ListState, -} - -impl BottomListDemo { - fn new() -> Self { - Self { - list_state: ListState::new(ITEM_COUNT, ListAlignment::Bottom, px(500.)).measure_all(), - } - } -} - -impl Render for BottomListDemo { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let max_offset = self.list_state.max_offset_for_scrollbar().y; - let current_offset = -self.list_state.scroll_px_offset_for_scrollbar().y; - - let viewport_height = self.list_state.viewport_bounds().size.height; - - let raw_fraction = if max_offset > px(0.) { - current_offset / max_offset - } else { - 0. - }; - - let total_height = viewport_height + max_offset; - let thumb_height = if total_height > px(0.) { - px(viewport_height.as_f32() * viewport_height.as_f32() / total_height.as_f32()) - .max(px(30.)) - } else { - px(30.) - }; - - let track_space = viewport_height - thumb_height; - let thumb_top = track_space * raw_fraction; - - let bug_detected = raw_fraction > 1.0; - - div() - .size_full() - .bg(rgb(0xFFFFFF)) - .flex() - .flex_col() - .p_4() - .gap_2() - .child( - div() - .text_sm() - .flex() - .flex_col() - .gap_1() - .child(format!( - "offset: {:.0} / max: {:.0} | fraction: {:.3}", - current_offset.as_f32(), - max_offset.as_f32(), - raw_fraction, - )) - .child( - div() - .text_color(if bug_detected { - rgb(0xCC0000) - } else { - rgb(0x008800) - }) - .child(if bug_detected { - format!( - "BUG: fraction is {:.3} (> 1.0) — thumb is off-track!", - raw_fraction - ) - } else { - "OK: fraction <= 1.0 — thumb is within track.".to_string() - }), - ), - ) - .child( - div() - .flex_1() - .flex() - .flex_row() - .overflow_hidden() - .border_1() - .border_color(rgb(0xCCCCCC)) - .rounded_sm() - .child( - list(self.list_state.clone(), |index, _window, _cx| { - let height = px(30. + (index % 5) as f32 * 10.); - div() - .h(height) - .w_full() - .flex() - .items_center() - .px_3() - .border_b_1() - .border_color(rgb(0xEEEEEE)) - .bg(if index % 2 == 0 { - rgb(0xFAFAFA) - } else { - rgb(0xFFFFFF) - }) - .text_sm() - .child(format!("Item {index}")) - .into_any() - }) - .flex_1(), - ) - // Scrollbar track - .child( - div() - .w(px(SCROLLBAR_WIDTH)) - .h_full() - .flex_shrink_0() - .bg(rgb(0xE0E0E0)) - .relative() - .child( - // Thumb — position is unclamped to expose the bug - div() - .absolute() - .top(thumb_top) - .w_full() - .h(thumb_height) - .bg(if bug_detected { - rgb(0xCC0000) - } else { - rgb(0x888888) - }) - .rounded_sm(), - ), - ), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(400.), px(500.)), cx); - cx.open_window( - WindowOptions { - focus: true, - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| cx.new(|_| BottomListDemo::new()), - ) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/mouse_pressure.rs b/crates/gpui_pre/examples/mouse_pressure.rs deleted file mode 100644 index 3470bf18..00000000 --- a/crates/gpui_pre/examples/mouse_pressure.rs +++ /dev/null @@ -1,87 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, MousePressureEvent, PressureStage, Window, WindowBounds, WindowOptions, - div, prelude::*, px, rgb, size, -}; -use gpui_platform::application; - -struct MousePressureExample { - pressure_stage: PressureStage, - pressure_amount: f32, -} - -impl Render for MousePressureExample { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .flex() - .flex_col() - .gap_3() - .bg(rgb(0x505050)) - .size(px(500.0)) - .justify_center() - .items_center() - .shadow_lg() - .border_1() - .border_color(rgb(0x0000ff)) - .text_xl() - .text_color(rgb(0xffffff)) - .child(format!("Pressure stage: {:?}", self.pressure_stage)) - .child(format!("Pressure amount: {:.2}", self.pressure_amount)) - .on_mouse_pressure(cx.listener(Self::on_mouse_pressure)) - } -} - -impl MousePressureExample { - fn on_mouse_pressure( - &mut self, - pressure_event: &MousePressureEvent, - _window: &mut Window, - cx: &mut Context, - ) { - self.pressure_amount = pressure_event.pressure; - self.pressure_stage = pressure_event.stage; - - cx.notify(); - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx); - - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| { - cx.new(|_| MousePressureExample { - pressure_stage: PressureStage::Zero, - pressure_amount: 0.0, - }) - }, - ) - .unwrap(); - - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/move_entity_between_windows.rs b/crates/gpui_pre/examples/move_entity_between_windows.rs deleted file mode 100644 index e0f16467..00000000 --- a/crates/gpui_pre/examples/move_entity_between_windows.rs +++ /dev/null @@ -1,160 +0,0 @@ -//! An entity registers callbacks via the `_in` API family and then gets -//! re-hosted in a new window via a click. The point of the example is to -//! demonstrate that callbacks dispatched after the move correctly target the -//! entity's *current* window rather than the window it was in at -//! registration time. -//! -//! To run: cargo run -p gpui --example move_entity_between_windows - -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use std::time::Duration; - -use gpui::{ - App, AppContext as _, Bounds, Context, EventEmitter, MouseButton, Render, SharedString, - Subscription, Task, Window, WindowBounds, WindowOptions, div, prelude::*, px, rgb, size, -}; -use gpui_platform::application; - -struct MoveToNewWindow; - -struct HelloWorld { - text: SharedString, - tick_count: u32, - move_count: u32, - _tasks: Vec>, - _subscriptions: Vec, -} - -impl EventEmitter for HelloWorld {} - -impl HelloWorld { - fn new(window: &mut Window, cx: &mut Context) -> Self { - let self_entity = cx.entity(); - - let task = cx.spawn_in(window, async move |this, cx| { - loop { - cx.background_executor().timer(Duration::from_secs(1)).await; - let result = this.update_in(cx, |this, window, _cx| { - this.tick_count += 1; - println!( - "tick #{} fired in entity's current window {}", - this.tick_count, - window.window_handle().window_id().as_u64(), - ); - }); - if let Err(err) = result { - println!("tick task giving up: {err}"); - return; - } - } - }); - - let subscription = cx.subscribe_in::<_, MoveToNewWindow>( - &self_entity, - window, - move |this, _emitter, _event, window, cx| { - let entered_window_id = window.window_handle().window_id().as_u64(); - println!( - "MoveToNewWindow handler fired in entity's current window {entered_window_id}", - ); - - this.move_count += 1; - cx.notify(); - - let entity = cx.entity(); - let old_window = window.window_handle(); - cx.defer(move |cx| { - let bounds = Bounds::centered(None, size(px(500.0), px(500.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - move |_, _| entity, - ) - .expect("failed to open new window"); - old_window - .update(cx, |_, window, _| window.remove_window()) - .ok(); - }); - }, - ); - - Self { - text: "World".into(), - tick_count: 0, - move_count: 0, - _tasks: vec![task], - _subscriptions: vec![subscription], - } - } -} - -impl Render for HelloWorld { - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - let window_id = window.window_handle().window_id().as_u64(); - - div() - .flex() - .flex_col() - .gap_3() - .bg(rgb(0x505050)) - .size(px(500.0)) - .justify_center() - .items_center() - .text_xl() - .text_color(rgb(0xffffff)) - .child(format!("Hello, {}!", self.text)) - .child(format!("Rendering in window: {window_id}")) - .child(format!("Ticks observed by entity: {}", self.tick_count)) - .child(format!("Moves observed by entity: {}", self.move_count)) - .child( - div() - .px_4() - .py_2() - .bg(rgb(0x4040ff)) - .rounded_md() - .child("Move me to a new window") - .on_mouse_down( - MouseButton::Left, - cx.listener(|_this, _, _window, cx| { - cx.emit(MoveToNewWindow); - }), - ), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(500.0), px(500.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |window, cx| cx.new(|cx| HelloWorld::new(window, cx)), - ) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/on_window_close_quit.rs b/crates/gpui_pre/examples/on_window_close_quit.rs deleted file mode 100644 index d61e23e4..00000000 --- a/crates/gpui_pre/examples/on_window_close_quit.rs +++ /dev/null @@ -1,103 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, FocusHandle, KeyBinding, Window, WindowBounds, WindowOptions, actions, - div, prelude::*, px, rgb, size, -}; -use gpui_platform::application; - -actions!(example, [CloseWindow]); - -struct ExampleWindow { - focus_handle: FocusHandle, -} - -impl Render for ExampleWindow { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .on_action(|_: &CloseWindow, window, _| { - window.remove_window(); - }) - .track_focus(&self.focus_handle) - .flex() - .flex_col() - .gap_3() - .bg(rgb(0x505050)) - .size(px(500.0)) - .justify_center() - .items_center() - .shadow_lg() - .border_1() - .border_color(rgb(0x0000ff)) - .text_xl() - .text_color(rgb(0xffffff)) - .child( - "Closing this window with cmd-w or the traffic lights should quit the application!", - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let mut bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx); - - cx.bind_keys([KeyBinding::new("cmd-w", CloseWindow, None)]); - cx.on_window_closed(|cx, _window_id| { - if cx.windows().is_empty() { - cx.quit(); - } - }) - .detach(); - - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |window, cx| { - cx.activate(false); - cx.new(|cx| { - let focus_handle = cx.focus_handle(); - focus_handle.focus(window, cx); - ExampleWindow { focus_handle } - }) - }, - ) - .unwrap(); - - bounds.origin.x += bounds.size.width; - - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |window, cx| { - cx.new(|cx| { - let focus_handle = cx.focus_handle(); - focus_handle.focus(window, cx); - ExampleWindow { focus_handle } - }) - }, - ) - .unwrap(); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/opacity.rs b/crates/gpui_pre/examples/opacity.rs deleted file mode 100644 index c2af3424..00000000 --- a/crates/gpui_pre/examples/opacity.rs +++ /dev/null @@ -1,195 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use std::{fs, path::PathBuf}; - -use anyhow::Result; -use gpui::{ - App, AssetSource, Bounds, BoxShadow, ClickEvent, Context, SharedString, Task, Window, - WindowBounds, WindowOptions, div, hsla, img, prelude::*, px, rgb, size, svg, -}; -use gpui_platform::application; - -struct Assets { - base: PathBuf, -} - -impl AssetSource for Assets { - fn load(&self, path: &str) -> Result>> { - fs::read(self.base.join(path)) - .map(|data| Some(std::borrow::Cow::Owned(data))) - .map_err(|e| e.into()) - } - - fn list(&self, path: &str) -> Result> { - fs::read_dir(self.base.join(path)) - .map(|entries| { - entries - .filter_map(|entry| { - entry - .ok() - .and_then(|entry| entry.file_name().into_string().ok()) - .map(SharedString::from) - }) - .collect() - }) - .map_err(|e| e.into()) - } -} - -struct HelloWorld { - _task: Option>, - opacity: f32, - animating: bool, -} - -impl HelloWorld { - fn new(_window: &mut Window, _: &mut Context) -> Self { - Self { - _task: None, - opacity: 0.5, - animating: false, - } - } - - fn start_animation(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context) { - self.opacity = 0.0; - self.animating = true; - cx.notify(); - } -} - -impl Render for HelloWorld { - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - if self.animating { - self.opacity += 0.005; - if self.opacity >= 1.0 { - self.animating = false; - self.opacity = 1.0; - } else { - window.request_animation_frame(); - } - } - - div() - .flex() - .flex_row() - .size_full() - .bg(rgb(0xe0e0e0)) - .text_xl() - .child( - div() - .flex() - .size_full() - .justify_center() - .items_center() - .border_1() - .text_color(gpui::blue()) - .child(div().child("This is background text.")), - ) - .child( - div() - .id("panel") - .on_click(cx.listener(Self::start_animation)) - .absolute() - .top_8() - .left_8() - .right_8() - .bottom_8() - .opacity(self.opacity) - .flex() - .justify_center() - .items_center() - .bg(gpui::white()) - .border_3() - .border_color(gpui::red()) - .text_color(gpui::yellow()) - .child( - div() - .flex() - .flex_col() - .gap_2() - .justify_center() - .items_center() - .size(px(300.)) - .bg(gpui::blue()) - .border_3() - .border_color(gpui::black()) - .shadow(vec![ - BoxShadow::new(px(10.0), px(10.0), hsla(0.0, 0.0, 0.0, 0.5)) - .blur_radius(px(1.0)) - .spread_radius(px(5.0)), - ]) - .child(img("image/app-icon.png").size_8()) - .child("Opacity Panel (Click to test)") - .child( - div() - .id("deep-level-text") - .flex() - .justify_center() - .items_center() - .p_4() - .bg(gpui::black()) - .text_color(gpui::white()) - .text_decoration_2() - .text_decoration_wavy() - .text_decoration_color(gpui::red()) - .child(format!("opacity: {:.1}", self.opacity)), - ) - .child( - svg() - .path("image/arrow_circle.svg") - .text_color(gpui::black()) - .text_2xl() - .size_8(), - ) - .child( - div() - .flex() - .children(["🎊", "✈️", "🎉", "🎈", "🎁", "🎂"].map(|emoji| { - div() - .child(emoji.to_string()) - .hover(|style| style.opacity(0.5)) - })), - ) - .child(img("image/black-cat-typing.gif").size_12()), - ), - ) - } -} - -fn run_example() { - application() - .with_assets(Assets { - base: PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples"), - }) - .run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(500.0), px(500.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |window, cx| cx.new(|cx| HelloWorld::new(window, cx)), - ) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/ownership_post.rs b/crates/gpui_pre/examples/ownership_post.rs deleted file mode 100644 index 04a0b6fe..00000000 --- a/crates/gpui_pre/examples/ownership_post.rs +++ /dev/null @@ -1,56 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{App, Context, Entity, EventEmitter, prelude::*}; -use gpui_platform::application; - -struct Counter { - count: usize, -} - -struct Change { - increment: usize, -} - -impl EventEmitter for Counter {} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let counter: Entity = cx.new(|_cx| Counter { count: 0 }); - let subscriber = cx.new(|cx: &mut Context| { - cx.subscribe(&counter, |subscriber, _emitter, event, _cx| { - subscriber.count += event.increment * 2; - }) - .detach(); - - Counter { - count: counter.read(cx).count * 2, - } - }); - - counter.update(cx, |counter, cx| { - counter.count += 2; - cx.notify(); - cx.emit(Change { increment: 2 }); - }); - - assert_eq!(subscriber.read(cx).count, 4); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/painting.rs b/crates/gpui_pre/examples/painting.rs deleted file mode 100644 index 3d89b0ff..00000000 --- a/crates/gpui_pre/examples/painting.rs +++ /dev/null @@ -1,478 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - Background, Bounds, ColorSpace, Context, MouseDownEvent, Path, PathBuilder, PathStyle, Pixels, - Point, Render, StrokeOptions, Window, WindowOptions, canvas, div, linear_color_stop, - linear_gradient, point, prelude::*, px, quad, rgb, size, -}; -use gpui_platform::application; - -struct PaintingViewer { - default_lines: Vec<(Path, Background)>, - background_quads: Vec<(Bounds, Background)>, - lines: Vec>>, - start: Point, - dashed: bool, - _painting: bool, -} - -impl PaintingViewer { - fn new(_window: &mut Window, _cx: &mut Context) -> Self { - let mut lines = vec![]; - - // Black squares beneath transparent paths. - let background_quads = vec![ - ( - Bounds { - origin: point(px(70.), px(70.)), - size: size(px(40.), px(40.)), - }, - gpui::black().into(), - ), - ( - Bounds { - origin: point(px(170.), px(70.)), - size: size(px(40.), px(40.)), - }, - gpui::black().into(), - ), - ( - Bounds { - origin: point(px(270.), px(70.)), - size: size(px(40.), px(40.)), - }, - gpui::black().into(), - ), - ( - Bounds { - origin: point(px(370.), px(70.)), - size: size(px(40.), px(40.)), - }, - gpui::black().into(), - ), - ( - Bounds { - origin: point(px(450.), px(50.)), - size: size(px(80.), px(80.)), - }, - gpui::black().into(), - ), - ]; - - // 50% opaque red path that extends across black quad. - let mut builder = PathBuilder::fill(); - builder.move_to(point(px(50.), px(50.))); - builder.line_to(point(px(130.), px(50.))); - builder.line_to(point(px(130.), px(130.))); - builder.line_to(point(px(50.), px(130.))); - builder.close(); - let path = builder.build().unwrap(); - let red = rgb(0xFF0000).alpha(0.5); - lines.push((path, red.into())); - - // 50% opaque blue path that extends across black quad. - let mut builder = PathBuilder::fill(); - builder.move_to(point(px(150.), px(50.))); - builder.line_to(point(px(230.), px(50.))); - builder.line_to(point(px(230.), px(130.))); - builder.line_to(point(px(150.), px(130.))); - builder.close(); - let path = builder.build().unwrap(); - let blue = rgb(0x0000FF).alpha(0.5); - lines.push((path, blue.into())); - - // 50% opaque green path that extends across black quad. - let mut builder = PathBuilder::fill(); - builder.move_to(point(px(250.), px(50.))); - builder.line_to(point(px(330.), px(50.))); - builder.line_to(point(px(330.), px(130.))); - builder.line_to(point(px(250.), px(130.))); - builder.close(); - let path = builder.build().unwrap(); - let green = rgb(0x00FF00).alpha(0.5); - lines.push((path, green.into())); - - // 50% opaque black path that extends across black quad. - let mut builder = PathBuilder::fill(); - builder.move_to(point(px(350.), px(50.))); - builder.line_to(point(px(430.), px(50.))); - builder.line_to(point(px(430.), px(130.))); - builder.line_to(point(px(350.), px(130.))); - builder.close(); - let path = builder.build().unwrap(); - let black = rgb(0x000000).alpha(0.5); - lines.push((path, black.into())); - - // Two 50% opaque red circles overlapping - center should be darker red - let mut builder = PathBuilder::fill(); - let center = point(px(530.), px(85.)); - let radius = px(30.); - builder.move_to(point(center.x + radius, center.y)); - builder.arc_to( - point(radius, radius), - px(0.), - false, - false, - point(center.x - radius, center.y), - ); - builder.arc_to( - point(radius, radius), - px(0.), - false, - false, - point(center.x + radius, center.y), - ); - builder.close(); - let path = builder.build().unwrap(); - let red1 = rgb(0xFF0000).alpha(0.5); - lines.push((path, red1.into())); - - let mut builder = PathBuilder::fill(); - let center = point(px(570.), px(85.)); - let radius = px(30.); - builder.move_to(point(center.x + radius, center.y)); - builder.arc_to( - point(radius, radius), - px(0.), - false, - false, - point(center.x - radius, center.y), - ); - builder.arc_to( - point(radius, radius), - px(0.), - false, - false, - point(center.x + radius, center.y), - ); - builder.close(); - let path = builder.build().unwrap(); - let red2 = rgb(0xFF0000).alpha(0.5); - lines.push((path, red2.into())); - - // draw a Rust logo - let mut builder = lyon::path::Path::svg_builder(); - lyon::extra::rust_logo::build_logo_path(&mut builder); - // move down the Path - let mut builder: PathBuilder = builder.into(); - builder.translate(point(px(10.), px(200.))); - builder.scale(0.9); - let path = builder.build().unwrap(); - lines.push((path, gpui::black().into())); - - // draw a lightening bolt ⚡ - let mut builder = PathBuilder::fill(); - builder.add_polygon( - &[ - point(px(150.), px(300.)), - point(px(200.), px(225.)), - point(px(200.), px(275.)), - point(px(250.), px(200.)), - ], - false, - ); - let path = builder.build().unwrap(); - lines.push((path, rgb(0x1d4ed8).into())); - - // draw a ⭐ - let mut builder = PathBuilder::fill(); - builder.move_to(point(px(350.), px(200.))); - builder.line_to(point(px(370.), px(260.))); - builder.line_to(point(px(430.), px(260.))); - builder.line_to(point(px(380.), px(300.))); - builder.line_to(point(px(400.), px(360.))); - builder.line_to(point(px(350.), px(320.))); - builder.line_to(point(px(300.), px(360.))); - builder.line_to(point(px(320.), px(300.))); - builder.line_to(point(px(270.), px(260.))); - builder.line_to(point(px(330.), px(260.))); - builder.line_to(point(px(350.), px(200.))); - let path = builder.build().unwrap(); - lines.push(( - path, - linear_gradient( - 180., - linear_color_stop(rgb(0xFACC15), 0.7), - linear_color_stop(rgb(0xD56D0C), 1.), - ) - .color_space(ColorSpace::Oklab), - )); - - // draw linear gradient - let square_bounds = Bounds { - origin: point(px(450.), px(200.)), - size: size(px(200.), px(80.)), - }; - let height = square_bounds.size.height; - let horizontal_offset = height; - let vertical_offset = px(30.); - let mut builder = PathBuilder::fill(); - builder.move_to(square_bounds.bottom_left()); - builder.curve_to( - square_bounds.origin + point(horizontal_offset, vertical_offset), - square_bounds.origin + point(px(0.0), vertical_offset), - ); - builder.line_to(square_bounds.top_right() + point(-horizontal_offset, vertical_offset)); - builder.curve_to( - square_bounds.bottom_right(), - square_bounds.top_right() + point(px(0.0), vertical_offset), - ); - builder.line_to(square_bounds.bottom_left()); - let path = builder.build().unwrap(); - lines.push(( - path, - linear_gradient( - 180., - linear_color_stop(gpui::blue(), 0.4), - linear_color_stop(gpui::red(), 1.), - ), - )); - - // draw a pie chart - let center = point(px(96.), px(96.)); - let pie_center = point(px(775.), px(255.)); - let segments = [ - ( - point(px(871.), px(255.)), - point(px(747.), px(163.)), - rgb(0x1374e9), - ), - ( - point(px(747.), px(163.)), - point(px(679.), px(263.)), - rgb(0xe13527), - ), - ( - point(px(679.), px(263.)), - point(px(754.), px(349.)), - rgb(0x0751ce), - ), - ( - point(px(754.), px(349.)), - point(px(854.), px(310.)), - rgb(0x209742), - ), - ( - point(px(854.), px(310.)), - point(px(871.), px(255.)), - rgb(0xfbc10a), - ), - ]; - - for (start, end, color) in segments { - let mut builder = PathBuilder::fill(); - builder.move_to(start); - builder.arc_to(center, px(0.), false, false, end); - builder.line_to(pie_center); - builder.close(); - let path = builder.build().unwrap(); - lines.push((path, color.into())); - } - - // draw a wave - let options = StrokeOptions::default() - .with_line_width(1.) - .with_line_join(lyon::path::LineJoin::Bevel); - let mut builder = PathBuilder::stroke(px(1.)).with_style(PathStyle::Stroke(options)); - builder.move_to(point(px(40.), px(420.))); - for i in 1..50 { - builder.line_to(point( - px(40.0 + i as f32 * 10.0), - px(420.0 + (i as f32 * 10.0).sin() * 40.0), - )); - } - let path = builder.build().unwrap(); - lines.push((path, gpui::green().into())); - - Self { - default_lines: lines.clone(), - background_quads, - lines: vec![], - start: point(px(0.), px(0.)), - dashed: false, - _painting: false, - } - } - - fn clear(&mut self, cx: &mut Context) { - self.lines.clear(); - cx.notify(); - } -} - -fn button( - text: &str, - cx: &mut Context, - on_click: impl Fn(&mut PaintingViewer, &mut Context) + 'static, -) -> impl IntoElement { - div() - .id(text.to_string()) - .child(text.to_string()) - .bg(gpui::black()) - .text_color(gpui::white()) - .active(|this| this.opacity(0.8)) - .flex() - .px_3() - .py_1() - .on_click(cx.listener(move |this, _, _, cx| on_click(this, cx))) -} - -impl Render for PaintingViewer { - fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { - let default_lines = self.default_lines.clone(); - let background_quads = self.background_quads.clone(); - let lines = self.lines.clone(); - let dashed = self.dashed; - - div() - .bg(gpui::white()) - .size_full() - .p_4() - .flex() - .flex_col() - .child( - div() - .flex() - .gap_2() - .justify_between() - .items_center() - .child("Mouse down any point and drag to draw lines (Hold on shift key to draw straight lines)") - .child( - div() - .flex() - .gap_x_2() - .child(button( - if dashed { "Solid" } else { "Dashed" }, - cx, - move |this, _| this.dashed = !dashed, - )) - .child(button("Clear", cx, |this, cx| this.clear(cx))), - ), - ) - .child( - div() - .size_full() - .child( - canvas( - move |_, _, _| {}, - move |_, _, window, _| { - // First draw background quads - for (bounds, color) in background_quads.iter() { - window.paint_quad(quad( - *bounds, - px(0.), - *color, - px(0.), - gpui::transparent_black(), - Default::default(), - )); - } - - // Then draw the default paths on top - for (path, color) in default_lines { - window.paint_path(path, color); - } - - for points in lines { - if points.len() < 2 { - continue; - } - - let mut builder = PathBuilder::stroke(px(1.)); - if dashed { - builder = builder.dash_array(&[px(4.), px(2.)]); - } - for (i, p) in points.into_iter().enumerate() { - if i == 0 { - builder.move_to(p); - } else { - builder.line_to(p); - } - } - - if let Ok(path) = builder.build() { - window.paint_path(path, gpui::black()); - } - } - }, - ) - .size_full(), - ) - .on_mouse_down( - gpui::MouseButton::Left, - cx.listener(|this, ev: &MouseDownEvent, _, _| { - this._painting = true; - this.start = ev.position; - let path = vec![ev.position]; - this.lines.push(path); - }), - ) - .on_mouse_move(cx.listener(|this, ev: &gpui::MouseMoveEvent, _, cx| { - if !this._painting { - return; - } - - let is_shifted = ev.modifiers.shift; - let mut pos = ev.position; - // When holding shift, draw a straight line - if is_shifted { - let dx = pos.x - this.start.x; - let dy = pos.y - this.start.y; - if dx.abs() > dy.abs() { - pos.y = this.start.y; - } else { - pos.x = this.start.x; - } - } - - if let Some(path) = this.lines.last_mut() { - path.push(pos); - } - - cx.notify(); - })) - .on_mouse_up( - gpui::MouseButton::Left, - cx.listener(|this, _, _, _| { - this._painting = false; - }), - ), - ) - } -} - -fn run_example() { - application().run(|cx| { - if !example_support::load_fonts(cx) { - return; - } - cx.open_window( - WindowOptions { - focus: true, - ..Default::default() - }, - |window, cx| cx.new(|cx| PaintingViewer::new(window, cx)), - ) - .unwrap(); - cx.on_window_closed(|cx, _window_id| { - cx.quit(); - }) - .detach(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/paths_bench.rs b/crates/gpui_pre/examples/paths_bench.rs deleted file mode 100644 index 236e70e9..00000000 --- a/crates/gpui_pre/examples/paths_bench.rs +++ /dev/null @@ -1,113 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - Background, Bounds, ColorSpace, Context, Path, PathBuilder, Pixels, Render, TitlebarOptions, - Window, WindowBounds, WindowOptions, canvas, div, linear_color_stop, linear_gradient, point, - prelude::*, px, rgb, size, -}; -use gpui_platform::application; - -const DEFAULT_WINDOW_WIDTH: Pixels = px(1024.0); -const DEFAULT_WINDOW_HEIGHT: Pixels = px(768.0); - -struct PaintingViewer { - default_lines: Vec<(Path, Background)>, - _painting: bool, -} - -impl PaintingViewer { - fn new(_window: &mut Window, _cx: &mut Context) -> Self { - let mut lines = vec![]; - - // draw a lightening bolt ⚡ - for _ in 0..2000 { - // draw a ⭐ - let mut builder = PathBuilder::fill(); - builder.move_to(point(px(350.), px(100.))); - builder.line_to(point(px(370.), px(160.))); - builder.line_to(point(px(430.), px(160.))); - builder.line_to(point(px(380.), px(200.))); - builder.line_to(point(px(400.), px(260.))); - builder.line_to(point(px(350.), px(220.))); - builder.line_to(point(px(300.), px(260.))); - builder.line_to(point(px(320.), px(200.))); - builder.line_to(point(px(270.), px(160.))); - builder.line_to(point(px(330.), px(160.))); - builder.line_to(point(px(350.), px(100.))); - let path = builder.build().unwrap(); - lines.push(( - path, - linear_gradient( - 180., - linear_color_stop(rgb(0xFACC15), 0.7), - linear_color_stop(rgb(0xD56D0C), 1.), - ) - .color_space(ColorSpace::Oklab), - )); - } - - Self { - default_lines: lines, - _painting: false, - } - } -} - -impl Render for PaintingViewer { - fn render(&mut self, window: &mut Window, _: &mut Context) -> impl IntoElement { - window.request_animation_frame(); - let lines = self.default_lines.clone(); - div().size_full().child( - canvas( - move |_, _, _| {}, - move |_, _, window, _| { - for (path, color) in lines { - window.paint_path(path, color); - } - }, - ) - .size_full(), - ) - } -} - -fn run_example() { - application().run(|cx| { - if !example_support::load_fonts(cx) { - return; - } - cx.open_window( - WindowOptions { - titlebar: Some(TitlebarOptions { - title: Some("Vulkan".into()), - ..Default::default() - }), - focus: true, - window_bounds: Some(WindowBounds::Windowed(Bounds::centered( - None, - size(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT), - cx, - ))), - ..Default::default() - }, - |window, cx| cx.new(|cx| PaintingViewer::new(window, cx)), - ) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/pattern.rs b/crates/gpui_pre/examples/pattern.rs deleted file mode 100644 index 7d2e98d1..00000000 --- a/crates/gpui_pre/examples/pattern.rs +++ /dev/null @@ -1,136 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, AppContext, Bounds, Context, Window, WindowBounds, WindowOptions, div, linear_color_stop, - linear_gradient, pattern_slash, prelude::*, px, rgb, size, -}; -use gpui_platform::application; - -struct PatternExample; - -impl Render for PatternExample { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .flex() - .flex_col() - .gap_3() - .bg(rgb(0xffffff)) - .size(px(600.0)) - .justify_center() - .items_center() - .shadow_lg() - .text_xl() - .text_color(rgb(0x000000)) - .child("Pattern Example") - .child( - div() - .flex() - .flex_col() - .border_1() - .border_color(gpui::blue()) - .child(div().w(px(54.0)).h(px(18.0)).bg(pattern_slash( - gpui::red(), - 18.0 / 4.0, - 18.0 / 4.0, - ))) - .child(div().w(px(54.0)).h(px(18.0)).bg(pattern_slash( - gpui::red(), - 18.0 / 4.0, - 18.0 / 4.0, - ))) - .child(div().w(px(54.0)).h(px(18.0)).bg(pattern_slash( - gpui::red(), - 18.0 / 4.0, - 18.0 / 4.0, - ))) - .child(div().w(px(54.0)).h(px(18.0)).bg(pattern_slash( - gpui::red(), - 18.0 / 4.0, - 18.0 / 2.0, - ))), - ) - .child( - div() - .flex() - .flex_col() - .border_1() - .border_color(gpui::blue()) - .bg(gpui::green().opacity(0.16)) - .child("Elements the same height should align") - .child(div().w(px(256.0)).h(px(56.0)).bg(pattern_slash( - gpui::red(), - 56.0 / 6.0, - 56.0 / 6.0, - ))) - .child(div().w(px(256.0)).h(px(56.0)).bg(pattern_slash( - gpui::green(), - 56.0 / 6.0, - 56.0 / 6.0, - ))) - .child(div().w(px(256.0)).h(px(56.0)).bg(pattern_slash( - gpui::blue(), - 56.0 / 6.0, - 56.0 / 6.0, - ))) - .child(div().w(px(256.0)).h(px(26.0)).bg(pattern_slash( - gpui::yellow(), - 56.0 / 6.0, - 56.0 / 6.0, - ))), - ) - .child( - div() - .border_1() - .border_color(gpui::blue()) - .w(px(240.0)) - .h(px(40.0)) - .bg(gpui::red()), - ) - .child( - div() - .border_1() - .border_color(gpui::blue()) - .w(px(240.0)) - .h(px(40.0)) - .bg(linear_gradient( - 45., - linear_color_stop(gpui::red(), 0.), - linear_color_stop(gpui::blue(), 1.), - )), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(600.0), px(600.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_window, cx| cx.new(|_cx| PatternExample), - ) - .unwrap(); - - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/popover.rs b/crates/gpui_pre/examples/popover.rs deleted file mode 100644 index 7678a6a3..00000000 --- a/crates/gpui_pre/examples/popover.rs +++ /dev/null @@ -1,197 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - Anchor, App, Context, Div, Hsla, Stateful, Window, WindowOptions, anchored, deferred, div, - prelude::*, px, -}; -use gpui_platform::application; - -/// An example show use deferred to create a floating layers. -struct HelloWorld { - open: bool, - secondary_open: bool, -} - -fn button(id: &'static str) -> Stateful

{ - div() - .id(id) - .bg(gpui::black()) - .text_color(gpui::white()) - .px_3() - .py_1() -} - -fn popover() -> Div { - div() - .flex() - .flex_col() - .items_center() - .justify_center() - .shadow_lg() - .p_3() - .rounded_md() - .bg(gpui::white()) - .text_color(gpui::black()) - .border_1() - .text_sm() - .border_color(gpui::black().opacity(0.1)) -} - -fn line(color: Hsla) -> Div { - div().w(px(480.)).h_2().bg(color.opacity(0.25)) -} - -impl HelloWorld { - fn render_secondary_popover( - &mut self, - _window: &mut Window, - cx: &mut Context, - ) -> impl IntoElement { - button("secondary-btn") - .mt_2() - .child("Child Popover") - .on_click(cx.listener(|this, _, _, cx| { - this.secondary_open = true; - cx.notify(); - })) - .when(self.secondary_open, |this| { - this.child( - // Now GPUI supports nested deferred! - deferred( - anchored() - .anchor(Anchor::TopLeft) - .snap_to_window_with_margin(px(8.)) - .child( - popover() - .child("This is second level Popover with nested deferred!") - .bg(gpui::white()) - .border_color(gpui::blue()) - .on_mouse_down_out(cx.listener(|this, _, _, cx| { - this.secondary_open = false; - cx.notify(); - })), - ), - ) - .priority(2), - ) - }) - } -} - -impl Render for HelloWorld { - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .flex() - .flex_col() - .gap_3() - .size_full() - .bg(gpui::white()) - .text_color(gpui::black()) - .justify_center() - .items_center() - .child( - div() - .flex() - .flex_row() - .gap_4() - .child( - button("popover0").child("Opened Popover").child( - deferred( - anchored() - .anchor(Anchor::TopLeft) - .snap_to_window_with_margin(px(8.)) - .child(popover().w_96().gap_3().child( - "This is a default opened Popover, \ - we can use deferred to render it \ - in a floating layer.", - )), - ) - .priority(0), - ), - ) - .child( - button("popover1") - .child("Open Popover") - .on_click(cx.listener(|this, _, _, cx| { - this.open = true; - cx.notify(); - })) - .when(self.open, |this| { - this.child( - deferred( - anchored() - .anchor(Anchor::TopLeft) - .snap_to_window_with_margin(px(8.)) - .child( - popover() - .w_96() - .gap_3() - .child( - "This is first level Popover, \ - we can use deferred to render it \ - in a floating layer.\n\ - Click outside to close.", - ) - .when(!self.secondary_open, |this| { - this.on_mouse_down_out(cx.listener( - |this, _, _, cx| { - this.open = false; - cx.notify(); - }, - )) - }) - // Here we need render popover after the content - // to ensure it will be on top layer. - .child( - self.render_secondary_popover(window, cx), - ), - ), - ) - .priority(1), - ) - }), - ), - ) - .child( - "Here is an example text rendered, \ - to ensure the Popover will float above this contents.", - ) - .children([ - line(gpui::red()), - line(gpui::yellow()), - line(gpui::blue()), - line(gpui::green()), - ]) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - cx.open_window(WindowOptions::default(), |_, cx| { - cx.new(|_| HelloWorld { - open: false, - secondary_open: false, - }) - }) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/scrollable.rs b/crates/gpui_pre/examples/scrollable.rs deleted file mode 100644 index c8179561..00000000 --- a/crates/gpui_pre/examples/scrollable.rs +++ /dev/null @@ -1,78 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{App, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px, size}; -use gpui_platform::application; - -struct Scrollable {} - -impl Render for Scrollable { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .size_full() - .id("vertical") - .p_4() - .overflow_scroll() - .bg(gpui::white()) - .child("Example for test 2 way scroll in nested layout") - .child( - div() - .h(px(5000.)) - .border_1() - .border_color(gpui::blue()) - .bg(gpui::blue().opacity(0.05)) - .p_4() - .child( - div() - .mb_5() - .w_full() - .id("horizontal") - .overflow_scroll() - .child( - div() - .w(px(2000.)) - .h(px(150.)) - .bg(gpui::green().opacity(0.1)) - .hover(|this| this.bg(gpui::green().opacity(0.2))) - .border_1() - .border_color(gpui::green()) - .p_4() - .child("Scroll Horizontal"), - ), - ) - .child("Scroll Vertical"), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| cx.new(|_| Scrollable {}), - ) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/set_menus.rs b/crates/gpui_pre/examples/set_menus.rs deleted file mode 100644 index 508a60e9..00000000 --- a/crates/gpui_pre/examples/set_menus.rs +++ /dev/null @@ -1,134 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Context, Global, Menu, MenuItem, SharedString, SystemMenuType, Window, WindowOptions, - actions, div, prelude::*, -}; -use gpui_platform::application; - -struct SetMenus; - -impl Render for SetMenus { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .flex() - .bg(gpui::white()) - .size_full() - .justify_center() - .items_center() - .text_xl() - .text_color(gpui::black()) - .child("Set Menus Example") - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - cx.set_global(AppState::new()); - - // Bring the menu bar to the foreground (so you can see the menu bar) - cx.activate(true); - // Register the `quit` function so it can be referenced - // by the `MenuItem::action` in the menu bar - cx.on_action(quit); - cx.on_action(toggle_check); - // Add menu items - set_app_menus(cx); - cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| SetMenus {})) - .unwrap(); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} - -#[derive(PartialEq)] -enum ViewMode { - List, - Grid, -} - -impl ViewMode { - fn toggle(&mut self) { - *self = match self { - ViewMode::List => ViewMode::Grid, - ViewMode::Grid => ViewMode::List, - } - } -} - -impl Into for ViewMode { - fn into(self) -> SharedString { - match self { - ViewMode::List => "List", - ViewMode::Grid => "Grid", - } - .into() - } -} - -struct AppState { - view_mode: ViewMode, -} - -impl AppState { - fn new() -> Self { - Self { - view_mode: ViewMode::List, - } - } -} - -impl Global for AppState {} - -fn set_app_menus(cx: &mut App) { - let app_state = cx.global::(); - cx.set_menus([Menu::new("set_menus").items([ - MenuItem::os_submenu("Services", SystemMenuType::Services), - MenuItem::separator(), - MenuItem::action("Disabled Item", gpui::NoAction).disabled(true), - MenuItem::submenu(Menu::new("Disabled Submenu").disabled(true)), - MenuItem::separator(), - MenuItem::action("List Mode", ToggleCheck).checked(app_state.view_mode == ViewMode::List), - MenuItem::submenu( - Menu::new("Mode").items([ - MenuItem::action(ViewMode::List, ToggleCheck) - .checked(app_state.view_mode == ViewMode::List), - MenuItem::action(ViewMode::Grid, ToggleCheck) - .checked(app_state.view_mode == ViewMode::Grid), - ]), - ), - MenuItem::separator(), - MenuItem::action("Quit", Quit), - ])]); -} - -// Associate actions using the `actions!` macro (or `Action` derive macro) -actions!(set_menus, [Quit, ToggleCheck]); - -// Define the quit function that is registered with the App -fn quit(_: &Quit, cx: &mut App) { - println!("Gracefully quitting the application..."); - cx.quit(); -} - -fn toggle_check(_: &ToggleCheck, cx: &mut App) { - let app_state = cx.global_mut::(); - app_state.view_mode.toggle(); - set_app_menus(cx); -} diff --git a/crates/gpui_pre/examples/shadow.rs b/crates/gpui_pre/examples/shadow.rs deleted file mode 100644 index 2d8dc094..00000000 --- a/crates/gpui_pre/examples/shadow.rs +++ /dev/null @@ -1,622 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, BoxShadow, Context, Div, SharedString, Window, WindowBounds, WindowOptions, div, - hsla, prelude::*, px, relative, rgb, size, -}; -use gpui_platform::application; - -struct Shadow {} - -impl Shadow { - fn base() -> Div { - div() - .size_16() - .bg(rgb(0xffffff)) - .rounded_full() - .border_1() - .border_color(hsla(0.0, 0.0, 0.0, 0.1)) - } - - fn square() -> Div { - div() - .size_16() - .bg(rgb(0xffffff)) - .border_1() - .border_color(hsla(0.0, 0.0, 0.0, 0.1)) - } - - fn rounded_small() -> Div { - div() - .size_16() - .bg(rgb(0xffffff)) - .rounded(px(4.)) - .border_1() - .border_color(hsla(0.0, 0.0, 0.0, 0.1)) - } - - fn rounded_medium() -> Div { - div() - .size_16() - .bg(rgb(0xffffff)) - .rounded(px(8.)) - .border_1() - .border_color(hsla(0.0, 0.0, 0.0, 0.1)) - } - - fn rounded_large() -> Div { - div() - .size_16() - .bg(rgb(0xffffff)) - .rounded(px(12.)) - .border_1() - .border_color(hsla(0.0, 0.0, 0.0, 0.1)) - } -} - -fn example(label: impl Into, example: impl IntoElement) -> impl IntoElement { - let label = label.into(); - - div() - .flex() - .flex_col() - .justify_center() - .items_center() - .w(relative(1. / 6.)) - .border_r_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .child( - div() - .flex() - .items_center() - .justify_center() - .flex_1() - .py_12() - .child(example), - ) - .child( - div() - .w_full() - .border_t_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .p_1() - .flex() - .items_center() - .child(label), - ) -} - -impl Render for Shadow { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .id("shadow-example") - .overflow_y_scroll() - .bg(rgb(0xffffff)) - .size_full() - .text_xs() - .child(div().flex().flex_col().w_full().children(vec![ - div() - .border_b_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .flex() - .flex_row() - .children(vec![ - example( - "Square", - Shadow::square().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Rounded 4", - Shadow::rounded_small().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Rounded 8", - Shadow::rounded_medium().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Rounded 16", - Shadow::rounded_large().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Circle", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - ]), - div() - .border_b_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .flex() - .w_full() - .children(vec![ - example("None", Shadow::base()), - // 2Xsmall shadow - example("2X Small", Shadow::base().shadow_2xs()), - // Xsmall shadow - example("Extra Small", Shadow::base().shadow_xs()), - // Small shadow - example("Small", Shadow::base().shadow_sm()), - // Medium shadow - example("Medium", Shadow::base().shadow_md()), - // Large shadow - example("Large", Shadow::base().shadow_lg()), - example("Extra Large", Shadow::base().shadow_xl()), - example("2X Large", Shadow::base().shadow_2xl()), - ]), - // Horizontal list of increasing blur radii - div() - .border_b_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .flex() - .children(vec![ - example( - "Blur 0", - Shadow::base().shadow(vec![BoxShadow::new( - px(0.), - px(8.), - hsla(0.0, 0.0, 0.0, 0.3), - )]), - ), - example( - "Blur 2", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(2.)), - ]), - ), - example( - "Blur 4", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(4.)), - ]), - ), - example( - "Blur 8", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Blur 16", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(16.)), - ]), - ), - ]), - // Horizontal list of increasing spread radii - div() - .border_b_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .flex() - .children(vec![ - example( - "Spread 0", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Spread 2", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(8.)) - .spread_radius(px(2.)), - ]), - ), - example( - "Spread 4", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(8.)) - .spread_radius(px(4.)), - ]), - ), - example( - "Spread 8", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(8.)) - .spread_radius(px(8.)), - ]), - ), - example( - "Spread 16", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(8.)) - .spread_radius(px(16.)), - ]), - ), - ]), - // Square spread examples - div() - .border_b_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .flex() - .children(vec![ - example( - "Square Spread 0", - Shadow::square().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Square Spread 8", - Shadow::square().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(8.)) - .spread_radius(px(8.)), - ]), - ), - example( - "Square Spread 16", - Shadow::square().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(8.)) - .spread_radius(px(16.)), - ]), - ), - ]), - // Rounded large spread examples - div() - .border_b_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .flex() - .children(vec![ - example( - "Rounded Large Spread 0", - Shadow::rounded_large().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Rounded Large Spread 8", - Shadow::rounded_large().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(8.)) - .spread_radius(px(8.)), - ]), - ), - example( - "Rounded Large Spread 16", - Shadow::rounded_large().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.3)) - .blur_radius(px(8.)) - .spread_radius(px(16.)), - ]), - ), - ]), - // Directional shadows - div() - .border_b_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .flex() - .children(vec![ - example( - "Left", - Shadow::base().shadow(vec![ - BoxShadow::new(px(-8.), px(0.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Right", - Shadow::base().shadow(vec![ - BoxShadow::new(px(8.), px(0.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Top", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(-8.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Bottom", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - ]), - // Square directional shadows - div() - .border_b_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .flex() - .children(vec![ - example( - "Square Left", - Shadow::square().shadow(vec![ - BoxShadow::new(px(-8.), px(0.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Square Right", - Shadow::square().shadow(vec![ - BoxShadow::new(px(8.), px(0.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Square Top", - Shadow::square().shadow(vec![ - BoxShadow::new(px(0.), px(-8.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Square Bottom", - Shadow::square().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - ]), - // Rounded large directional shadows - div() - .border_b_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .flex() - .children(vec![ - example( - "Rounded Large Left", - Shadow::rounded_large().shadow(vec![ - BoxShadow::new(px(-8.), px(0.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Rounded Large Right", - Shadow::rounded_large().shadow(vec![ - BoxShadow::new(px(8.), px(0.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Rounded Large Top", - Shadow::rounded_large().shadow(vec![ - BoxShadow::new(px(0.), px(-8.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - example( - "Rounded Large Bottom", - Shadow::rounded_large().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.5, 0.5, 0.3)) - .blur_radius(px(8.)), - ]), - ), - ]), - // Multiple shadows for different shapes - div() - .border_b_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .flex() - .children(vec![ - example( - "Circle Multiple", - Shadow::base().shadow(vec![ - BoxShadow::new( - px(0.), - px(-12.), - hsla(0.0 / 360., 1.0, 0.5, 0.3), - ) - .blur_radius(px(8.)) - .spread_radius(px(2.)), - BoxShadow::new( - px(12.), - px(0.), - hsla(60.0 / 360., 1.0, 0.5, 0.3), - ) - .blur_radius(px(8.)) - .spread_radius(px(2.)), - BoxShadow::new( - px(0.), - px(12.), - hsla(120.0 / 360., 1.0, 0.5, 0.3), - ) - .blur_radius(px(8.)) - .spread_radius(px(2.)), - BoxShadow::new( - px(-12.), - px(0.), - hsla(240.0 / 360., 1.0, 0.5, 0.3), - ) - .blur_radius(px(8.)) - .spread_radius(px(2.)), - ]), - ), - example( - "Square Multiple", - Shadow::square().shadow(vec![ - BoxShadow::new( - px(0.), - px(-12.), - hsla(0.0 / 360., 1.0, 0.5, 0.3), - ) - .blur_radius(px(8.)) - .spread_radius(px(2.)), - BoxShadow::new( - px(12.), - px(0.), - hsla(60.0 / 360., 1.0, 0.5, 0.3), - ) - .blur_radius(px(8.)) - .spread_radius(px(2.)), - BoxShadow::new( - px(0.), - px(12.), - hsla(120.0 / 360., 1.0, 0.5, 0.3), - ) - .blur_radius(px(8.)) - .spread_radius(px(2.)), - BoxShadow::new( - px(-12.), - px(0.), - hsla(240.0 / 360., 1.0, 0.5, 0.3), - ) - .blur_radius(px(8.)) - .spread_radius(px(2.)), - ]), - ), - example( - "Rounded Large Multiple", - Shadow::rounded_large().shadow(vec![ - BoxShadow::new( - px(0.), - px(-12.), - hsla(0.0 / 360., 1.0, 0.5, 0.3), - ) - .blur_radius(px(8.)) - .spread_radius(px(2.)), - BoxShadow::new( - px(12.), - px(0.), - hsla(60.0 / 360., 1.0, 0.5, 0.3), - ) - .blur_radius(px(8.)) - .spread_radius(px(2.)), - BoxShadow::new( - px(0.), - px(12.), - hsla(120.0 / 360., 1.0, 0.5, 0.3), - ) - .blur_radius(px(8.)) - .spread_radius(px(2.)), - BoxShadow::new( - px(-12.), - px(0.), - hsla(240.0 / 360., 1.0, 0.5, 0.3), - ) - .blur_radius(px(8.)) - .spread_radius(px(2.)), - ]), - ), - ]), - // Inset shadows (CSS `box-shadow: inset ...`). - div() - .border_b_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .flex() - .w_full() - .children(vec![ - example( - "Inset basic", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(0.), hsla(0.0, 0.0, 0.0, 0.5)) - .blur_radius(px(12.)) - .inset(), - ]), - ), - example( - "Inset offset", - Shadow::base().shadow(vec![ - BoxShadow::new(px(6.), px(6.), hsla(0.0, 0.0, 0.0, 0.5)) - .blur_radius(px(8.)) - .inset(), - ]), - ), - example( - "Inset spread", - Shadow::base().shadow(vec![ - BoxShadow::new(px(0.), px(0.), hsla(0.0, 0.0, 0.0, 0.5)) - .blur_radius(px(4.)) - .spread_radius(px(8.)) - .inset(), - ]), - ), - example( - "Inset rounded", - Shadow::rounded_large().shadow(vec![ - BoxShadow::new(px(0.), px(4.), hsla(0.0, 0.0, 0.0, 0.5)) - .blur_radius(px(10.)) - .spread_radius(px(2.)) - .inset(), - ]), - ), - example( - "Inset sharp", - Shadow::square().shadow(vec![ - BoxShadow::new(px(0.), px(0.), hsla(0.0, 0.0, 0.0, 0.6)) - .spread_radius(px(6.)) - .inset(), - ]), - ), - ]), - // Combined: drop + inset shadows on the same element. - div() - .border_b_1() - .border_color(hsla(0.0, 0.0, 0.0, 1.0)) - .flex() - .w_full() - .children(vec![example( - "Drop + Inset", - Shadow::rounded_medium().shadow(vec![ - BoxShadow::new(px(0.), px(8.), hsla(0.0, 0.0, 0.0, 0.25)) - .blur_radius(px(12.)), - BoxShadow::new(px(0.), px(2.), hsla(0.0, 0.0, 0.0, 0.4)) - .blur_radius(px(4.)) - .inset(), - ]), - )]), - ])) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(1000.0), px(800.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| cx.new(|_| Shadow {}), - ) - .unwrap(); - - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/svg/dragon.svg b/crates/gpui_pre/examples/svg/dragon.svg deleted file mode 100644 index e2de1a0e..00000000 --- a/crates/gpui_pre/examples/svg/dragon.svg +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/crates/gpui_pre/examples/svg/svg.rs b/crates/gpui_pre/examples/svg/svg.rs deleted file mode 100644 index c5ac9b4e..00000000 --- a/crates/gpui_pre/examples/svg/svg.rs +++ /dev/null @@ -1,108 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "../example_support/fonts.rs"] -mod example_support; - -use std::fs; -use std::path::PathBuf; - -use anyhow::Result; -use gpui::{ - App, AssetSource, Bounds, Context, SharedString, Window, WindowBounds, WindowOptions, div, - prelude::*, px, rgb, size, svg, -}; -use gpui_platform::application; - -struct Assets { - base: PathBuf, -} - -impl AssetSource for Assets { - fn load(&self, path: &str) -> Result>> { - fs::read(self.base.join(path)) - .map(|data| Some(std::borrow::Cow::Owned(data))) - .map_err(|err| err.into()) - } - - fn list(&self, path: &str) -> Result> { - fs::read_dir(self.base.join(path)) - .map(|entries| { - entries - .filter_map(|entry| { - entry - .ok() - .and_then(|entry| entry.file_name().into_string().ok()) - .map(SharedString::from) - }) - .collect() - }) - .map_err(|err| err.into()) - } -} - -struct SvgExample; - -impl Render for SvgExample { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .flex() - .flex_row() - .size_full() - .justify_center() - .items_center() - .gap_8() - .bg(rgb(0xffffff)) - .child( - svg() - .path("svg/dragon.svg") - .size_8() - .text_color(rgb(0xff0000)), - ) - .child( - svg() - .path("svg/dragon.svg") - .size_8() - .text_color(rgb(0x00ff00)), - ) - .child( - svg() - .path("svg/dragon.svg") - .size_8() - .text_color(rgb(0x0000ff)), - ) - } -} - -fn run_example() { - application() - .with_assets(Assets { - base: PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples"), - }) - .run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(300.0), px(300.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| cx.new(|_| SvgExample), - ) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/system_notifications.rs b/crates/gpui_pre/examples/system_notifications.rs deleted file mode 100644 index faf69191..00000000 --- a/crates/gpui_pre/examples/system_notifications.rs +++ /dev/null @@ -1,155 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -//! Demonstrates posting, replacing, dismissing, and responding to system notifications. - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, Div, SharedString, Stateful, SystemNotification, - SystemNotificationAction, SystemNotificationResponse, Window, WindowBounds, WindowOptions, div, - prelude::*, px, rgb, size, -}; -use gpui_platform::application; - -const NOTIFICATION_TAG: &str = "gpui-system-notification-example"; - -struct SystemNotificationExample { - revision: usize, - status: SharedString, -} - -impl Render for SystemNotificationExample { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .flex() - .flex_col() - .size_full() - .gap_4() - .p_8() - .bg(rgb(0x18181b)) - .text_color(rgb(0xf4f4f5)) - .child(div().text_2xl().child("GPUI system notifications")) - .child(div().text_sm().text_color(rgb(0xa1a1aa)).child( - "Post repeatedly to replace the notification with the same tag. Click the \ - notification body or an action button to send a response back to GPUI.", - )) - .child( - div() - .flex() - .gap_3() - .child(button("show", "Show or replace").on_click(cx.listener( - |this, _, _, cx| { - this.revision += 1; - let revision = this.revision; - cx.show_system_notification(SystemNotification { - tag: NOTIFICATION_TAG.into(), - title: format!("Example notification {revision}").into(), - body: "This notification was posted by the GPUI example.".into(), - actions: vec![ - SystemNotificationAction { - id: "open".into(), - label: "Open".into(), - }, - SystemNotificationAction { - id: "snooze".into(), - label: "Snooze".into(), - }, - ], - }); - this.status = format!("Posted notification revision {revision}").into(); - cx.notify(); - }, - ))) - .child( - button("dismiss", "Dismiss").on_click(cx.listener(|this, _, _, cx| { - cx.dismiss_system_notification(NOTIFICATION_TAG); - this.status = "Dismissed the notification".into(); - cx.notify(); - })), - ), - ) - .child( - div() - .mt_2() - .p_4() - .rounded_md() - .bg(rgb(0x27272a)) - .child(self.status.clone()), - ) - .when(cfg!(target_os = "macos"), |this| { - this.child(div().mt_2().text_xs().text_color(rgb(0x71717a)).child( - "macOS only delivers notifications when this example runs from an app bundle.", - )) - }) - } -} - -fn button(id: &'static str, label: &'static str) -> Stateful
{ - div() - .id(id) - .px_4() - .py_2() - .rounded_md() - .bg(rgb(0x3f3f46)) - .hover(|style| style.bg(rgb(0x52525b))) - .active(|style| style.bg(rgb(0x71717a))) - .cursor_pointer() - .child(label) -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - cx.set_app_identity("dev.zed.gpui.system-notifications", "GPUI Notifications"); - - let view = cx.new(|_| SystemNotificationExample { - revision: 0, - status: "No notification posted yet".into(), - }); - cx.on_system_notification_response({ - let view = view.clone(); - move |response, cx| { - let SystemNotificationResponse { tag, action_id } = response; - view.update(cx, |this, cx| { - this.status = match action_id { - Some(action_id) => { - format!("Received action '{action_id}' for tag '{tag}'").into() - } - None => format!("Notification body clicked for tag '{tag}'").into(), - }; - cx.notify(); - }); - } - }); - - let bounds = Bounds::centered(None, size(px(560.), px(360.)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - titlebar: Some(gpui::TitlebarOptions { - title: Some("System Notifications Example".into()), - ..Default::default() - }), - ..Default::default() - }, - move |_, _| view, - ) - .expect("failed to open system notifications example window"); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/tab_stop.rs b/crates/gpui_pre/examples/tab_stop.rs deleted file mode 100644 index cc7c34a9..00000000 --- a/crates/gpui_pre/examples/tab_stop.rs +++ /dev/null @@ -1,220 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, Div, ElementId, FocusHandle, KeyBinding, SharedString, Stateful, Window, - WindowBounds, WindowOptions, actions, div, prelude::*, px, size, -}; -use gpui_platform::application; - -actions!(example, [Tab, TabPrev]); - -struct Example { - focus_handle: FocusHandle, - items: Vec, - message: SharedString, -} - -impl Example { - fn new(window: &mut Window, cx: &mut Context) -> Self { - let items = vec![ - cx.focus_handle().tab_index(1).tab_stop(true), - cx.focus_handle().tab_index(2).tab_stop(true), - cx.focus_handle().tab_index(3).tab_stop(true), - cx.focus_handle(), - cx.focus_handle().tab_index(2).tab_stop(true), - ]; - - let focus_handle = cx.focus_handle(); - window.focus(&focus_handle, cx); - - Self { - focus_handle, - items, - message: SharedString::from("Press `Tab`, `Shift-Tab` to switch focus."), - } - } - - fn on_tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context) { - window.focus_next(cx); - self.message = SharedString::from("You have pressed `Tab`."); - } - - fn on_tab_prev(&mut self, _: &TabPrev, window: &mut Window, cx: &mut Context) { - window.focus_prev(cx); - self.message = SharedString::from("You have pressed `Shift-Tab`."); - } -} - -impl Render for Example { - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - fn tab_stop_style(this: T) -> T { - this.border_3().border_color(gpui::blue()) - } - - fn button(id: impl Into) -> Stateful
{ - div() - .id(id) - .h_10() - .flex_1() - .flex() - .justify_center() - .items_center() - .border_1() - .border_color(gpui::black()) - .bg(gpui::black()) - .text_color(gpui::white()) - .focus(tab_stop_style) - .shadow_sm() - } - - div() - .id("app") - .track_focus(&self.focus_handle) - .on_action(cx.listener(Self::on_tab)) - .on_action(cx.listener(Self::on_tab_prev)) - .size_full() - .flex() - .flex_col() - .p_4() - .gap_3() - .bg(gpui::white()) - .text_color(gpui::black()) - .child(self.message.clone()) - .children( - self.items - .clone() - .into_iter() - .enumerate() - .map(|(ix, item_handle)| { - div() - .id(("item", ix)) - .track_focus(&item_handle) - .h_10() - .w_full() - .flex() - .justify_center() - .items_center() - .border_1() - .border_color(gpui::black()) - .when( - item_handle.tab_stop && item_handle.is_focused(window), - tab_stop_style, - ) - .map(|this| match item_handle.tab_stop { - true => this - .hover(|this| this.bg(gpui::black().opacity(0.1))) - .child(format!("tab_index: {}", item_handle.tab_index)), - false => this.opacity(0.4).child("tab_stop: false"), - }) - }), - ) - .child( - div() - .flex() - .flex_row() - .gap_3() - .items_center() - .child( - button("el1") - .tab_index(4) - .child("Button 1") - .on_click(cx.listener(|this, _, _, cx| { - this.message = "You have clicked Button 1.".into(); - cx.notify(); - })), - ) - .child( - button("el2") - .tab_index(5) - .child("Button 2") - .on_click(cx.listener(|this, _, _, cx| { - this.message = "You have clicked Button 2.".into(); - cx.notify(); - })), - ), - ) - .child( - div() - .id("group-1") - .tab_index(6) - .tab_group() - .tab_stop(false) - .child( - button("group-1-button-1") - .tab_index(1) - .child("Tab index [6, 1]"), - ) - .child( - button("group-1-button-2") - .tab_index(2) - .child("Tab index [6, 2]"), - ) - .child( - button("group-1-button-3") - .tab_index(3) - .child("Tab index [6, 3]"), - ), - ) - .child( - div() - .id("group-2") - .tab_index(7) - .tab_group() - .tab_stop(false) - .child( - button("group-2-button-1") - .tab_index(1) - .child("Tab index [7, 1]"), - ) - .child( - button("group-2-button-2") - .tab_index(2) - .child("Tab index [7, 2]"), - ) - .child( - button("group-2-button-3") - .tab_index(3) - .child("Tab index [7, 3]"), - ), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - cx.bind_keys([ - KeyBinding::new("tab", Tab, None), - KeyBinding::new("shift-tab", TabPrev, None), - ]); - - let bounds = Bounds::centered(None, size(px(800.), px(600.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |window, cx| cx.new(|cx| Example::new(window, cx)), - ) - .unwrap(); - - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/testing.rs b/crates/gpui_pre/examples/testing.rs deleted file mode 100644 index 76e3b773..00000000 --- a/crates/gpui_pre/examples/testing.rs +++ /dev/null @@ -1,559 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -//! Example demonstrating GPUI's testing infrastructure. -//! -//! When run normally, this displays an interactive counter window. -//! The tests below demonstrate various GPUI testing patterns. -//! -//! Run the app: cargo run -p gpui --example testing -//! Run tests: cargo test -p gpui --example testing --features test-support - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, FocusHandle, Focusable, Render, Task, Window, WindowBounds, - WindowOptions, actions, div, prelude::*, px, rgb, size, -}; -use gpui_platform::application; - -actions!(counter, [Increment, Decrement]); - -struct Counter { - count: i32, - focus_handle: FocusHandle, - _subscription: gpui::Subscription, -} - -/// Event emitted by Counter -struct CounterEvent; - -impl gpui::EventEmitter for Counter {} - -impl Counter { - fn new(cx: &mut Context) -> Self { - let subscription = cx.subscribe_self(|this: &mut Self, _event: &CounterEvent, _cx| { - this.count = 999; - }); - - Self { - count: 0, - focus_handle: cx.focus_handle(), - _subscription: subscription, - } - } - - fn increment(&mut self, _: &Increment, _window: &mut Window, cx: &mut Context) { - self.count += 1; - cx.notify(); - } - - fn decrement(&mut self, _: &Decrement, _window: &mut Window, cx: &mut Context) { - self.count -= 1; - cx.notify(); - } - - fn load(&self, cx: &mut Context) -> Task<()> { - cx.spawn(async move |this, cx| { - // Simulate loading data (e.g., from disk or network) - this.update(cx, |counter, _| { - counter.count = 100; - }) - .ok(); - }) - } - - fn reload(&self, cx: &mut Context) { - cx.spawn(async move |this, cx| { - // Simulate reloading data in the background - this.update(cx, |counter, _| { - counter.count += 50; - }) - .ok(); - }) - .detach(); - } -} - -impl Focusable for Counter { - fn focus_handle(&self, _cx: &App) -> FocusHandle { - self.focus_handle.clone() - } -} - -impl Render for Counter { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .id("counter") - .key_context("Counter") - .on_action(cx.listener(Self::increment)) - .on_action(cx.listener(Self::decrement)) - .track_focus(&self.focus_handle) - .flex() - .flex_col() - .gap_4() - .bg(rgb(0x1e1e2e)) - .size_full() - .justify_center() - .items_center() - .child( - div() - .text_3xl() - .text_color(rgb(0xcdd6f4)) - .child(format!("{}", self.count)), - ) - .child( - div() - .flex() - .gap_2() - .child( - div() - .id("decrement") - .px_4() - .py_2() - .bg(rgb(0x313244)) - .hover(|s| s.bg(rgb(0x45475a))) - .rounded_md() - .cursor_pointer() - .text_color(rgb(0xcdd6f4)) - .on_click(cx.listener(|this, _, window, cx| { - this.decrement(&Decrement, window, cx) - })) - .child("−"), - ) - .child( - div() - .id("increment") - .px_4() - .py_2() - .bg(rgb(0x313244)) - .hover(|s| s.bg(rgb(0x45475a))) - .rounded_md() - .cursor_pointer() - .text_color(rgb(0xcdd6f4)) - .on_click(cx.listener(|this, _, window, cx| { - this.increment(&Increment, window, cx) - })) - .child("+"), - ), - ) - .child( - div() - .flex() - .gap_2() - .child( - div() - .id("load") - .px_4() - .py_2() - .bg(rgb(0x313244)) - .hover(|s| s.bg(rgb(0x45475a))) - .rounded_md() - .cursor_pointer() - .text_color(rgb(0xcdd6f4)) - .on_click(cx.listener(|this, _, _, cx| { - this.load(cx).detach(); - })) - .child("Load"), - ) - .child( - div() - .id("reload") - .px_4() - .py_2() - .bg(rgb(0x313244)) - .hover(|s| s.bg(rgb(0x45475a))) - .rounded_md() - .cursor_pointer() - .text_color(rgb(0xcdd6f4)) - .on_click(cx.listener(|this, _, _, cx| { - this.reload(cx); - })) - .child("Reload"), - ), - ) - .child( - div() - .text_sm() - .text_color(rgb(0x6c7086)) - .child("Press ↑/↓ or click buttons"), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - cx.bind_keys([ - gpui::KeyBinding::new("up", Increment, Some("Counter")), - gpui::KeyBinding::new("down", Decrement, Some("Counter")), - ]); - - let bounds = Bounds::centered(None, size(px(300.), px(200.)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |window, cx| { - let counter = cx.new(|cx| Counter::new(cx)); - counter.focus_handle(cx).focus(window, cx); - counter - }, - ) - .unwrap(); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} - -#[cfg(test)] -mod tests { - use super::*; - use gpui::{TestAppContext, VisualTestContext}; - use rand::prelude::*; - - /// Here's a basic GPUI test. Just add the macro and take a TestAppContext as an argument! - /// - /// Note that synchronous side effects run immediately after your "update*" calls complete. - #[gpui::test] - fn basic_testing(cx: &mut TestAppContext) { - let counter = cx.new(|cx| Counter::new(cx)); - - counter.update(cx, |counter, _| { - counter.count = 42; - }); - - // Note that TestAppContext doesn't support `read(cx)` - let updated = counter.read_with(cx, |counter, _| counter.count); - assert_eq!(updated, 42); - - // Emit an event - the subscriber will run immediately after the update finishes - counter.update(cx, |_, cx| { - cx.emit(CounterEvent); - }); - - let count_after_update = counter.read_with(cx, |counter, _| counter.count); - assert_eq!( - count_after_update, 999, - "Side effects should run after update completes" - ); - } - - /// Tests which involve the window require you to construct a VisualTestContext. - /// Just like synchronous side effects, the window will be drawn after every "update*" - /// call, so you can test render-dependent behavior. - #[gpui::test] - fn test_counter_in_window(cx: &mut TestAppContext) { - let window = cx.update(|cx| { - cx.open_window(Default::default(), |_, cx| cx.new(|cx| Counter::new(cx))) - .unwrap() - }); - - let mut cx = VisualTestContext::from_window(window.into(), cx); - let counter = window.root(&mut cx).unwrap(); - - // Action dispatch depends on the element tree to resolve which action handler - // to call, and this works exactly as you'd expect in a test. - let focus_handle = counter.read_with(&cx, |counter, _| counter.focus_handle.clone()); - cx.update(|window, cx| { - focus_handle.dispatch_action(&Increment, window, cx); - }); - - let count_after = counter.read_with(&cx, |counter, _| counter.count); - assert_eq!( - count_after, 1, - "Action dispatched via focus handle should increment" - ); - } - - /// GPUI tests can also be async, simply add the async keyword before the test. - /// Note that the test executor is single thread, so async side effects (including - /// background tasks) won't run until you explicitly yield control. - #[gpui::test] - async fn test_async_operations(cx: &mut TestAppContext) { - let counter = cx.new(|cx| Counter::new(cx)); - - // Tasks can be awaited directly - counter.update(cx, |counter, cx| counter.load(cx)).await; - - let count = counter.read_with(cx, |counter, _| counter.count); - assert_eq!(count, 100, "Load task should have set count to 100"); - - // But side effects don't run until you yield control - counter.update(cx, |counter, cx| counter.reload(cx)); - - let count = counter.read_with(cx, |counter, _| counter.count); - assert_eq!(count, 100, "Detached reload task shouldn't have run yet"); - - // This runs all pending tasks - cx.run_until_parked(); - - let count = counter.read_with(cx, |counter, _| counter.count); - assert_eq!(count, 150, "Reload task should have run after parking"); - } - - /// Note that the test executor panics if you await a future that waits on - /// something outside GPUI's control, like a reading a file or network IO. - /// You should mock external systems where possible, as this feature can be used - /// to detect potential deadlocks in your async code. - /// - /// However, if you want to disable this check use `allow_parking()` - #[gpui::test] - async fn test_allow_parking(cx: &mut TestAppContext) { - // Allow the thread to park - cx.executor().allow_parking(); - - // Simulate an external system (like a file system) with an OS thread - let (tx, rx) = futures::channel::oneshot::channel(); - std::thread::spawn(move || { - std::thread::sleep(std::time::Duration::from_millis(5)); - tx.send(42).ok(); - }); - - // Without allow_parking(), this await would panic because GPUI's - // scheduler runs out of tasks while waiting for the external thread. - let result = rx.await.unwrap(); - assert_eq!(result, 42); - } - - /// GPUI also provides support for property testing, via the iterations flag - #[gpui::test(iterations = 10)] - fn test_counter_random_operations(cx: &mut TestAppContext, mut rng: StdRng) { - let window = cx.update(|cx| { - cx.open_window(Default::default(), |_, cx| cx.new(|cx| Counter::new(cx))) - .unwrap() - }); - let mut cx = VisualTestContext::from_window(window.into(), cx); - - let counter = cx.new(|cx| Counter::new(cx)); - - // Perform random increments/decrements - let mut expected = 0i32; - for _ in 0..100 { - if rng.random_bool(0.5) { - expected += 1; - counter.update_in(&mut cx, |counter, window, cx| { - counter.increment(&Increment, window, cx) - }); - } else { - expected -= 1; - counter.update_in(&mut cx, |counter, window, cx| { - counter.decrement(&Decrement, window, cx) - }); - } - } - - let actual = counter.read_with(&cx, |counter, _| counter.count); - assert_eq!( - actual, expected, - "Counter should match expected after random ops" - ); - } - - /// Now, all of those tests are good, but GPUI also provides strong support for testing distributed systems. - /// Let's setup a mock network and enhance the counter to send messages over it. - mod distributed_systems { - use std::sync::{Arc, Mutex}; - - /// The state of the mock network. - struct MockNetworkState { - ordering: Vec, - a_to_b: Vec, - b_to_a: Vec, - } - - /// A mock network that delivers messages between two peers. - #[derive(Clone)] - struct MockNetwork { - state: Arc>, - } - - impl MockNetwork { - fn new() -> Self { - Self { - state: Arc::new(Mutex::new(MockNetworkState { - ordering: Vec::new(), - a_to_b: Vec::new(), - b_to_a: Vec::new(), - })), - } - } - - fn a_client(&self) -> NetworkClient { - NetworkClient { - network: self.clone(), - is_a: true, - } - } - - fn b_client(&self) -> NetworkClient { - NetworkClient { - network: self.clone(), - is_a: false, - } - } - } - - /// A client handle for sending/receiving messages over the mock network. - #[derive(Clone)] - struct NetworkClient { - network: MockNetwork, - is_a: bool, - } - - // See, networking is easy! - impl NetworkClient { - fn send(&self, value: i32) { - let mut network = self.network.state.lock().unwrap(); - network.ordering.push(value); - if self.is_a { - network.b_to_a.push(value); - } else { - network.a_to_b.push(value); - } - } - - fn receive_all(&self) -> Vec { - let mut network = self.network.state.lock().unwrap(); - if self.is_a { - network.a_to_b.drain(..).collect() - } else { - network.b_to_a.drain(..).collect() - } - } - } - - use gpui::Context; - - /// A networked counter that can send/receive over a mock network. - struct NetworkedCounter { - count: i32, - client: NetworkClient, - } - - impl NetworkedCounter { - fn new(client: NetworkClient) -> Self { - Self { count: 0, client } - } - - /// Increment the counter and broadcast the change. - fn increment(&mut self, delta: i32, cx: &mut Context) { - self.count += delta; - - cx.background_spawn({ - let client = self.client.clone(); - async move { - client.send(delta); - } - }) - .detach(); - } - - /// Process incoming increment requests. - fn sync(&mut self) { - for delta in self.client.receive_all() { - self.count += delta; - } - } - } - - use super::*; - - /// You can simulate distributed systems with multiple app contexts, simply by adding - /// additional parameters. - #[gpui::test] - fn test_app_sync(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { - let network = MockNetwork::new(); - - let a = cx_a.new(|_| NetworkedCounter::new(network.a_client())); - let b = cx_b.new(|_| NetworkedCounter::new(network.b_client())); - - // B increments locally and broadcasts the delta - b.update(cx_b, |b, cx| b.increment(42, cx)); - b.read_with(cx_b, |b, _| assert_eq!(b.count, 42)); // B's count is set immediately - a.read_with(cx_a, |a, _| assert_eq!(a.count, 0)); // A's count is in a side effect - - cx_b.run_until_parked(); // Send the delta from B - a.update(cx_a, |a, _| a.sync()); // Receive the delta at A - - b.read_with(cx_b, |b, _| assert_eq!(b.count, 42)); // Both counts now match - a.read_with(cx_a, |a, _| assert_eq!(a.count, 42)); - } - - /// Multiple apps can run concurrently, and to capture this each test app shares - /// a dispatcher. Whenever you call `run_until_parked`, the dispatcher will randomly - /// pick which app's tasks to run next. This allows you to test that your distributed code - /// is robust to different execution orderings. - #[gpui::test(iterations = 10)] - fn test_random_interleaving( - cx_a: &mut TestAppContext, - cx_b: &mut TestAppContext, - mut rng: StdRng, - ) { - let network = MockNetwork::new(); - - // Track execution order - let mut original_order = Vec::new(); - let a = cx_a.new(|_| NetworkedCounter::new(MockNetwork::a_client(&network))); - let b = cx_b.new(|_| NetworkedCounter::new(MockNetwork::b_client(&network))); - - let num_operations: usize = rng.random_range(3..8); - - for i in 0..num_operations { - let i = i as i32; - let which = rng.random_bool(0.5); - - original_order.push(i); - if which { - b.update(cx_b, |b, cx| b.increment(i, cx)); - } else { - a.update(cx_a, |a, cx| a.increment(i, cx)); - } - } - - // This will send all of the pending increment messages, from both a and b - cx_a.run_until_parked(); - - a.update(cx_a, |a, _| a.sync()); - b.update(cx_b, |b, _| b.sync()); - - let a_count = a.read_with(cx_a, |a, _| a.count); - let b_count = b.read_with(cx_b, |b, _| b.count); - - assert_eq!(a_count, b_count, "A and B should have the same count"); - - // Nicely format the execution order output. - // Run this test with `-- --nocapture` to see it! - let actual = network.state.lock().unwrap().ordering.clone(); - let spawned: Vec<_> = original_order.iter().map(|n| format!("{}", n)).collect(); - let ran: Vec<_> = actual.iter().map(|n| format!("{}", n)).collect(); - let diff: Vec<_> = original_order - .iter() - .zip(actual.iter()) - .map(|(o, a)| { - if o == a { - " ".to_string() - } else { - "^".to_string() - } - }) - .collect(); - println!("spawned: [{}]", spawned.join(", ")); - println!("ran: [{}]", ran.join(", ")); - println!(" [{}]", diff.join(", ")); - } - } -} diff --git a/crates/gpui_pre/examples/text.rs b/crates/gpui_pre/examples/text.rs deleted file mode 100644 index a1244eba..00000000 --- a/crates/gpui_pre/examples/text.rs +++ /dev/null @@ -1,415 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use std::{ - borrow::Cow, - ops::{Deref, DerefMut}, - sync::Arc, -}; - -use gpui::{ - AbsoluteLength, App, Context, DefiniteLength, ElementId, Global, Hsla, Menu, SharedString, - TextStyle, TitlebarOptions, Window, WindowBounds, WindowOptions, bounds, colors::DefaultColors, - div, point, prelude::*, px, relative, rgb, size, -}; -use gpui_platform::application; -use std::iter; - -#[derive(Clone, Debug)] -pub struct TextContext { - font_size: f32, - line_height: f32, - type_scale: f32, -} - -impl Default for TextContext { - fn default() -> Self { - TextContext { - font_size: 16.0, - line_height: 1.3, - type_scale: 1.33, - } - } -} - -impl TextContext { - pub fn get_global(cx: &App) -> &Arc { - &cx.global::().0 - } -} - -#[derive(Clone, Debug)] -pub struct GlobalTextContext(pub Arc); - -impl Deref for GlobalTextContext { - type Target = Arc; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for GlobalTextContext { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl Global for GlobalTextContext {} - -pub trait ActiveTextContext { - fn text_context(&self) -> &Arc; -} - -impl ActiveTextContext for App { - fn text_context(&self) -> &Arc { - &self.global::().0 - } -} - -#[derive(Clone, PartialEq)] -pub struct SpecimenTheme { - pub bg: Hsla, - pub fg: Hsla, -} - -impl Default for SpecimenTheme { - fn default() -> Self { - Self { - bg: gpui::white(), - fg: gpui::black(), - } - } -} - -impl SpecimenTheme { - pub fn invert(&self) -> Self { - Self { - bg: self.fg, - fg: self.bg, - } - } -} - -#[derive(Debug, Clone, PartialEq, IntoElement)] -struct Specimen { - id: ElementId, - scale: f32, - text_style: Option, - string: SharedString, - invert: bool, -} - -impl Specimen { - pub fn new(id: usize) -> Self { - let string = SharedString::new_static("The quick brown fox jumps over the lazy dog"); - let id_string = format!("specimen-{}", id); - let id = ElementId::Name(id_string.into()); - Self { - id, - scale: 1.0, - text_style: None, - string, - invert: false, - } - } - - pub fn invert(mut self) -> Self { - self.invert = !self.invert; - self - } - - pub fn scale(mut self, scale: f32) -> Self { - self.scale = scale; - self - } -} - -impl RenderOnce for Specimen { - fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { - let rem_size = window.rem_size(); - let scale = self.scale; - let global_style = cx.text_context(); - - let style_override = self.text_style; - - let mut font_size = global_style.font_size; - let mut line_height = global_style.line_height; - - if let Some(style_override) = style_override { - font_size = style_override.font_size.to_pixels(rem_size).into(); - line_height = match style_override.line_height { - DefiniteLength::Absolute(absolute_len) => match absolute_len { - AbsoluteLength::Rems(absolute_len) => absolute_len.to_pixels(rem_size).into(), - AbsoluteLength::Pixels(absolute_len) => absolute_len.into(), - }, - DefiniteLength::Fraction(value) => value, - }; - } - - let mut theme = SpecimenTheme::default(); - - if self.invert { - theme = theme.invert(); - } - - div() - .id(self.id) - .bg(theme.bg) - .text_color(theme.fg) - .text_size(px(font_size * scale)) - .line_height(relative(line_height)) - .p(px(10.0)) - .child(self.string) - } -} - -#[derive(Debug, Clone, PartialEq, IntoElement)] -struct CharacterGrid { - scale: f32, - invert: bool, - text_style: Option, -} - -impl CharacterGrid { - pub fn new() -> Self { - Self { - scale: 1.0, - invert: false, - text_style: None, - } - } - - pub fn scale(mut self, scale: f32) -> Self { - self.scale = scale; - self - } -} - -impl RenderOnce for CharacterGrid { - fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement { - let mut theme = SpecimenTheme::default(); - - if self.invert { - theme = theme.invert(); - } - - let characters = vec![ - "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "A", "B", "C", "D", "E", "F", "G", - "H", "I", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", - "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "p", "q", - "r", "s", "t", "u", "v", "w", "x", "y", "z", "ẞ", "ſ", "ß", "ð", "Þ", "þ", "α", "β", - "Γ", "γ", "Δ", "δ", "η", "θ", "ι", "κ", "Λ", "λ", "μ", "ν", "ξ", "π", "τ", "υ", "φ", - "χ", "ψ", "∂", "а", "в", "Ж", "ж", "З", "з", "К", "к", "л", "м", "Н", "н", "Р", "р", - "У", "у", "ф", "ч", "ь", "ы", "Э", "э", "Я", "я", "ij", "öẋ", ".,", "⣝⣑", "~", "*", - "_", "^", "`", "'", "(", "{", "«", "#", "&", "@", "$", "¢", "%", "|", "?", "¶", "µ", - "❮", "<=", "!=", "==", "--", "++", "=>", "->", "🏀", "🎊", "😍", "❤️", "👍", "👎", - ]; - - let columns = 20; - let rows = characters.len().div_ceil(columns); - - let grid_rows = (0..rows).map(|row_idx| { - let start_idx = row_idx * columns; - let end_idx = (start_idx + columns).min(characters.len()); - - div() - .w_full() - .flex() - .flex_row() - .children((start_idx..end_idx).map(|i| { - div() - .text_center() - .size(px(62.)) - .bg(theme.bg) - .text_color(theme.fg) - .text_size(px(24.0)) - .line_height(relative(1.0)) - .child(characters[i]) - })) - .when(end_idx - start_idx < columns, |d| { - d.children( - iter::repeat_with(|| div().flex_1()).take(columns - (end_idx - start_idx)), - ) - }) - }); - - div().p_4().gap_2().flex().flex_col().children(grid_rows) - } -} - -struct TextExample { - next_id: usize, - font_family: SharedString, -} - -impl TextExample { - fn next_id(&mut self) -> usize { - self.next_id += 1; - self.next_id - } - - fn button( - text: &str, - cx: &mut Context, - on_click: impl Fn(&mut Self, &mut Context) + 'static, - ) -> impl IntoElement { - div() - .id(text.to_string()) - .flex_none() - .child(text.to_string()) - .bg(gpui::black()) - .text_color(gpui::white()) - .active(|this| this.opacity(0.8)) - .px_3() - .py_1() - .on_click(cx.listener(move |this, _, _, cx| on_click(this, cx))) - } -} - -const FONT_FAMILIES: [&str; 5] = [ - ".ZedMono", - ".SystemUIFont", - "Menlo", - "Monaco", - "Courier New", -]; - -impl Render for TextExample { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let tcx = cx.text_context(); - let colors = cx.default_colors().clone(); - - let type_scale = tcx.type_scale; - - let step_down_2 = 1.0 / (type_scale * type_scale); - let step_down_1 = 1.0 / type_scale; - let base = 1.0; - let step_up_1 = base * type_scale; - let step_up_2 = step_up_1 * type_scale; - let step_up_3 = step_up_2 * type_scale; - let step_up_4 = step_up_3 * type_scale; - let step_up_5 = step_up_4 * type_scale; - let step_up_6 = step_up_5 * type_scale; - - div() - .font_family(self.font_family.clone()) - .size_full() - .child( - div() - .bg(gpui::white()) - .border_b_1() - .border_color(gpui::black()) - .p_3() - .flex() - .child(Self::button(&self.font_family, cx, |this, cx| { - let new_family = FONT_FAMILIES - .iter() - .position(|f| *f == this.font_family.as_str()) - .map(|idx| FONT_FAMILIES[(idx + 1) % FONT_FAMILIES.len()]) - .unwrap_or(FONT_FAMILIES[0]); - - this.font_family = SharedString::new(new_family); - cx.notify(); - })), - ) - .child( - div() - .id("text-example") - .overflow_y_scroll() - .overflow_x_hidden() - .bg(rgb(0xffffff)) - .size_full() - .child(div().child(CharacterGrid::new().scale(base))) - .child( - div() - .child(Specimen::new(self.next_id()).scale(step_down_2)) - .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) - .child(Specimen::new(self.next_id()).scale(step_down_1)) - .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) - .child(Specimen::new(self.next_id()).scale(base)) - .child(Specimen::new(self.next_id()).scale(base).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_1)) - .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_2)) - .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_3)) - .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_4)) - .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_5)) - .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_6)) - .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), - ), - ) - .child(div().w(px(240.)).h_full().bg(colors.container)) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - cx.set_menus(vec![Menu { - name: "GPUI Typography".into(), - disabled: false, - items: vec![], - }]); - - let fonts = [include_bytes!( - "../../../assets/fonts/lilex/Lilex-Regular.ttf" - )] - .iter() - .map(|b| Cow::Borrowed(&b[..])) - .collect(); - - _ = cx.text_system().add_fonts(fonts); - - cx.init_colors(); - cx.set_global(GlobalTextContext(Arc::new(TextContext::default()))); - - let window = cx - .open_window( - WindowOptions { - titlebar: Some(TitlebarOptions { - title: Some("GPUI Typography".into()), - ..Default::default() - }), - window_bounds: Some(WindowBounds::Windowed(bounds( - point(px(0.0), px(0.0)), - size(px(920.), px(720.)), - ))), - ..Default::default() - }, - |_window, cx| { - cx.new(|_cx| TextExample { - next_id: 0, - font_family: ".ZedMono".into(), - }) - }, - ) - .unwrap(); - - window - .update(cx, |_view, _window, cx| { - cx.activate(true); - }) - .unwrap(); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/text_layout.rs b/crates/gpui_pre/examples/text_layout.rs deleted file mode 100644 index 07f560ab..00000000 --- a/crates/gpui_pre/examples/text_layout.rs +++ /dev/null @@ -1,117 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, FontStyle, FontWeight, StyledText, Window, WindowBounds, WindowOptions, - div, prelude::*, px, size, -}; -use gpui_platform::application; - -struct HelloWorld {} - -impl Render for HelloWorld { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .bg(gpui::white()) - .flex() - .flex_col() - .gap_2() - .p_4() - .size_full() - .child(div().child("Text left")) - .child(div().text_center().child("Text center")) - .child(div().text_right().child("Text right")) - .child(div().text_decoration_1().child("Text left (underline)")) - .child( - div() - .text_center() - .text_decoration_1() - .child("Text center (underline)"), - ) - .child( - div() - .text_right() - .text_decoration_1() - .child("Text right (underline)"), - ) - .child(div().line_through().child("Text left (line_through)")) - .child( - div() - .text_center() - .line_through() - .child("Text center (line_through)"), - ) - .child( - div() - .text_right() - .line_through() - .child("Text right (line_through)"), - ) - .child( - div() - .flex() - .gap_2() - .justify_between() - .child( - div() - .w(px(400.)) - .border_1() - .border_color(gpui::blue()) - .p_1() - .whitespace_nowrap() - .overflow_hidden() - .text_center() - .child("A long non-wrapping text align center"), - ) - .child( - div() - .w_32() - .border_1() - .border_color(gpui::blue()) - .p_1() - .whitespace_nowrap() - .overflow_hidden() - .text_right() - .child("100%"), - ), - ) - .child(div().flex().gap_2().justify_between().child( - StyledText::new("ABCD").with_highlights([ - (0..1, FontWeight::EXTRA_BOLD.into()), - (2..3, FontStyle::Italic.into()), - ]), - )) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(800.0), px(600.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| cx.new(|_| HelloWorld {}), - ) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/text_wrapper.rs b/crates/gpui_pre/examples/text_wrapper.rs deleted file mode 100644 index 51e58fc7..00000000 --- a/crates/gpui_pre/examples/text_wrapper.rs +++ /dev/null @@ -1,144 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, TextOverflow, Window, WindowBounds, WindowOptions, div, prelude::*, px, - size, -}; -use gpui_platform::application; - -struct HelloWorld {} - -impl Render for HelloWorld { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let text = "The longest word 你好世界这段是中文,こんにちはこの段落は日本語です in any of the major \ - English language dictionaries is pneumonoultramicroscopicsilicovolcanoconiosis, a word that \ - refers to a lung disease contracted from the inhalation of very fine silica particles, \ - a url https://github.com/zed-industries/zed/pull/35724?query=foo&bar=2, \ - specifically from a volcano; medically, it is the same as silicosis."; - div() - .id("page") - .size_full() - .flex() - .flex_col() - .p_2() - .gap_2() - .bg(gpui::white()) - .child( - div() - .flex() - .flex_row() - .flex_shrink_0() - .gap_2() - .child( - div() - .flex() - .border_1() - .border_color(gpui::red()) - .text_ellipsis() - .child("longer text in flex 1"), - ) - .child( - div() - .flex() - .border_1() - .border_color(gpui::red()) - .text_ellipsis() - .child("short flex"), - ) - .child( - div() - .overflow_hidden() - .border_1() - .border_color(gpui::red()) - .text_ellipsis() - .w_full() - .child("A short text in normal div"), - ), - ) - .child( - div() - .flex_shrink_0() - .text_xl() - .truncate() - .border_1() - .border_color(gpui::blue()) - .child("ELLIPSIS: ".to_owned() + text), - ) - .child( - div() - .flex_shrink_0() - .text_xl() - .overflow_hidden() - .text_ellipsis() - .line_clamp(2) - .border_1() - .border_color(gpui::blue()) - .child("ELLIPSIS 2 lines: ".to_owned() + text), - ) - .child( - div() - .flex_shrink_0() - .text_xl() - .overflow_hidden() - .text_overflow(TextOverflow::Truncate("".into())) - .border_1() - .border_color(gpui::green()) - .child("TRUNCATE: ".to_owned() + text), - ) - .child( - div() - .flex_shrink_0() - .text_xl() - .overflow_hidden() - .text_overflow(TextOverflow::Truncate("".into())) - .line_clamp(3) - .border_1() - .border_color(gpui::green()) - .child("TRUNCATE 3 lines: ".to_owned() + text), - ) - .child( - div() - .flex_shrink_0() - .text_xl() - .whitespace_nowrap() - .overflow_hidden() - .border_1() - .border_color(gpui::black()) - .child("NOWRAP: ".to_owned() + text), - ) - .child(div().text_xl().w_full().child(text)) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(800.0), px(600.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| cx.new(|_| HelloWorld {}), - ) - .unwrap(); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/tree.rs b/crates/gpui_pre/examples/tree.rs deleted file mode 100644 index 891be479..00000000 --- a/crates/gpui_pre/examples/tree.rs +++ /dev/null @@ -1,64 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -//! Renders a div with deep children hierarchy. This example is useful to exemplify that Zed can -//! handle deep hierarchies (even though it cannot just yet!). -#[path = "example_support/fonts.rs"] -mod example_support; - -use std::sync::LazyLock; - -use gpui::{App, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px, size}; -use gpui_platform::application; - -struct Tree {} - -static DEPTH: LazyLock = LazyLock::new(|| { - std::env::var("GPUI_TREE_DEPTH") - .ok() - .and_then(|depth| depth.parse().ok()) - .unwrap_or_else(|| 50) -}); - -impl Render for Tree { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - let mut depth = *DEPTH; - static COLORS: [gpui::Hsla; 4] = [gpui::red(), gpui::blue(), gpui::green(), gpui::yellow()]; - let mut colors = COLORS.iter().cycle().copied(); - let mut next_div = || div().p_0p5().bg(colors.next().unwrap()); - let mut innermost_node = next_div(); - while depth > 0 { - innermost_node = next_div().child(innermost_node); - depth -= 1; - } - innermost_node - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(300.0), px(300.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| cx.new(|_| Tree {}), - ) - .unwrap(); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/uniform_list.rs b/crates/gpui_pre/examples/uniform_list.rs deleted file mode 100644 index 4a2c4788..00000000 --- a/crates/gpui_pre/examples/uniform_list.rs +++ /dev/null @@ -1,71 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px, rgb, size, - uniform_list, -}; -use gpui_platform::application; - -struct UniformListExample {} - -impl Render for UniformListExample { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - div().size_full().bg(rgb(0xffffff)).child( - uniform_list( - "entries", - 50, - cx.processor(|_this, range, _window, _cx| { - let mut items = Vec::new(); - for ix in range { - let item = ix + 1; - - items.push( - div() - .id(ix) - .px_2() - .cursor_pointer() - .on_click(move |_event, _window, _cx| { - println!("clicked Item {item:?}"); - }) - .child(format!("Item {item}")), - ); - } - items - }), - ) - .h_full(), - ) - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(300.0), px(300.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| cx.new(|_| UniformListExample {}), - ) - .unwrap(); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/view_example/example_editor.rs b/crates/gpui_pre/examples/view_example/example_editor.rs deleted file mode 100644 index 2064d7ca..00000000 --- a/crates/gpui_pre/examples/view_example/example_editor.rs +++ /dev/null @@ -1,549 +0,0 @@ -//! `Editor` — the workhorse entity. It owns the cursor, blink, focus, keyboard -//! handling, and the specialized text-shaping renderer. The *text itself* lives -//! in a shared `Entity` it's handed at construction, so the value is -//! readable/writable from outside while the editing machinery stays in here. -//! -//! This is the piece that proves the point: a text input is genuinely -//! complicated, and `View` lets all of that complexity live in one entity that -//! anything can embed. - -use std::ops::Range; -use std::time::Duration; - -use gpui::{ - App, Bounds, Context, ElementInputHandler, Entity, EntityInputHandler, FocusHandle, Focusable, - InteractiveElement, LayoutId, PaintQuad, Pixels, ShapedLine, SharedString, Subscription, Task, - TextRun, UTF16Selection, Window, fill, hsla, point, prelude::*, px, relative, size, -}; -use unicode_segmentation::*; - -use crate::{Backspace, Delete, End, Home, Left, Right}; - -pub struct Editor { - pub value: Entity, - pub focus_handle: FocusHandle, - pub cursor: usize, - pub cursor_visible: bool, - _blink_task: Task<()>, - _subscriptions: Vec, -} - -impl Editor { - /// An editor that owns its own string internally, seeded with `text`. - /// Nothing to allocate or wire up at the call site. - pub fn new(text: impl Into, window: &mut Window, cx: &mut Context) -> Self { - let value = cx.new(|_| text.into()); - Self::over(value, window, cx) - } - - /// An editor over a string *you* own, so the value is shared in and out. - pub fn over(value: Entity, window: &mut Window, cx: &mut Context) -> Self { - let focus_handle = cx.focus_handle(); - - let focus_sub = cx.on_focus(&focus_handle, window, |this, _window, cx| { - this.start_blink(cx); - }); - let blur_sub = cx.on_blur(&focus_handle, window, |this, _window, cx| { - this.stop_blink(cx); - }); - - // The value is shared: anything can write it while we hold a cursor into - // it. Observe it so external writes (a) clamp the cursor back onto a char - // boundary before the next IME round-trip can slice out of bounds, and - // (b) notify us, so an `editor.cached(..)` subtree re-renders — the cache - // is keyed on *our* notify, not the value's. - let value_sub = cx.observe(&value, |this, value, cx| { - let content = value.read(cx); - let mut cursor = this.cursor.min(content.len()); - while cursor > 0 && !content.is_char_boundary(cursor) { - cursor -= 1; - } - this.cursor = cursor; - cx.notify(); - }); - - Self { - value, - focus_handle, - cursor: 0, - cursor_visible: false, - _blink_task: Task::ready(()), - _subscriptions: vec![focus_sub, blur_sub, value_sub], - } - } - - /// The current text. Read this from anywhere to get the value out. - pub fn text(&self, cx: &App) -> String { - self.value.read(cx).clone() - } - - fn start_blink(&mut self, cx: &mut Context) { - self.cursor_visible = true; - self._blink_task = Self::spawn_blink_task(cx); - } - - fn stop_blink(&mut self, cx: &mut Context) { - self.cursor_visible = false; - self._blink_task = Task::ready(()); - cx.notify(); - } - - fn spawn_blink_task(cx: &mut Context) -> Task<()> { - cx.spawn(async move |this, cx| { - loop { - cx.background_executor() - .timer(Duration::from_millis(500)) - .await; - let result = this.update(cx, |editor, cx| { - editor.cursor_visible = !editor.cursor_visible; - cx.notify(); - }); - if result.is_err() { - break; - } - } - }) - } - - fn reset_blink(&mut self, cx: &mut Context) { - self.cursor_visible = true; - self._blink_task = Self::spawn_blink_task(cx); - } - - pub fn left(&mut self, _: &Left, _: &mut Window, cx: &mut Context) { - let content = self.text(cx); - if self.cursor > 0 { - self.cursor = previous_boundary(&content, self.cursor); - } - self.reset_blink(cx); - cx.notify(); - } - - pub fn right(&mut self, _: &Right, _: &mut Window, cx: &mut Context) { - let content = self.text(cx); - if self.cursor < content.len() { - self.cursor = next_boundary(&content, self.cursor); - } - self.reset_blink(cx); - cx.notify(); - } - - pub fn home(&mut self, _: &Home, _: &mut Window, cx: &mut Context) { - self.cursor = 0; - self.reset_blink(cx); - cx.notify(); - } - - pub fn end(&mut self, _: &End, _: &mut Window, cx: &mut Context) { - self.cursor = self.text(cx).len(); - self.reset_blink(cx); - cx.notify(); - } - - pub fn backspace(&mut self, _: &Backspace, _: &mut Window, cx: &mut Context) { - let content = self.text(cx); - if self.cursor > 0 { - let prev = previous_boundary(&content, self.cursor); - let cursor = self.cursor; - self.value.update(cx, |s, cx| { - s.drain(prev..cursor); - cx.notify(); - }); - self.cursor = prev; - } - self.reset_blink(cx); - cx.notify(); - } - - pub fn delete(&mut self, _: &Delete, _: &mut Window, cx: &mut Context) { - let content = self.text(cx); - if self.cursor < content.len() { - let next = next_boundary(&content, self.cursor); - let cursor = self.cursor; - self.value.update(cx, |s, cx| { - s.drain(cursor..next); - cx.notify(); - }); - } - self.reset_blink(cx); - cx.notify(); - } - - pub fn insert_newline(&mut self, cx: &mut Context) { - let cursor = self.cursor; - self.value.update(cx, |s, cx| { - s.insert(cursor, '\n'); - cx.notify(); - }); - self.cursor += 1; - self.reset_blink(cx); - cx.notify(); - } -} - -fn previous_boundary(content: &str, offset: usize) -> usize { - content - .grapheme_indices(true) - .rev() - .find_map(|(idx, _)| (idx < offset).then_some(idx)) - .unwrap_or(0) -} - -fn next_boundary(content: &str, offset: usize) -> usize { - content - .grapheme_indices(true) - .find_map(|(idx, _)| (idx > offset).then_some(idx)) - .unwrap_or(content.len()) -} - -fn offset_from_utf16(content: &str, offset: usize) -> usize { - let mut utf8_offset = 0; - let mut utf16_count = 0; - for ch in content.chars() { - if utf16_count >= offset { - break; - } - utf16_count += ch.len_utf16(); - utf8_offset += ch.len_utf8(); - } - utf8_offset -} - -fn offset_to_utf16(content: &str, offset: usize) -> usize { - let mut utf16_offset = 0; - let mut utf8_count = 0; - for ch in content.chars() { - if utf8_count >= offset { - break; - } - utf8_count += ch.len_utf8(); - utf16_offset += ch.len_utf16(); - } - utf16_offset -} - -fn range_to_utf16(content: &str, range: &Range) -> Range { - offset_to_utf16(content, range.start)..offset_to_utf16(content, range.end) -} - -fn range_from_utf16(content: &str, range_utf16: &Range) -> Range { - offset_from_utf16(content, range_utf16.start)..offset_from_utf16(content, range_utf16.end) -} - -impl Focusable for Editor { - fn focus_handle(&self, _cx: &App) -> FocusHandle { - self.focus_handle.clone() - } -} - -impl EntityInputHandler for Editor { - fn text_for_range( - &mut self, - range_utf16: Range, - actual_range: &mut Option>, - _window: &mut Window, - cx: &mut Context, - ) -> Option { - let content = self.text(cx); - let range = range_from_utf16(&content, &range_utf16); - actual_range.replace(range_to_utf16(&content, &range)); - Some(content[range].to_string()) - } - - fn selected_text_range( - &mut self, - _ignore_disabled_input: bool, - _window: &mut Window, - cx: &mut Context, - ) -> Option { - let content = self.text(cx); - let utf16_cursor = offset_to_utf16(&content, self.cursor); - Some(UTF16Selection { - range: utf16_cursor..utf16_cursor, - reversed: false, - }) - } - - fn marked_text_range( - &self, - _window: &mut Window, - _cx: &mut Context, - ) -> Option> { - None - } - - fn unmark_text(&mut self, _window: &mut Window, _cx: &mut Context) {} - - fn replace_text_in_range( - &mut self, - range_utf16: Option>, - new_text: &str, - _window: &mut Window, - cx: &mut Context, - ) { - let content = self.text(cx); - let range = range_utf16 - .as_ref() - .map(|r| range_from_utf16(&content, r)) - .unwrap_or(self.cursor..self.cursor); - - let new_content = content[..range.start].to_owned() + new_text + &content[range.end..]; - self.cursor = range.start + new_text.len(); - self.value.update(cx, |s, cx| { - *s = new_content; - cx.notify(); - }); - self.reset_blink(cx); - cx.notify(); - } - - fn replace_and_mark_text_in_range( - &mut self, - range_utf16: Option>, - new_text: &str, - _new_selected_range_utf16: Option>, - window: &mut Window, - cx: &mut Context, - ) { - self.replace_text_in_range(range_utf16, new_text, window, cx); - } - - fn bounds_for_range( - &mut self, - _range_utf16: Range, - _bounds: Bounds, - _window: &mut Window, - _cx: &mut Context, - ) -> Option> { - None - } - - fn character_index_for_point( - &mut self, - _point: gpui::Point, - _window: &mut Window, - _cx: &mut Context, - ) -> Option { - None - } -} - -impl gpui::Render for Editor { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - EditorText { - editor: cx.entity(), - } - } -} - -// --------------------------------------------------------------------------- -// EditorText — the specialized renderer: shapes the text and paints the cursor. -// --------------------------------------------------------------------------- - -struct EditorText { - editor: Entity, -} - -struct EditorTextPrepaint { - lines: Vec, - cursor: Option, -} - -impl IntoElement for EditorText { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -impl Element for EditorText { - type RequestLayoutState = (); - type PrepaintState = EditorTextPrepaint; - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _id: Option<&gpui::GlobalElementId>, - _inspector_id: Option<&gpui::InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - let editor = self.editor.read(cx); - let content = editor.value.read(cx); - let line_count = content.split('\n').count().max(1); - let line_height = window.line_height(); - let mut style = gpui::Style::default(); - style.size.width = relative(1.).into(); - style.size.height = (line_height * line_count as f32).into(); - (window.request_layout(style, [], cx), ()) - } - - fn prepaint( - &mut self, - _id: Option<&gpui::GlobalElementId>, - _inspector_id: Option<&gpui::InspectorElementId>, - bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Self::PrepaintState { - let editor = self.editor.read(cx); - let content = editor.value.read(cx).clone(); - let cursor_offset = editor.cursor; - let cursor_visible = editor.cursor_visible; - let is_focused = editor.focus_handle.is_focused(window); - - let style = window.text_style(); - let text_color = style.color; - let font_size = style.font_size.to_pixels(window.rem_size()); - let line_height = window.line_height(); - - let is_placeholder = content.is_empty(); - - let lines: Vec = if is_placeholder { - let placeholder: SharedString = "Type here...".into(); - let run = TextRun { - len: placeholder.len(), - font: style.font(), - color: hsla(0., 0., 0.5, 0.5), - background_color: None, - underline: None, - strikethrough: None, - }; - vec![ - window - .text_system() - .shape_line(placeholder, font_size, &[run], None), - ] - } else { - content - .split('\n') - .map(|line_str| { - let text: SharedString = SharedString::from(line_str.to_string()); - let run = TextRun { - len: text.len(), - font: style.font(), - color: text_color, - background_color: None, - underline: None, - strikethrough: None, - }; - window - .text_system() - .shape_line(text, font_size, &[run], None) - }) - .collect() - }; - - let cursor = if is_focused && cursor_visible && !is_placeholder { - let (cursor_line, offset_in_line) = cursor_line_and_offset(&content, cursor_offset); - let cursor_line = cursor_line.min(lines.len().saturating_sub(1)); - let cursor_x = lines[cursor_line].x_for_index(offset_in_line); - Some(fill( - Bounds::new( - point( - bounds.left() + cursor_x, - bounds.top() + line_height * cursor_line as f32, - ), - size(px(1.5), line_height), - ), - text_color, - )) - } else if is_focused && cursor_visible && is_placeholder { - Some(fill( - Bounds::new( - point(bounds.left(), bounds.top()), - size(px(1.5), line_height), - ), - text_color, - )) - } else { - None - }; - - EditorTextPrepaint { lines, cursor } - } - - fn paint( - &mut self, - _id: Option<&gpui::GlobalElementId>, - _inspector_id: Option<&gpui::InspectorElementId>, - bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - prepaint: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - let focus_handle = self.editor.read(cx).focus_handle.clone(); - window.handle_input( - &focus_handle, - ElementInputHandler::new(bounds, self.editor.clone()), - cx, - ); - - let line_height = window.line_height(); - for (i, line) in prepaint.lines.iter().enumerate() { - let origin = point(bounds.left(), bounds.top() + line_height * i as f32); - line.paint(origin, line_height, gpui::TextAlign::Left, None, window, cx) - .unwrap(); - } - - if let Some(cursor) = prepaint.cursor.take() { - window.paint_quad(cursor); - } - } -} - -fn cursor_line_and_offset(content: &str, cursor: usize) -> (usize, usize) { - let mut line_index = 0; - let mut line_start = 0; - for (i, ch) in content.char_indices() { - if i >= cursor { - break; - } - if ch == '\n' { - line_index += 1; - line_start = i + 1; - } - } - (line_index, cursor - line_start) -} - -pub fn standard_actions(editor: Entity) -> impl FnOnce(E) -> E { - move |element| { - element - .on_action({ - let editor = editor.clone(); - move |a: &Left, window, cx| editor.update(cx, |e, cx| e.left(a, window, cx)) - }) - .on_action({ - let editor = editor.clone(); - move |a: &Right, window, cx| editor.update(cx, |e, cx| e.right(a, window, cx)) - }) - .on_action({ - let editor = editor.clone(); - move |a: &Home, window, cx| editor.update(cx, |e, cx| e.home(a, window, cx)) - }) - .on_action({ - let editor = editor.clone(); - move |a: &End, window, cx| editor.update(cx, |e, cx| e.end(a, window, cx)) - }) - .on_action({ - let editor = editor.clone(); - move |a: &Backspace, window, cx| { - editor.update(cx, |e, cx| e.backspace(a, window, cx)) - } - }) - .on_action(move |a: &Delete, window, cx| { - editor.update(cx, |e, cx| e.delete(a, window, cx)) - }) - } -} diff --git a/crates/gpui_pre/examples/view_example/example_input.rs b/crates/gpui_pre/examples/view_example/example_input.rs deleted file mode 100644 index 25d74013..00000000 --- a/crates/gpui_pre/examples/view_example/example_input.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! `Input` — a single-line text input. The shaping layer over `Editor`. -//! -//! Construct it two ways, depending on how much state you want to own: -//! * `Input::new(value: Entity)` — you hold just the string; the input -//! allocates the `Editor` internally via `use_state`. Value readable, cursor hidden. -//! * `Input::editor(editor: Entity)` — you hold the editor; cursor/selection -//! are now yours to read and drive too. -//! -//! Either way the chrome is identical. Because the string (or editor) is the -//! input's *identity*, the internal `use_state(Editor)` is collision-safe across -//! any number of inputs. - -use gpui::{ - App, BoxShadow, CursorStyle, Entity, EntityId, Hsla, IntoElement, Pixels, StyleRefinement, - Window, div, hsla, point, prelude::*, px, white, -}; - -use crate::example_editor::{Editor, standard_actions}; - -enum Source { - Value(Entity), - Editor(Entity), -} - -#[derive(IntoElement)] -pub struct Input { - source: Source, - width: Option, - color: Option, -} - -impl Input { - /// Backed by a bare string; the editor is allocated internally. - pub fn new(value: Entity) -> Self { - Self { - source: Source::Value(value), - width: None, - color: None, - } - } - - /// Backed by an editor you own (so you can read/drive its cursor). - pub fn editor(editor: Entity) -> Self { - Self { - source: Source::Editor(editor), - width: None, - color: None, - } - } - - pub fn width(mut self, width: Pixels) -> Self { - self.width = Some(width); - self - } - - pub fn color(mut self, color: Hsla) -> Self { - self.color = Some(color); - self - } -} - -impl gpui::View for Input { - fn entity_id(&self) -> Option { - Some(match &self.source { - Source::Value(value) => value.entity_id(), - Source::Editor(editor) => editor.entity_id(), - }) - } - - fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { - // Get the editor: use the one we were handed, or allocate it under our - // own (string-derived) identity so it persists and never collides. - let editor = match self.source { - Source::Value(value) => { - window.use_state(cx, move |window, cx| Editor::over(value, window, cx)) - } - Source::Editor(editor) => editor, - }; - - let focus_handle = editor.read(cx).focus_handle.clone(); - let is_focused = focus_handle.is_focused(window); - let text_color = self.color.unwrap_or(hsla(0., 0., 0.1, 1.)); - let box_width = self.width.unwrap_or(px(300.)); - - let border = if is_focused { - hsla(220. / 360., 0.8, 0.5, 1.) - } else { - hsla(0., 0., 0.75, 1.) - }; - - div() - .id("input") - .key_context("TextInput") - .track_focus(&focus_handle) - .cursor(CursorStyle::IBeam) - .map(standard_actions(editor.clone())) - .w(box_width) - .h(px(36.)) - .px(px(8.)) - .bg(white()) - .border_1() - .border_color(border) - .when(is_focused, |this| { - this.shadow(vec![BoxShadow { - color: hsla(220. / 360., 0.8, 0.5, 0.3), - offset: point(px(0.), px(0.)), - blur_radius: px(4.), - spread_radius: px(1.), - inset: false, - }]) - }) - .rounded(px(4.)) - .overflow_hidden() - .flex() - .items_center() - .line_height(px(20.)) - .text_size(px(14.)) - .text_color(text_color) - .child(editor.cached(StyleRefinement::default().size_full())) - } -} diff --git a/crates/gpui_pre/examples/view_example/example_tests.rs b/crates/gpui_pre/examples/view_example/example_tests.rs deleted file mode 100644 index a3edae8c..00000000 --- a/crates/gpui_pre/examples/view_example/example_tests.rs +++ /dev/null @@ -1,131 +0,0 @@ -//! Tests for the input composition. Require the `test-support` feature: -//! -//! ```sh -//! cargo test -p gpui --example view_example --features test-support -//! ``` - -#[cfg(test)] -mod tests { - use gpui::{Context, Entity, KeyBinding, TestAppContext, Window, prelude::*}; - - use crate::example_editor::Editor; - use crate::example_input::Input; - use crate::{Backspace, Delete, End, Home, Left, Right}; - - /// Two inputs, each backed by an editor we own (so the test can focus and - /// read them). Proves data flows through the shared `String` and that - /// sibling inputs stay isolated. - struct Harness { - a: Entity, - b: Entity, - } - - impl Render for Harness { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - gpui::div() - .child(Input::editor(self.a.clone())) - .child(Input::editor(self.b.clone())) - } - } - - fn bind_keys(cx: &mut TestAppContext) { - cx.update(|cx| { - cx.bind_keys([ - KeyBinding::new("backspace", Backspace, None), - KeyBinding::new("delete", Delete, None), - KeyBinding::new("left", Left, None), - KeyBinding::new("right", Right, None), - KeyBinding::new("home", Home, None), - KeyBinding::new("end", End, None), - ]); - }); - } - - fn setup( - cx: &mut TestAppContext, - ) -> ( - Entity, - Entity, - Entity, - &mut gpui::VisualTestContext, - ) { - bind_keys(cx); - - let (harness, cx) = cx.add_window_view(|window, cx| { - let a_value = cx.new(|_| String::new()); - let b_value = cx.new(|_| String::new()); - let a = cx.new(|cx| Editor::over(a_value, window, cx)); - let b = cx.new(|cx| Editor::over(b_value, window, cx)); - Harness { a, b } - }); - - let a = cx.read_entity(&harness, |h, _| h.a.clone()); - let b = cx.read_entity(&harness, |h, _| h.b.clone()); - let a_value = cx.read_entity(&a, |e, _| e.value.clone()); - let b_value = cx.read_entity(&b, |e, _| e.value.clone()); - - // Focus the first input's editor. - cx.update(|window, cx| { - let focus_handle = a.read(cx).focus_handle.clone(); - window.focus(&focus_handle, cx); - }); - - (a, a_value, b_value, cx) - } - - #[gpui::test] - fn typing_updates_the_shared_string(cx: &mut TestAppContext) { - let (editor, a_value, _b_value, cx) = setup(cx); - - cx.simulate_input("hello"); - - cx.read_entity(&a_value, |value, _| assert_eq!(value, "hello")); - cx.read_entity(&editor, |editor, _| assert_eq!(editor.cursor, 5)); - } - - #[gpui::test] - fn sibling_inputs_are_isolated(cx: &mut TestAppContext) { - let (_editor, a_value, b_value, cx) = setup(cx); - - cx.simulate_input("x"); - - cx.read_entity(&a_value, |value, _| assert_eq!(value, "x")); - cx.read_entity(&b_value, |value, _| { - assert_eq!(value, "", "typing in input A must not touch input B") - }); - } - - #[gpui::test] - fn external_writes_clamp_the_cursor(cx: &mut TestAppContext) { - let (editor, a_value, _b_value, cx) = setup(cx); - - cx.simulate_input("hello"); - cx.read_entity(&editor, |editor, _| assert_eq!(editor.cursor, 5)); - - // Write the shared value from outside the editor. The old cursor (5) - // now points into the middle of a multi-byte character; the editor's - // observation must clamp it back onto a boundary. - cx.update(|_, cx| { - a_value.update(cx, |value, cx| { - *value = "日本".to_string(); - cx.notify(); - }) - }); - - cx.read_entity(&a_value, |value, _| assert_eq!(value, "日本")); - cx.read_entity(&editor, |editor, _| { - assert_eq!(editor.cursor, 3, "cursor must clamp to a char boundary"); - }); - } - - #[gpui::test] - fn arrows_move_the_cursor(cx: &mut TestAppContext) { - let (editor, _a_value, _b_value, cx) = setup(cx); - - cx.simulate_input("abc"); - cx.read_entity(&editor, |editor, _| assert_eq!(editor.cursor, 3)); - - cx.simulate_keystrokes("left left"); - cx.read_entity(&editor, |editor, _| assert_eq!(editor.cursor, 1)); - } -} diff --git a/crates/gpui_pre/examples/view_example/example_text_area.rs b/crates/gpui_pre/examples/view_example/example_text_area.rs deleted file mode 100644 index 07640b93..00000000 --- a/crates/gpui_pre/examples/view_example/example_text_area.rs +++ /dev/null @@ -1,118 +0,0 @@ -//! `TextArea` — a multi-line text box. Same `Editor` workhorse, taller chrome, -//! and `Enter` inserts a newline instead of being ignored. Constructible from a -//! string or an editor, exactly like [`Input`](crate::example_input::Input). - -use gpui::{ - App, BoxShadow, CursorStyle, Entity, EntityId, Hsla, IntoElement, StyleRefinement, Window, div, - hsla, point, prelude::*, px, white, -}; - -use crate::Enter; -use crate::example_editor::{Editor, standard_actions}; - -enum Source { - Value(Entity), - Editor(Entity), -} - -#[derive(IntoElement)] -pub struct TextArea { - source: Source, - rows: usize, - color: Option, -} - -impl TextArea { - pub fn new(value: Entity, rows: usize) -> Self { - Self { - source: Source::Value(value), - rows, - color: None, - } - } - - pub fn editor(editor: Entity, rows: usize) -> Self { - Self { - source: Source::Editor(editor), - rows, - color: None, - } - } - - pub fn color(mut self, color: Hsla) -> Self { - self.color = Some(color); - self - } -} - -impl gpui::View for TextArea { - fn entity_id(&self) -> Option { - Some(match &self.source { - Source::Value(value) => value.entity_id(), - Source::Editor(editor) => editor.entity_id(), - }) - } - - fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { - let editor = match self.source { - Source::Value(value) => { - window.use_state(cx, move |window, cx| Editor::over(value, window, cx)) - } - Source::Editor(editor) => editor, - }; - - let focus_handle = editor.read(cx).focus_handle.clone(); - let is_focused = focus_handle.is_focused(window); - let text_color = self.color.unwrap_or(hsla(0., 0., 0.1, 1.)); - let row_height = px(20.); - let box_height = row_height * self.rows as f32 + px(16.); - - let border = if is_focused { - hsla(220. / 360., 0.8, 0.5, 1.) - } else { - hsla(0., 0., 0.75, 1.) - }; - - div() - .id("text-area") - .key_context("TextInput") - .track_focus(&focus_handle) - .cursor(CursorStyle::IBeam) - .map(standard_actions(editor.clone())) - // Enter is the one binding that differs from a single-line input. - .on_action({ - let editor = editor.clone(); - move |_: &Enter, _window, cx| editor.update(cx, |e, cx| e.insert_newline(cx)) - }) - .w(px(400.)) - .h(box_height) - .p(px(8.)) - .bg(white()) - .border_1() - .border_color(border) - .when(is_focused, |this| { - this.shadow(vec![BoxShadow { - color: hsla(220. / 360., 0.8, 0.5, 0.3), - offset: point(px(0.), px(0.)), - blur_radius: px(4.), - spread_radius: px(1.), - inset: false, - }]) - }) - .rounded(px(4.)) - .overflow_hidden() - .line_height(row_height) - .text_size(px(14.)) - .text_color(text_color) - // The cache style is computed from the `rows` prop: change `rows` and - // the editor's cached bounds change, busting its cache and re-laying - // out the text. (`Input` just uses `size_full()` — nothing to vary.) - .child( - editor.cached( - StyleRefinement::default() - .w_full() - .h(row_height * self.rows as f32), - ), - ) - } -} diff --git a/crates/gpui_pre/examples/view_example/view_example_main.rs b/crates/gpui_pre/examples/view_example/view_example_main.rs deleted file mode 100644 index d2e4991d..00000000 --- a/crates/gpui_pre/examples/view_example/view_example_main.rs +++ /dev/null @@ -1,179 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -//! View example — composing a text input from the `View` primitives. -//! -//! The whole point: a text input is deceptively complicated, and `View` makes it -//! easy to compose one. Three pieces, each shown in its own section: -//! -//! * `Editor` — the workhorse entity: cursor, blink, focus, keyboard, and a -//! specialized text renderer. All the hard parts live here. -//! * `String` — the data plane. `editor.text(cx)` / `value.read(cx)` get it out. -//! * `Input` / `TextArea` — the shaping layer. Each takes a `String` (and grows -//! the editor internally) OR an `Editor` (so you can read the cursor). -//! -//! Run: `cargo run -p gpui --example view_example` - -#[path = "../example_support/fonts.rs"] -mod example_support; - -mod example_editor; -mod example_input; -mod example_text_area; - -#[cfg(test)] -mod example_tests; - -use example_editor::Editor; -use example_input::Input; -use example_text_area::TextArea; - -use gpui::{ - App, Bounds, Context, Div, Entity, IntoElement, KeyBinding, Render, SharedString, Window, - WindowBounds, WindowOptions, actions, div, hsla, prelude::*, px, rgb, size, -}; -use gpui_platform::application; - -actions!( - view_example, - [Backspace, Delete, Left, Right, Home, End, Enter, Quit] -); - -/// A tiny stateless view that reads an editor's cursor and is composed *beside* -/// the thing editing it — two views over one entity, zero wiring. -#[derive(IntoElement)] -struct CursorReadout { - editor: Entity, -} - -impl CursorReadout { - fn new(editor: Entity) -> Self { - Self { editor } - } -} - -impl gpui::RenderOnce for CursorReadout { - fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { - let cursor = self.editor.read(cx).cursor; - div() - .text_sm() - .text_color(hsla(0., 0., 0.45, 1.)) - .child(SharedString::from(format!("cursor @ {cursor}"))) - } -} - -struct ViewExample; - -impl ViewExample { - fn new() -> Self { - Self - } -} - -impl Render for ViewExample { - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - // The data plane: plain strings, allocated at the top by the hook. - let name = window.use_state(cx, |_, _| String::new()); - let email = window.use_state(cx, |_, _| String::from("me@example.com")); - let bio = window.use_state(cx, |_, _| String::new()); - // Editors that own their own string internally — no extra wiring up top. - let notes = window.use_state(cx, |window, cx| Editor::new("multi\nline", window, cx)); - let owned = window.use_state(cx, |window, cx| Editor::new("editable", window, cx)); - - div() - .flex() - .flex_col() - .size_full() - .bg(rgb(0xf0f0f0)) - .p(px(24.)) - .gap(px(24.)) - .child( - section("Inputs — from a String (cursor stays internal)") - .child(Input::new(name).width(px(320.))) - .child( - Input::new(email) - .width(px(320.)) - .color(hsla(0., 0., 0.3, 1.)), - ), - ) - .child( - section("Input — from an Editor (read its cursor beside it)").child( - div() - .flex() - .items_center() - .gap(px(12.)) - .child(Input::editor(owned.clone()).width(px(320.))) - .child(CursorReadout::new(owned)), - ), - ) - .child( - section("Text areas — from a String, or from an Editor") - .child(TextArea::new(bio, 3)) - .child( - div() - .flex() - .items_start() - .gap(px(12.)) - .child(TextArea::editor(notes.clone(), 3).color(hsla( - 250. / 360., - 0.7, - 0.4, - 1., - ))) - .child(CursorReadout::new(notes)), - ), - ) - } -} - -/// A labeled vertical section. -fn section(title: &str) -> Div { - div().flex().flex_col().gap(px(8.)).child( - div() - .text_sm() - .text_color(hsla(0., 0., 0.3, 1.)) - .child(SharedString::from(title.to_string())), - ) -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(560.0), px(480.0)), cx); - cx.bind_keys([ - KeyBinding::new("backspace", Backspace, None), - KeyBinding::new("delete", Delete, None), - KeyBinding::new("left", Left, None), - KeyBinding::new("right", Right, None), - KeyBinding::new("home", Home, None), - KeyBinding::new("end", End, None), - KeyBinding::new("enter", Enter, None), - KeyBinding::new("cmd-q", Quit, None), - ]); - - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| cx.new(|_| ViewExample::new()), - ) - .unwrap(); - - cx.on_action(|_: &Quit, cx| cx.quit()); - cx.activate(true); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/window.rs b/crates/gpui_pre/examples/window.rs deleted file mode 100644 index 51dbe727..00000000 --- a/crates/gpui_pre/examples/window.rs +++ /dev/null @@ -1,355 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, KeyBinding, PromptButton, PromptLevel, Window, WindowBounds, WindowKind, - WindowOptions, actions, div, prelude::*, px, rgb, size, -}; -use gpui_platform::application; - -struct SubWindow { - custom_titlebar: bool, - is_dialog: bool, -} - -fn button(text: &str, on_click: impl Fn(&mut Window, &mut App) + 'static) -> impl IntoElement { - div() - .id(text.to_string()) - .flex_none() - .px_2() - .bg(rgb(0xf7f7f7)) - .active(|this| this.opacity(0.85)) - .border_1() - .border_color(rgb(0xe0e0e0)) - .rounded_sm() - .cursor_pointer() - .child(text.to_string()) - .on_click(move |_, window, cx| on_click(window, cx)) -} - -impl Render for SubWindow { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let window_bounds = - WindowBounds::Windowed(Bounds::centered(None, size(px(250.0), px(200.0)), cx)); - - div() - .flex() - .flex_col() - .bg(rgb(0xffffff)) - .size_full() - .gap_2() - .when(self.custom_titlebar, |cx| { - cx.child( - div() - .flex() - .h(px(32.)) - .px_4() - .bg(gpui::blue()) - .text_color(gpui::white()) - .w_full() - .child( - div() - .flex() - .items_center() - .justify_center() - .size_full() - .child("Custom Titlebar"), - ), - ) - }) - .child( - div() - .p_8() - .flex() - .flex_col() - .gap_2() - .child("SubWindow") - .when(self.is_dialog, |div| { - div.child(button("Open Nested Dialog", move |_, cx| { - cx.open_window( - WindowOptions { - window_bounds: Some(window_bounds), - kind: WindowKind::Dialog, - ..Default::default() - }, - |_, cx| { - cx.new(|_| SubWindow { - custom_titlebar: false, - is_dialog: true, - }) - }, - ) - .unwrap(); - })) - }) - .child(button("Close", |window, _| { - window.remove_window(); - })), - ) - } -} - -struct WindowDemo {} - -impl Render for WindowDemo { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let window_bounds = - WindowBounds::Windowed(Bounds::centered(None, size(px(300.0), px(300.0)), cx)); - - div() - .p_4() - .flex() - .flex_wrap() - .bg(rgb(0xffffff)) - .size_full() - .justify_center() - .content_center() - .gap_2() - .child(button("Normal", move |_, cx| { - cx.open_window( - WindowOptions { - window_bounds: Some(window_bounds), - ..Default::default() - }, - |_, cx| { - cx.new(|_| SubWindow { - custom_titlebar: false, - is_dialog: false, - }) - }, - ) - .unwrap(); - })) - .child(button("Popup", move |_, cx| { - cx.open_window( - WindowOptions { - window_bounds: Some(window_bounds), - kind: WindowKind::PopUp, - ..Default::default() - }, - |_, cx| { - cx.new(|_| SubWindow { - custom_titlebar: false, - is_dialog: false, - }) - }, - ) - .unwrap(); - })) - .child(button("Floating", move |_, cx| { - cx.open_window( - WindowOptions { - window_bounds: Some(window_bounds), - kind: WindowKind::Floating, - ..Default::default() - }, - |_, cx| { - cx.new(|_| SubWindow { - custom_titlebar: false, - is_dialog: false, - }) - }, - ) - .unwrap(); - })) - .child(button("Dialog", move |_, cx| { - cx.open_window( - WindowOptions { - window_bounds: Some(window_bounds), - kind: WindowKind::Dialog, - ..Default::default() - }, - |_, cx| { - cx.new(|_| SubWindow { - custom_titlebar: false, - is_dialog: true, - }) - }, - ) - .unwrap(); - })) - .child(button("Custom Titlebar", move |_, cx| { - cx.open_window( - WindowOptions { - titlebar: None, - window_bounds: Some(window_bounds), - ..Default::default() - }, - |_, cx| { - cx.new(|_| SubWindow { - custom_titlebar: true, - is_dialog: false, - }) - }, - ) - .unwrap(); - })) - .child(button("Invisible", move |_, cx| { - cx.open_window( - WindowOptions { - show: false, - window_bounds: Some(window_bounds), - ..Default::default() - }, - |_, cx| { - cx.new(|_| SubWindow { - custom_titlebar: false, - is_dialog: false, - }) - }, - ) - .unwrap(); - })) - .child(button("Unmovable", move |_, cx| { - cx.open_window( - WindowOptions { - is_movable: false, - titlebar: None, - window_bounds: Some(window_bounds), - ..Default::default() - }, - |_, cx| { - cx.new(|_| SubWindow { - custom_titlebar: false, - is_dialog: false, - }) - }, - ) - .unwrap(); - })) - .child(button("Unresizable", move |_, cx| { - cx.open_window( - WindowOptions { - is_resizable: false, - window_bounds: Some(window_bounds), - ..Default::default() - }, - |_, cx| { - cx.new(|_| SubWindow { - custom_titlebar: false, - is_dialog: false, - }) - }, - ) - .unwrap(); - })) - .child(button("Unminimizable", move |_, cx| { - cx.open_window( - WindowOptions { - is_minimizable: false, - window_bounds: Some(window_bounds), - ..Default::default() - }, - |_, cx| { - cx.new(|_| SubWindow { - custom_titlebar: false, - is_dialog: false, - }) - }, - ) - .unwrap(); - })) - .child(button("Hide Application", |window, cx| { - cx.hide(); - - // Restore the application after 3 seconds - window - .spawn(cx, async move |cx| { - cx.background_executor() - .timer(std::time::Duration::from_secs(3)) - .await; - cx.update(|_, cx| { - cx.activate(false); - }) - }) - .detach(); - })) - .child(button("Resize", |window, _| { - let content_size = window.bounds().size; - window.resize(size(content_size.height, content_size.width)); - })) - .child(button("Prompt", |window, cx| { - let answer = window.prompt( - PromptLevel::Info, - "Are you sure?", - None, - &["OK", "Cancel"], - cx, - ); - - cx.spawn(async move |_| { - if answer.await.unwrap() == 0 { - println!("You have clicked Ok"); - } else { - println!("You have clicked Cancel"); - } - }) - .detach(); - })) - .child(button("Prompt (non-English)", |window, cx| { - let answer = window.prompt( - PromptLevel::Info, - "Are you sure?", - None, - &[PromptButton::ok("确定"), PromptButton::cancel("取消")], - cx, - ); - - cx.spawn(async move |_| { - if answer.await.unwrap() == 0 { - println!("You have clicked Ok"); - } else { - println!("You have clicked Cancel"); - } - }) - .detach(); - })) - } -} - -actions!(window, [Quit]); - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(800.0), px(600.0)), cx); - - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |window, cx| { - cx.new(|cx| { - cx.observe_window_bounds(window, move |_, window, _| { - println!("Window bounds changed: {:?}", window.bounds()); - }) - .detach(); - - WindowDemo {} - }) - }, - ) - .unwrap(); - - cx.activate(true); - cx.on_action(|_: &Quit, cx| cx.quit()); - cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/window_movable.rs b/crates/gpui_pre/examples/window_movable.rs deleted file mode 100644 index b5fb2603..00000000 --- a/crates/gpui_pre/examples/window_movable.rs +++ /dev/null @@ -1,131 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, FocusHandle, Window, WindowBounds, WindowOptions, div, prelude::*, px, - rgb, size, -}; -use gpui::{SharedString, TitlebarOptions}; -use gpui_platform::application; - -struct ExampleWindow { - label: SharedString, - focus_handle: FocusHandle, -} - -impl Render for ExampleWindow { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .track_focus(&self.focus_handle) - .flex() - .flex_col() - .gap_3() - .bg(rgb(0x2e2e2e)) - .size_full() - .justify_center() - .items_center() - .p_8() - .text_lg() - .text_color(rgb(0xffffff)) - .child(self.label.clone()) - .child( - div() - .text_sm() - .text_color(rgb(0xb0b0b0)) - .child("Try to drag the titlebar, and check the Window menu."), - ) - } -} - -fn open_test_window( - cx: &mut App, - bounds: Bounds, - label: &str, - is_movable: bool, - appears_transparent: bool, - app_owns_titlebar_drag: bool, -) { - let label = SharedString::from(format!( - "{label}\nis_movable: {is_movable}\n\ - appears_transparent: {appears_transparent}\n\ - app_owns_titlebar_drag: {app_owns_titlebar_drag}" - )); - - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - is_movable, - app_owns_titlebar_drag, - titlebar: Some(TitlebarOptions { - title: Some(label.clone()), - appears_transparent, - ..Default::default() - }), - ..Default::default() - }, - |window, cx| { - cx.new(|cx| { - let focus_handle = cx.focus_handle(); - focus_handle.focus(window, cx); - ExampleWindow { - label, - focus_handle, - } - }) - }, - ) - .unwrap(); -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let window_size = size(px(420.), px(280.0)); - let base = Bounds::centered(None, window_size, cx); - - // (label, is_movable, appears_transparent, app_owns_titlebar_drag, col, row) - let windows = [ - ("Native titlebar, movable", true, false, false, 0.0, 0.0), - ( - "Native titlebar, NOT movable", - false, - false, - false, - 1.0, - 0.0, - ), - ("Custom titlebar, movable", true, true, false, 0.0, 1.0), - ("Custom titlebar, NOT movable", false, true, false, 1.0, 1.0), - ]; - - for (label, is_movable, appears_transparent, app_owns_titlebar_drag, col, row) in windows { - let mut bounds = base; - bounds.origin.x += window_size.width * col; - bounds.origin.y += window_size.height * row; - open_test_window( - cx, - bounds, - label, - is_movable, - appears_transparent, - app_owns_titlebar_drag, - ); - } - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/window_positioning.rs b/crates/gpui_pre/examples/window_positioning.rs deleted file mode 100644 index 22c9b359..00000000 --- a/crates/gpui_pre/examples/window_positioning.rs +++ /dev/null @@ -1,240 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, DisplayId, Hsla, Pixels, SharedString, Size, Window, - WindowBackgroundAppearance, WindowBounds, WindowKind, WindowOptions, div, point, prelude::*, - px, rgb, -}; -use gpui_platform::application; - -struct WindowContent { - text: SharedString, - bounds: Bounds, - bg: Hsla, -} - -impl Render for WindowContent { - fn render(&mut self, window: &mut Window, _: &mut Context) -> impl IntoElement { - let window_bounds = window.bounds(); - - div() - .flex() - .flex_col() - .bg(self.bg) - .size_full() - .items_center() - .text_color(rgb(0xffffff)) - .child(self.text.clone()) - .child( - div() - .flex() - .flex_col() - .text_sm() - .items_center() - .size_full() - .child(format!( - "origin: {}, {} size: {}, {}", - self.bounds.origin.x, - self.bounds.origin.y, - self.bounds.size.width, - self.bounds.size.height - )) - .child(format!( - "cx.bounds() origin: {}, {} size {}, {}", - window_bounds.origin.x, - window_bounds.origin.y, - window_bounds.size.width, - window_bounds.size.height - )), - ) - } -} - -fn build_window_options(display_id: DisplayId, bounds: Bounds) -> WindowOptions { - WindowOptions { - // Set the bounds of the window in screen coordinates - window_bounds: Some(WindowBounds::Windowed(bounds)), - // Specify the display_id to ensure the window is created on the correct screen - display_id: Some(display_id), - titlebar: None, - window_background: WindowBackgroundAppearance::Transparent, - focus: false, - show: true, - kind: WindowKind::PopUp, - is_movable: false, - app_id: None, - window_min_size: None, - window_decorations: None, - tabbing_identifier: None, - ..Default::default() - } -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - // Create several new windows, positioned in the top right corner of each screen - let size = Size { - width: px(350.), - height: px(75.), - }; - let margin_offset = px(150.); - - for screen in cx.displays() { - let bounds = Bounds { - origin: point(margin_offset, margin_offset), - size, - }; - - cx.open_window(build_window_options(screen.id(), bounds), |_, cx| { - cx.new(|_| WindowContent { - text: format!("Top Left {:?}", screen.id()).into(), - bg: gpui::red(), - bounds, - }) - }) - .unwrap(); - - let bounds = Bounds { - origin: screen.bounds().top_right() - - point(size.width + margin_offset, -margin_offset), - size, - }; - - cx.open_window(build_window_options(screen.id(), bounds), |_, cx| { - cx.new(|_| WindowContent { - text: format!("Top Right {:?}", screen.id()).into(), - bg: gpui::red(), - bounds, - }) - }) - .unwrap(); - - let bounds = Bounds { - origin: screen.bounds().bottom_left() - - point(-margin_offset, size.height + margin_offset), - size, - }; - - cx.open_window(build_window_options(screen.id(), bounds), |_, cx| { - cx.new(|_| WindowContent { - text: format!("Bottom Left {:?}", screen.id()).into(), - bg: gpui::blue(), - bounds, - }) - }) - .unwrap(); - - let bounds = Bounds { - origin: screen.bounds().bottom_right() - - point(size.width + margin_offset, size.height + margin_offset), - size, - }; - - cx.open_window(build_window_options(screen.id(), bounds), |_, cx| { - cx.new(|_| WindowContent { - text: format!("Bottom Right {:?}", screen.id()).into(), - bg: gpui::blue(), - bounds, - }) - }) - .unwrap(); - - let bounds = Bounds { - origin: point(screen.bounds().center().x - size.center().x, margin_offset), - size, - }; - - cx.open_window(build_window_options(screen.id(), bounds), |_, cx| { - cx.new(|_| WindowContent { - text: format!("Top Center {:?}", screen.id()).into(), - bg: gpui::black(), - bounds, - }) - }) - .unwrap(); - - let bounds = Bounds { - origin: point(margin_offset, screen.bounds().center().y - size.center().y), - size, - }; - - cx.open_window(build_window_options(screen.id(), bounds), |_, cx| { - cx.new(|_| WindowContent { - text: format!("Left Center {:?}", screen.id()).into(), - bg: gpui::black(), - bounds, - }) - }) - .unwrap(); - - let bounds = Bounds { - origin: point( - screen.bounds().center().x - size.center().x, - screen.bounds().center().y - size.center().y, - ), - size, - }; - - cx.open_window(build_window_options(screen.id(), bounds), |_, cx| { - cx.new(|_| WindowContent { - text: format!("Center {:?}", screen.id()).into(), - bg: gpui::black(), - bounds, - }) - }) - .unwrap(); - - let bounds = Bounds { - origin: point( - screen.bounds().size.width - size.width - margin_offset, - screen.bounds().center().y - size.center().y, - ), - size, - }; - - cx.open_window(build_window_options(screen.id(), bounds), |_, cx| { - cx.new(|_| WindowContent { - text: format!("Right Center {:?}", screen.id()).into(), - bg: gpui::black(), - bounds, - }) - }) - .unwrap(); - - let bounds = Bounds { - origin: point( - screen.bounds().center().x - size.center().x, - screen.bounds().size.height - size.height - margin_offset, - ), - size, - }; - - cx.open_window(build_window_options(screen.id(), bounds), |_, cx| { - cx.new(|_| WindowContent { - text: format!("Bottom Center {:?}", screen.id()).into(), - bg: gpui::black(), - bounds, - }) - }) - .unwrap(); - } - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/examples/window_shadow.rs b/crates/gpui_pre/examples/window_shadow.rs deleted file mode 100644 index fba5e91c..00000000 --- a/crates/gpui_pre/examples/window_shadow.rs +++ /dev/null @@ -1,252 +0,0 @@ -#![cfg_attr(target_family = "wasm", no_main)] - -#[path = "example_support/fonts.rs"] -mod example_support; - -use gpui::{ - App, Bounds, Context, CursorStyle, Decorations, HitboxBehavior, Hsla, MouseButton, Pixels, - Point, ResizeEdge, Size, Window, WindowBackgroundAppearance, WindowBounds, WindowDecorations, - WindowOptions, black, canvas, div, green, point, prelude::*, px, rgb, size, transparent_black, - white, -}; -use gpui_platform::application; - -struct WindowShadow {} - -// Things to do: -// 1. We need a way of calculating which edge or corner the mouse is on, -// and then dispatch on that -// 2. We need to improve the shadow rendering significantly -// 3. We need to implement the techniques in here in Zed - -impl Render for WindowShadow { - fn render(&mut self, window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let decorations = window.window_decorations(); - let rounding = px(10.0); - let shadow_size = px(10.0); - let border_size = px(1.0); - let grey = rgb(0x808080); - window.set_client_inset(shadow_size); - - div() - .id("window-backdrop") - .bg(transparent_black()) - .map(|div| match decorations { - Decorations::Server => div, - Decorations::Client { tiling, .. } => div - .bg(gpui::transparent_black()) - .child( - canvas( - |_bounds, window, _cx| { - window.insert_hitbox( - Bounds::new( - point(px(0.0), px(0.0)), - window.window_bounds().get_bounds().size, - ), - HitboxBehavior::Normal, - ) - }, - move |_bounds, hitbox, window, _cx| { - let mouse = window.mouse_position(); - let size = window.window_bounds().get_bounds().size; - let Some(edge) = resize_edge(mouse, shadow_size, size) else { - return; - }; - window.set_cursor_style( - match edge { - ResizeEdge::Top | ResizeEdge::Bottom => { - CursorStyle::ResizeUpDown - } - ResizeEdge::Left | ResizeEdge::Right => { - CursorStyle::ResizeLeftRight - } - ResizeEdge::TopLeft | ResizeEdge::BottomRight => { - CursorStyle::ResizeUpLeftDownRight - } - ResizeEdge::TopRight | ResizeEdge::BottomLeft => { - CursorStyle::ResizeUpRightDownLeft - } - }, - &hitbox, - ); - }, - ) - .size_full() - .absolute(), - ) - .when(!(tiling.top || tiling.right), |div| { - div.rounded_tr(rounding) - }) - .when(!(tiling.top || tiling.left), |div| div.rounded_tl(rounding)) - .when(!tiling.top, |div| div.pt(shadow_size)) - .when(!tiling.bottom, |div| div.pb(shadow_size)) - .when(!tiling.left, |div| div.pl(shadow_size)) - .when(!tiling.right, |div| div.pr(shadow_size)) - .on_mouse_move(|_e, window, _cx| window.refresh()) - .on_mouse_down(MouseButton::Left, move |e, window, _cx| { - let size = window.window_bounds().get_bounds().size; - let pos = e.position; - - match resize_edge(pos, shadow_size, size) { - Some(edge) => window.start_window_resize(edge), - None => window.start_window_move(), - }; - }), - }) - .size_full() - .child( - div() - .cursor(CursorStyle::Arrow) - .map(|div| match decorations { - Decorations::Server => div, - Decorations::Client { tiling } => div - .border_color(grey) - .when(!(tiling.top || tiling.right), |div| { - div.rounded_tr(rounding) - }) - .when(!(tiling.top || tiling.left), |div| div.rounded_tl(rounding)) - .when(!tiling.top, |div| div.border_t(border_size)) - .when(!tiling.bottom, |div| div.border_b(border_size)) - .when(!tiling.left, |div| div.border_l(border_size)) - .when(!tiling.right, |div| div.border_r(border_size)) - .when(!tiling.is_tiled(), |div| { - div.shadow(vec![ - gpui::BoxShadow::new( - px(0.), - px(0.), - Hsla { - h: 0., - s: 0., - l: 0., - a: 0.4, - }, - ) - .blur_radius(shadow_size / 2.), - ]) - }), - }) - .on_mouse_move(|_e, _, cx| { - cx.stop_propagation(); - }) - .bg(gpui::rgb(0xCCCCFF)) - .size_full() - .flex() - .flex_col() - .justify_around() - .child( - div().w_full().flex().flex_row().justify_around().child( - div() - .flex() - .bg(white()) - .size(px(300.0)) - .justify_center() - .items_center() - .shadow_lg() - .border_1() - .border_color(rgb(0x0000ff)) - .text_xl() - .text_color(rgb(0xffffff)) - .child( - div() - .id("hello") - .w(px(200.0)) - .h(px(100.0)) - .bg(green()) - .shadow(vec![ - gpui::BoxShadow::new( - px(0.), - px(0.), - Hsla { - h: 0., - s: 0., - l: 0., - a: 1.0, - }, - ) - .blur_radius(px(20.0)), - ]) - .map(|div| match decorations { - Decorations::Server => div, - Decorations::Client { .. } => div - .on_mouse_down( - MouseButton::Left, - |_e, window, _| { - window.start_window_move(); - }, - ) - .on_click(|e, window, _| { - if e.is_right_click() { - window.show_window_menu(e.position()); - } - }) - .text_color(black()) - .child("this is the custom titlebar"), - }), - ), - ), - ), - ) - } -} - -fn resize_edge(pos: Point, shadow_size: Pixels, size: Size) -> Option { - let edge = if pos.y < shadow_size && pos.x < shadow_size { - ResizeEdge::TopLeft - } else if pos.y < shadow_size && pos.x > size.width - shadow_size { - ResizeEdge::TopRight - } else if pos.y < shadow_size { - ResizeEdge::Top - } else if pos.y > size.height - shadow_size && pos.x < shadow_size { - ResizeEdge::BottomLeft - } else if pos.y > size.height - shadow_size && pos.x > size.width - shadow_size { - ResizeEdge::BottomRight - } else if pos.y > size.height - shadow_size { - ResizeEdge::Bottom - } else if pos.x < shadow_size { - ResizeEdge::Left - } else if pos.x > size.width - shadow_size { - ResizeEdge::Right - } else { - return None; - }; - Some(edge) -} - -fn run_example() { - application().run(|cx: &mut App| { - if !example_support::load_fonts(cx) { - return; - } - let bounds = Bounds::centered(None, size(px(600.0), px(600.0)), cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - window_background: WindowBackgroundAppearance::Opaque, - window_decorations: Some(WindowDecorations::Client), - ..Default::default() - }, - |window, cx| { - cx.new(|cx| { - cx.observe_window_appearance(window, |_, window, _| { - window.refresh(); - }) - .detach(); - WindowShadow {} - }) - }, - ) - .unwrap(); - }); -} - -#[cfg(not(target_family = "wasm"))] -fn main() { - run_example(); -} - -#[cfg(target_family = "wasm")] -#[wasm_bindgen::prelude::wasm_bindgen(start)] -pub fn start() { - gpui_platform::web_init(); - run_example(); -} diff --git a/crates/gpui_pre/resources/windows/gpui.manifest.xml b/crates/gpui_pre/resources/windows/gpui.manifest.xml deleted file mode 100644 index d11e1a4b..00000000 --- a/crates/gpui_pre/resources/windows/gpui.manifest.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - true/pm - PerMonitorV2 - SegmentHeap - - - - - - - - diff --git a/crates/gpui_pre/resources/windows/gpui.rc b/crates/gpui_pre/resources/windows/gpui.rc deleted file mode 100644 index a6f37877..00000000 --- a/crates/gpui_pre/resources/windows/gpui.rc +++ /dev/null @@ -1,2 +0,0 @@ -#define RT_MANIFEST 24 -1 RT_MANIFEST "resources/windows/gpui.manifest.xml" \ No newline at end of file diff --git a/crates/gpui_pre/src/_accessibility.rs b/crates/gpui_pre/src/_accessibility.rs deleted file mode 100644 index af14b638..00000000 --- a/crates/gpui_pre/src/_accessibility.rs +++ /dev/null @@ -1,295 +0,0 @@ -//! # Accessibility in GPUI -//! -//! "Accessibility" refers to the ability of your application to be used by all -//! users, regardless of disability status. There are many aspects, all important, including: -//! - Ensuring sufficient text contrast. -//! - Providing a mechanism to disable animations. -//! - Providing a mechanism to increase text sizes. -//! - etc. -//! -//! This guide is focused on **programmatic accessibility**. This allows -//! assistive technology, such as screen readers or Braille displays, to inspect -//! and interact with your app. Docs for contributors working on accessibility -//! support can be found in the `a11y` module's doc comment. -//! -//! GPUI integrates with [AccessKit] to provide programmatic accessibility -//! features (referred to as simply "accessibility" for the rest of this guide). -//! -//! A minimal example can be found in the `examples/a11y` directory. -//! -//! ## Background -//! -//! Accessibility support is based on two key capabilities: -//! - Exposing information about the current UI state to assistive technology. -//! - Responding to actions requested by assistive technology. -//! -//! For example, a screen reader might want to announce to the user that a new -//! button has appeared. The user may then want to use a voice control program -//! to press that button. -//! -//! ### IDs in GPUI - [`ElementId`] and [`GlobalElementId`] -//! -//! In GPUI, each [`Element`] can have an [`id`][Element::id]: -//! ```rust -//! # use gpui::*; -//! let div_with_id = div().id("my-id").child(text!("hello")); -//! -//! // IDs are optional -//! let div_without_id = div().child(text!("hello")); -//! ``` -//! -//! [`Element`]s with IDs are also assigned a [`GlobalElementId`]. This global -//! ID is formed by composing all the non-`None` IDs of its ancestors. For -//! example: -//! ```rust -//! # use gpui::*; -//! let inner = div().id("inner-id"); -//! let middle = div().child(inner); // no ID -//! let outer = div().id("outer-id").child(middle); -//! ``` -//! In this example, `inner`s global ID is (roughly speaking) `["outer-id", -//! "inner-id"]`. -//! -//! Since `middle` doesn't have an ID itself, it has no global ID. -//! -//! [`GlobalElementId`]s should be unique per-frame. Duplicate global IDs in the -//! same frame will likely cause bugs. -//! -//! ### IDs and accessibility -//! -//! When GPUI renders a frame, it walks your UI tree, and finds nodes with -//! global IDs, and informs assistive technology about this node. -//! -//! In order for nodes to be reported, they must also have a non-`None` -//! [`role`][Element::a11y_role]. This is used to inform assistive technology -//! what *sort* of node it is (button, label, table, etc.). You can use -//! [`div().id(...).role()`][StatefulInteractiveElement::role] to set the role. -//! -//! Nodes with the same global ID *across frames* are considered to be "the -//! same" node. For example: -//! ```rust -//! # use gpui::*; -//! // The UI in frame 1 -//! let frame_1 = div() -//! .id("parent") -//! .role(Role::Button) -//! .child( -//! div() -//! .id("id-1") -//! .role(Role::Label) -//! .child(text!("hello")) -//! ); -//! -//! // The UI on the next frame -//! let frame_2 = div() -//! .id("parent") -//! .role(Role::Button) -//! .child( -//! div() -//! .id("id-2") // <- different ID -//! .role(Role::Label) -//! .child(text!("hello")) -//! ); -//! ``` -//! Logically, the UI has not changed. But the screen reader has no way of -//! knowing that both child [`div`]s are "the same". So assistive technology -//! will interpret this as one node being removed, and another node being added. -//! This can be very disorienting for users, since announcements typically only -//! happen when something has *meaningfully* changed. -//! -//! In other words, by controlling the ID of an element, you can control whether -//! a change to a UI element is considered meaningful. You can also control -//! whether elements are reported to assistive technology *at all* by setting -//! the [`role`][Element::a11y_role], since nodes with no role are not reported. -//! -//! #### IDs and text -//! -//! Special care must be taken when dealing with text. -//! -//! GPUI provides the [`text!`] macro, which wraps strings in the [`Text`] type, -//! but automatically derives an ID. Usually, this is what you want. However, -//! the way it generates its ID is subtle and perhaps surprising. -//! -//! The ID of an invocation of the [`text!`] macro is derived from the -//! **location in the source code of that invocation**. For example: -//! -//! ```rust -//! # use gpui::*; -//! let a = text!("a"); -//! let b = text!("b"); -//! -//! // Different source locations, different IDs -//! assert_ne!(a.id(), b.id()); -//! -//! // However: -//! -//! fn make_text(s: &str) -> Text { text!(s) } -//! -//! let a = make_text("a"); -//! let b = make_text("b"); -//! -//! // Both `a` and `b` are produced by the same `text!` invocation, so the IDs -//! // are the same -//! assert_eq!(a.id(), b.id()); -//! ``` -//! This can produce surprising behaviour. For example, this footgun: -//! ```rust -//! # use gpui::*; -//! let todos = vec!["eat lunch", "drink water", "go to gym"]; -//! let todo_divs = todos.into_iter().map(|todo| { -//! text!(todo) -//! }); -//! -//! div() -//! .id("todo-list") -//! .role(Role::Document) -//! .children(todo_divs); // ERROR: multiple nodes with the same global ID -//! ``` -//! -//! Here, when we map the iterator, since we have only written [`text!`] once, -//! there is only one ID. And since they have the same ancestors and the same -//! ID, they will have the same global ID. In release builds, this will mean -//! some nodes get silently dropped! -//! -//! To fix this, you can set an ID: -//! ```rust -//! # use gpui::*; -//! let todos = vec!["eat lunch", "drink water", "go to gym"]; -//! let todo_divs = todos.into_iter().enumerate().map(|(index, todo)| { -//! text!(todo).with_id(index) // OR `text(id = index, todo)` -//! }); -//! -//! div() -//! .id("todo-list") -//! .role(Role::Document) -//! .children(todo_divs); -//! ``` -//! Another possible solution is to wrap the [`text!`] in another node that -//! *does* have a unique global ID. For example: -//! ```rust -//! # use gpui::*; -//! let todos = vec!["eat lunch", "drink water", "go to gym"]; -//! let todo_divs = todos.into_iter().enumerate().map(|(index, todo)| { -//! div().id(index).child(text!(todo)) -//! }); -//! -//! div() -//! .id("todo-list") -//! .role(Role::Document) -//! .children(todo_divs); -//! ``` -//! Since the AccessKit [`NodeId`][accesskit::NodeId] is derived from the global -//! ID, and the global ID takes into account the IDs of all ancestors, this -//! works too. -//! -//! Occasionally, you will need to create a [`Text`] element with *no* ID. You -//! can achieve this with [`Text::new_inaccessible`]. If you are creating a -//! custom UI component (e.g. a button), you may want this so that you can set a -//! label property on a parent [`div`] without duplicating the text in the -//! accessibility tree. -//! -//! ### Handling actions -//! -//! Assistive technology can dispatch actions to the UI. While many users of -//! assistive technology use traditional input devices (e.g. a keyboard), some -//! use more specialized systems. For example, users with limited mobility may -//! use voice control to interact with your app. -//! -//! When a user dispatches an action, it is dispatched *to a specific node*. It -//! is your responsibility to tell the UI elements how they should respond when -//! a request comes in. -//! -//! Note, these actions are **totally unrelated** to GPUI's [`Action`] trait. -//! AccessKit exposes [`accesskit::Action`]. In GPUI, this is re-exported as -//! [`AccessibleAction`]. -//! -//! To respond to an accessible action, use -//! [`div().on_a11y_action()`][InteractiveElement::on_a11y_action]: -//! ```rust,ignore -//! div() -//! .id("my-slider") -//! .role(Role::Slider) -//! .on_a11y_action(AccessibleAction::Increment, |_extra, _window, _cx| { -//! position += 1; -//! cx.notify(); -//! }) -//! .child(my_cool_slider()); -//! ``` -//! -//! Note that some common actions are automatically registered. For example, -//! [`.on_click()`][StatefulInteractiveElement::on_click] adds an -//! [`AccessibleAction::Click`] handler that calls the click handler. -//! -//! ## Synthetic children -//! -//! Sometimes, a custom [`Element`] may want to appear as if it is really made -//! of multiple nodes. For example, a totally hypothetical custom text editor -//! element may want to have [`Role::TextInput`], while presenting children -//! consisting of [`Role::TextRun`]s. -//! -//! This is possible using [`Element::a11y_synthetic_children`]. For example: -//! ```rust,ignore -//! # use gpui::*; -//! impl Element for MyCustomTextField { -//! -//! // ... -//! -//! fn a11y_role(&self) -> Option { -//! Some(Role::TextInput) -//! } -//! -//! fn a11y_synthetic_children( -//! &mut self, -//! _prepaint: &mut Self::PrepaintState, -//! builder: &mut A11ySubtreeBuilder, -//! ) { -//! // Create the synthetic child node -//! let mut run = accesskit::Node::new(Role::TextRun); -//! run.set_value(self.text.clone()); -//! run.set_character_lengths( -//! self.text.chars().map(|c| c.len_utf8() as u8).collect::>(), -//! ); -//! -//! // Insert it as a child of `MyCustomTextField` -//! let run_id = builder.synthetic_node_id(0); -//! builder.push_child(run_id, run); -//! -//! // You can also mutate the parent (i.e. the `MyCustomTextField`) -//! let caret = accesskit::TextPosition { -//! node: run_id, -//! character_index: self.cursor, -//! }; -//! builder.parent_node().set_text_selection(accesskit::TextSelection { -//! anchor: caret, -//! focus: caret, -//! }); -//! } -//! } -//! ``` -//! -//! Notably, synthetic children are added *after* an element is -//! [prepainted][Element::prepaint], so prepaint state can be used (for example, -//! to determine what is visible on screen). -//! -//! ## Further reading -//! -//! Designing high-quality accessible interfaces can be challenging, in the same -//! way that designing high-quality traditional interfaces can be. The -//! following pages have useful information: -//! -//! - [AccessKit]: The cross-platform accessibility toolkit GPUI uses -//! internally. -//! - [MDN WAI-ARIA basics][mdn-aria]: Introduction to roles, properties, and -//! states. -//! - [ARIA Authoring Practices Guide][apg]: W3C patterns for accessible -//! widgets. -//! -//! Note that, while GPUI mimics web APIs, it doesn't necessarily behave -//! *exactly* as a web browser would with the same attributes. -//! -//! [AccessKit]: https://accesskit.dev/ -//! [mdn-aria]: https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Accessibility/WAI-ARIA_basics -//! [apg]: https://www.w3.org/WAI/ARIA/apg/ - -#[cfg(doc)] -use crate::*; // so I don't have to qualify every type :) diff --git a/crates/gpui_pre/src/_ownership_and_data_flow.rs b/crates/gpui_pre/src/_ownership_and_data_flow.rs deleted file mode 100644 index 68699cce..00000000 --- a/crates/gpui_pre/src/_ownership_and_data_flow.rs +++ /dev/null @@ -1,140 +0,0 @@ -//! In GPUI, every model or view in the application is actually owned by a single top-level object called the `App`. When a new entity or view is created (referred to collectively as _entities_), the application is given ownership of their state to enable their participation in a variety of app services and interaction with other entities. -//! -//! To illustrate, consider the trivial app below. We start the app by calling `run` with a callback, which is passed a reference to the `App` that owns all the state for the application. This `App` is our gateway to all application-level services, such as opening windows, presenting dialogs, etc. It also has an `insert_entity` method, which is called below to create an entity and give ownership of it to the application. -//! -//! ```no_run -//! # use gpui::{App, AppContext, Application, Entity}; -//! # struct Counter { -//! # count: usize, -//! # } -//! gpui_platform::application().run(|cx: &mut App| { -//! let _counter: Entity = cx.new(|_cx| Counter { count: 0 }); -//! // ... -//! }); -//! ``` -//! -//! The call to `new_entity` returns an _entity handle_, which carries a type parameter based on the type of object it references. By itself, this `Entity` handle doesn't provide access to the entity's state. It's merely an inert identifier plus a compile-time type tag, and it maintains a reference counted pointer to the underlying `Counter` object that is owned by the app. -//! -//! Much like an `Rc` from the Rust standard library, this reference count is incremented when the handle is cloned and decremented when it is dropped to enable shared ownership over the underlying model, but unlike an `Rc` it only provides access to the model's state when a reference to an `App` is available. The handle doesn't truly _own_ the state, but it can be used to access the state from its true owner, the `App`. Stripping away some of the setup code for brevity: -//! -//! ```no_run -//! # use gpui::{App, AppContext, Application, Context, Entity}; -//! # struct Counter { -//! # count: usize, -//! # } -//! gpui_platform::application().run(|cx: &mut App| { -//! let counter: Entity = cx.new(|_cx| Counter { count: 0 }); -//! // Call `update` to access the model's state. -//! counter.update(cx, |counter: &mut Counter, _cx: &mut Context| { -//! counter.count += 1; -//! }); -//! }); -//! ``` -//! -//! To update the counter, we call `update` on the handle, passing the context reference and a callback. The callback is yielded a mutable reference to the counter, which can be used to manipulate state. -//! -//! The callback is also provided a second `Context` reference. This reference is similar to the `App` reference provided to the `run` callback. A `Context` is actually a wrapper around the `App`, including some additional data to indicate which particular entity it is tied to; in this case the counter. -//! -//! In addition to the application-level services provided by `App`, a `Context` provides access to entity-level services. For example, it can be used it to inform observers of this entity that its state has changed. Let's add that to our example, by calling `cx.notify()`. -//! -//! ```no_run -//! # use gpui::{App, AppContext, Application, Entity}; -//! # struct Counter { -//! # count: usize, -//! # } -//! gpui_platform::application().run(|cx: &mut App| { -//! let counter: Entity = cx.new(|_cx| Counter { count: 0 }); -//! counter.update(cx, |counter, cx| { -//! counter.count += 1; -//! cx.notify(); // Notify observers -//! }); -//! }); -//! ``` -//! -//! Next, these notifications need to be observed and reacted to. Before updating the counter, we'll construct a second counter that observes it. Whenever the first counter changes, twice its count is assigned to the second counter. Note how `observe` is called on the `Context` belonging to our second counter to arrange for it to be notified whenever the first counter notifies. The call to `observe` returns a `Subscription`, which is `detach`ed to preserve this behavior for as long as both counters exist. We could also store this subscription and drop it at a time of our choosing to cancel this behavior. -//! -//! The `observe` callback is passed a mutable reference to the observer and a _handle_ to the observed counter, whose state we access with the `read` method. -//! -//! ```no_run -//! # use gpui::{App, AppContext, Application, Entity, prelude::*}; -//! # struct Counter { -//! # count: usize, -//! # } -//! gpui_platform::application().run(|cx: &mut App| { -//! let first_counter: Entity = cx.new(|_cx| Counter { count: 0 }); -//! -//! let second_counter = cx.new(|cx: &mut Context| { -//! // Note we can set up the callback before the Counter is even created! -//! cx.observe( -//! &first_counter, -//! |second: &mut Counter, first: Entity, cx| { -//! second.count = first.read(cx).count * 2; -//! }, -//! ) -//! .detach(); -//! -//! Counter { count: 0 } -//! }); -//! -//! first_counter.update(cx, |counter, cx| { -//! counter.count += 1; -//! cx.notify(); -//! }); -//! -//! assert_eq!(second_counter.read(cx).count, 2); -//! }); -//! ``` -//! -//! After updating the first counter, it can be noted that the observing counter's state is maintained according to our subscription. -//! -//! In addition to `observe` and `notify`, which indicate that an entity's state has changed, GPUI also offers `subscribe` and `emit`, which enables entities to emit typed events. To opt into this system, the emitting object must implement the `EventEmitter` trait. -//! -//! Let's introduce a new event type called `CounterChangeEvent`, then indicate that `Counter` can emit this type of event: -//! -//! ```no_run -//! use gpui::EventEmitter; -//! # struct Counter { -//! # count: usize, -//! # } -//! struct CounterChangeEvent { -//! increment: usize, -//! } -//! -//! impl EventEmitter for Counter {} -//! ``` -//! -//! Next, the example should be updated, replacing the observation with a subscription. Whenever the counter is incremented, a `Change` event is emitted to indicate the magnitude of the increase. -//! -//! ```no_run -//! # use gpui::{App, AppContext, Application, Context, Entity, EventEmitter}; -//! # struct Counter { -//! # count: usize, -//! # } -//! # struct CounterChangeEvent { -//! # increment: usize, -//! # } -//! # impl EventEmitter for Counter {} -//! gpui_platform::application().run(|cx: &mut App| { -//! let first_counter: Entity = cx.new(|_cx| Counter { count: 0 }); -//! -//! let second_counter = cx.new(|cx: &mut Context| { -//! // Note we can set up the callback before the Counter is even created! -//! cx.subscribe(&first_counter, |second: &mut Counter, _first: Entity, event, _cx| { -//! second.count += event.increment * 2; -//! }) -//! .detach(); -//! -//! Counter { -//! count: first_counter.read(cx).count * 2, -//! } -//! }); -//! -//! first_counter.update(cx, |first, cx| { -//! first.count += 2; -//! cx.emit(CounterChangeEvent { increment: 2 }); -//! cx.notify(); -//! }); -//! -//! assert_eq!(second_counter.read(cx).count, 4); -//! }); -//! ``` diff --git a/crates/gpui_pre/src/action.rs b/crates/gpui_pre/src/action.rs deleted file mode 100644 index 2d2473dc..00000000 --- a/crates/gpui_pre/src/action.rs +++ /dev/null @@ -1,459 +0,0 @@ -// Modified for gpui-pre (snapshot of zed@5b055fa): the `actions!` derive paths are crate-relative. -use anyhow::{Context as _, Result}; -use collections::{HashMap, TypeIdHashMap}; -pub use gpui_macros::Action; -pub use no_action::{NoAction, Unbind, is_no_action, is_unbind}; -use serde_json::json; -use std::{ - any::{Any, TypeId}, - fmt::Display, -}; - -/// Defines and registers unit structs that can be used as actions. For more complex data types, derive `Action`. -/// -/// For example: -/// -/// ``` -/// use gpui::actions; -/// actions!(editor, [MoveUp, MoveDown, MoveLeft, MoveRight, Newline]); -/// ``` -/// -/// This will create actions with names like `editor::MoveUp`, `editor::MoveDown`, etc. -/// -/// The namespace argument `editor` can also be omitted, though it is required for Zed actions. -#[macro_export] -macro_rules! actions { - ($namespace:path, [ $( $(#[$attr:meta])* $name:ident),* $(,)? ]) => { - $( - #[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::default::Default, ::std::fmt::Debug, $crate::Action)] - #[action(namespace = $namespace)] - $(#[$attr])* - pub struct $name; - )* - }; - ([ $( $(#[$attr:meta])* $name:ident),* $(,)? ]) => { - $( - #[derive(::std::clone::Clone, ::std::cmp::PartialEq, ::std::default::Default, ::std::fmt::Debug, $crate::Action)] - $(#[$attr])* - pub struct $name; - )* - }; -} - -/// Actions are used to implement keyboard-driven UI. When you declare an action, you can bind keys -/// to the action in the keymap and listeners for that action in the element tree. -/// -/// To declare a list of simple actions, you can use the actions! macro, which defines a simple unit -/// struct action for each listed action name in the given namespace. -/// -/// ``` -/// use gpui::actions; -/// actions!(editor, [MoveUp, MoveDown, MoveLeft, MoveRight, Newline]); -/// ``` -/// -/// Registering the actions with the same name will result in a panic during `App` creation. -/// -/// # Derive Macro -/// -/// More complex data types can also be actions, by using the derive macro for `Action`: -/// -/// ``` -/// use gpui::Action; -/// #[derive(Clone, PartialEq, serde::Deserialize, schemars::JsonSchema, Action)] -/// #[action(namespace = editor)] -/// pub struct SelectNext { -/// pub replace_newest: bool, -/// } -/// ``` -/// -/// The derive macro for `Action` requires that the type implement `Clone` and `PartialEq`. It also -/// requires `serde::Deserialize` and `schemars::JsonSchema` unless `#[action(no_json)]` is -/// specified. In Zed these trait impls are used to load keymaps from JSON. -/// -/// Multiple arguments separated by commas may be specified in `#[action(...)]`: -/// -/// - `namespace = some_namespace` sets the namespace. In Zed this is required. -/// -/// - `name = "ActionName"` overrides the action's name. This must not contain `::`. -/// -/// - `no_json` causes the `build` method to always error and `action_json_schema` to return `None`, -/// and allows actions not implement `serde::Serialize` and `schemars::JsonSchema`. -/// -/// - `no_register` skips registering the action. This is useful for implementing the `Action` trait -/// while not supporting invocation by name or JSON deserialization. -/// -/// - `deprecated_aliases = ["editor::SomeAction"]` specifies deprecated old names for the action. -/// These action names should *not* correspond to any actions that are registered. These old names -/// can then still be used to refer to invoke this action. In Zed, the keymap JSON schema will -/// accept these old names and provide warnings. -/// -/// - `deprecated = "Message about why this action is deprecation"` specifies a deprecation message. -/// In Zed, the keymap JSON schema will cause this to be displayed as a warning. -/// -/// # Manual Implementation -/// -/// If you want to control the behavior of the action trait manually, you can use the lower-level -/// `#[register_action]` macro, which only generates the code needed to register your action before -/// `main`. -/// -/// ``` -/// use gpui::{SharedString, register_action}; -/// #[derive(Clone, PartialEq, Eq, serde::Deserialize, schemars::JsonSchema)] -/// pub struct Paste { -/// pub content: SharedString, -/// } -/// -/// impl gpui::Action for Paste { -/// # fn boxed_clone(&self) -> Box { unimplemented!()} -/// # fn partial_eq(&self, other: &dyn gpui::Action) -> bool { unimplemented!() } -/// # fn name(&self) -> &'static str { "Paste" } -/// # fn name_for_type() -> &'static str { "Paste" } -/// # fn build(value: serde_json::Value) -> anyhow::Result> { -/// # unimplemented!() -/// # } -/// } -/// -/// register_action!(Paste); -/// ``` -pub trait Action: Any + Send { - /// Clone the action into a new box - fn boxed_clone(&self) -> Box; - - /// Do a partial equality check on this action and the other - fn partial_eq(&self, action: &dyn Action) -> bool; - - /// Get the name of this action, for displaying in UI - fn name(&self) -> &'static str; - - /// Get the name of this action type (static) - fn name_for_type() -> &'static str - where - Self: Sized; - - /// Build this action from a JSON value. This is used to construct actions from the keymap. - /// A value of `{}` will be passed for actions that don't have any parameters. - fn build(value: serde_json::Value) -> Result> - where - Self: Sized; - - /// Optional JSON schema for the action's input data. - fn action_json_schema(_: &mut schemars::SchemaGenerator) -> Option - where - Self: Sized, - { - None - } - - /// A list of alternate, deprecated names for this action. These names can still be used to - /// invoke the action. In Zed, the keymap JSON schema will accept these old names and provide - /// warnings. - fn deprecated_aliases() -> &'static [&'static str] - where - Self: Sized, - { - &[] - } - - /// Returns the deprecation message for this action, if any. In Zed, the keymap JSON schema will - /// cause this to be displayed as a warning. - fn deprecation_message() -> Option<&'static str> - where - Self: Sized, - { - None - } - - /// The documentation for this action, if any. When using the derive macro for actions - /// this will be automatically generated from the doc comments on the action struct. - fn documentation() -> Option<&'static str> - where - Self: Sized, - { - None - } -} - -impl std::fmt::Debug for dyn Action { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("dyn Action") - .field("name", &self.name()) - .finish() - } -} - -impl dyn Action { - /// Type-erase Action type. - pub fn as_any(&self) -> &dyn Any { - self as &dyn Any - } -} - -/// Error type for `Keystroke::parse`. This is used instead of `anyhow::Error` so that Zed can use -/// markdown to display it. -#[derive(Debug)] -pub enum ActionBuildError { - /// Indicates that an action with this name has not been registered. - NotFound { - /// Name of the action that was not found. - name: String, - }, - /// Indicates that an error occurred while building the action, typically a JSON deserialization - /// error. - BuildError { - /// Name of the action that was attempting to be built. - name: String, - /// Error that occurred while building the action. - error: anyhow::Error, - }, -} - -impl std::error::Error for ActionBuildError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - ActionBuildError::NotFound { .. } => None, - ActionBuildError::BuildError { error, .. } => error.source(), - } - } -} - -impl Display for ActionBuildError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ActionBuildError::NotFound { name } => { - write!(f, "Didn't find an action named \"{name}\"") - } - ActionBuildError::BuildError { name, error } => { - write!(f, "Error while building action \"{name}\": {error}") - } - } - } -} - -type ActionBuilder = fn(json: serde_json::Value) -> anyhow::Result>; - -pub(crate) struct ActionRegistry { - by_name: HashMap<&'static str, ActionData>, - names_by_type_id: TypeIdHashMap<&'static str>, - all_names: Vec<&'static str>, // So we can return a static slice. - deprecated_aliases: HashMap<&'static str, &'static str>, // deprecated name -> preferred name - deprecation_messages: HashMap<&'static str, &'static str>, // action name -> deprecation message - documentation: HashMap<&'static str, &'static str>, // action name -> documentation -} - -impl Default for ActionRegistry { - fn default() -> Self { - let mut this = ActionRegistry { - by_name: Default::default(), - names_by_type_id: Default::default(), - documentation: Default::default(), - all_names: Default::default(), - deprecated_aliases: Default::default(), - deprecation_messages: Default::default(), - }; - - this.load_actions(); - - this - } -} - -struct ActionData { - pub build: ActionBuilder, - pub json_schema: fn(&mut schemars::SchemaGenerator) -> Option, -} - -/// This type must be public so that our macros can build it in other crates. -/// But this is an implementation detail and should not be used directly. -#[doc(hidden)] -pub struct MacroActionBuilder(pub fn() -> MacroActionData); - -/// This type must be public so that our macros can build it in other crates. -/// But this is an implementation detail and should not be used directly. -#[doc(hidden)] -pub struct MacroActionData { - pub name: &'static str, - pub type_id: TypeId, - pub build: ActionBuilder, - pub json_schema: fn(&mut schemars::SchemaGenerator) -> Option, - pub deprecated_aliases: &'static [&'static str], - pub deprecation_message: Option<&'static str>, - pub documentation: Option<&'static str>, -} - -inventory::collect!(MacroActionBuilder); - -impl ActionRegistry { - /// Load all registered actions into the registry. - pub(crate) fn load_actions(&mut self) { - for builder in inventory::iter:: { - let action = builder.0(); - self.insert_action(action); - } - } - - fn insert_action(&mut self, action: MacroActionData) { - let name = action.name; - if self.by_name.contains_key(name) { - panic!( - "Action with name `{name}` already registered \ - (might be registered in `#[action(deprecated_aliases = [...])]`." - ); - } - self.by_name.insert( - name, - ActionData { - build: action.build, - json_schema: action.json_schema, - }, - ); - for &alias in action.deprecated_aliases { - if self.by_name.contains_key(alias) { - panic!( - "Action with name `{alias}` already registered. \ - `{alias}` is specified in `#[action(deprecated_aliases = [...])]` for action `{name}`." - ); - } - self.by_name.insert( - alias, - ActionData { - build: action.build, - json_schema: action.json_schema, - }, - ); - self.deprecated_aliases.insert(alias, name); - self.all_names.push(alias); - } - self.names_by_type_id.insert(action.type_id, name); - self.all_names.push(name); - if let Some(deprecation_msg) = action.deprecation_message { - self.deprecation_messages.insert(name, deprecation_msg); - } - if let Some(documentation) = action.documentation { - self.documentation.insert(name, documentation); - } - } - - /// Construct an action based on its name and optional JSON parameters sourced from the keymap. - pub fn build_action_type(&self, type_id: &TypeId) -> Result> { - let name = self - .names_by_type_id - .get(type_id) - .with_context(|| format!("no action type registered for {type_id:?}"))?; - - Ok(self.build_action(name, None)?) - } - - #[cfg(feature = "profiler")] - pub(crate) fn try_resolve_action(&self, type_id: &TypeId) -> Option<&'static str> { - self.names_by_type_id.get(type_id).copied() - } - - /// Construct an action based on its name and optional JSON parameters sourced from the keymap. - pub fn build_action( - &self, - name: &str, - params: Option, - ) -> std::result::Result, ActionBuildError> { - let build_action = self - .by_name - .get(name) - .ok_or_else(|| ActionBuildError::NotFound { - name: name.to_owned(), - })? - .build; - (build_action)(params.unwrap_or_else(|| json!({}))).map_err(|e| { - ActionBuildError::BuildError { - name: name.to_owned(), - error: e, - } - }) - } - - pub fn all_action_names(&self) -> &[&'static str] { - self.all_names.as_slice() - } - - pub fn action_schemas( - &self, - generator: &mut schemars::SchemaGenerator, - ) -> Vec<(&'static str, Option)> { - // Use the order from all_names so that the resulting schema has sensible order. - self.all_names - .iter() - .map(|name| { - let action_data = self - .by_name - .get(name) - .expect("All actions in all_names should be registered"); - (*name, (action_data.json_schema)(generator)) - }) - .collect::>() - } - - pub fn action_schema_by_name( - &self, - name: &str, - generator: &mut schemars::SchemaGenerator, - ) -> Option> { - self.by_name - .get(name) - .map(|action_data| (action_data.json_schema)(generator)) - } - - pub fn deprecated_aliases(&self) -> &HashMap<&'static str, &'static str> { - &self.deprecated_aliases - } - - pub fn deprecation_messages(&self) -> &HashMap<&'static str, &'static str> { - &self.deprecation_messages - } - - pub fn documentation(&self) -> &HashMap<&'static str, &'static str> { - &self.documentation - } -} - -/// Generate a list of all the registered actions. -/// Useful for transforming the list of available actions into a -/// format suited for static analysis such as in validating keymaps, or -/// generating documentation. -pub fn generate_list_of_all_registered_actions() -> impl Iterator { - inventory::iter:: - .into_iter() - .map(|builder| builder.0()) -} - -mod no_action { - use crate as gpui; - use schemars::JsonSchema; - use serde::Deserialize; - - actions!( - zed, - [ - /// Action with special handling which unbinds the keybinding this is associated with, - /// if it is the highest precedence match. - NoAction - ] - ); - - /// Action with special handling which unbinds later bindings for the same keystrokes when they - /// dispatch the named action, regardless of that action's context. - /// - /// In keymap JSON this is written as: - /// - /// `["zed::Unbind", "editor::NewLine"]` - #[derive(Clone, Debug, PartialEq, Deserialize, JsonSchema, gpui::Action)] - #[action(namespace = zed)] - pub struct Unbind(pub gpui::SharedString); - - /// Returns whether or not this action represents a removed key binding. - pub fn is_no_action(action: &dyn gpui::Action) -> bool { - action.as_any().is::() - } - - /// Returns whether or not this action represents an unbind marker. - pub fn is_unbind(action: &dyn gpui::Action) -> bool { - action.as_any().is::() - } -} diff --git a/crates/gpui_pre/src/app.rs b/crates/gpui_pre/src/app.rs deleted file mode 100644 index 9811fc37..00000000 --- a/crates/gpui_pre/src/app.rs +++ /dev/null @@ -1,3192 +0,0 @@ -use scheduler::Instant; -use std::{ - any::{TypeId, type_name}, - cell::{BorrowMutError, Cell, Ref, RefCell, RefMut}, - ffi::OsString, - marker::PhantomData, - mem, - ops::{Deref, DerefMut}, - path::{Path, PathBuf}, - rc::{Rc, Weak}, - sync::{Arc, atomic::Ordering::SeqCst}, - time::Duration, -}; - -use anyhow::{Context as _, Result, anyhow}; -use derive_more::{Deref, DerefMut}; -use futures::{ - Future, FutureExt, - channel::oneshot, - future::{LocalBoxFuture, Shared}, -}; -use itertools::Itertools; -use parking_lot::RwLock; -use slotmap::SlotMap; - -pub use async_context::*; -#[cfg(feature = "bench-support")] -pub use bench_context::{BenchAppContext, BenchReport, BenchWindowContext, bench_platform}; -use collections::{FxHashMap, FxHashSet, HashMap, TypeIdHashMap, TypeIdHashSet, VecDeque}; -pub use context::*; -pub use entity_map::*; -use gpui_util::{ResultExt, debug_panic}; -#[cfg(any(test, feature = "test-support"))] -pub use headless_app_context::*; -use http_client::{HttpClient, Url}; -use smallvec::SmallVec; -#[cfg(any(test, feature = "test-support"))] -pub use test_app::*; -#[cfg(any(test, feature = "test-support"))] -pub use test_context::*; -#[cfg(all(target_os = "macos", any(test, feature = "test-support")))] -pub use visual_test_context::*; - -#[cfg(any(feature = "inspector", debug_assertions))] -use crate::InspectorElementRegistry; -use crate::{ - Action, ActionBuildError, ActionRegistry, Any, AnyView, AnyWindowHandle, AppContext, Arena, - ArenaBox, Asset, AssetSource, BackgroundExecutor, Bounds, ClipboardItem, ClipboardReadError, - CursorStyle, DispatchPhase, DisplayId, EventEmitter, ExternalDragPayload, FocusHandle, - FocusMap, ForegroundExecutor, Global, KeyBinding, KeyContext, Keymap, Keystroke, LayoutId, - Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform, PlatformDisplay, - PlatformKeyboardLayout, PlatformKeyboardMapper, Point, Priority, PromptBuilder, PromptButton, - PromptHandle, PromptLevel, Render, RenderImage, RenderablePromptHandle, Reservation, - ScreenCaptureSource, SharedString, SubscriberSet, Subscription, SvgRenderer, - SystemNotification, SystemNotificationResponse, Task, TextRenderingMode, TextSystem, - ThermalState, Window, WindowAppearance, WindowButtonLayout, WindowHandle, WindowId, - WindowInvalidator, - colors::{Colors, GlobalColors}, - hash, init_app_menus, -}; - -mod async_context; -#[cfg(feature = "bench-support")] -mod bench_context; -mod context; -mod entity_map; -#[cfg(any(test, feature = "test-support"))] -mod headless_app_context; -#[cfg(any(test, feature = "test-support"))] -mod test_app; -#[cfg(any(test, feature = "test-support"))] -mod test_context; -#[cfg(all(target_os = "macos", any(test, feature = "test-support")))] -mod visual_test_context; - -/// The duration for which native applications wait for futures returned from -/// [Context::on_app_quit] before fully quitting. -pub const SHUTDOWN_TIMEOUT: Duration = Duration::from_millis(200); - -/// Temporary(?) wrapper around [`RefCell`] to help us debug any double borrows. -/// Strongly consider removing after stabilization. -#[doc(hidden)] -pub struct AppCell { - app: RefCell, -} - -impl AppCell { - #[doc(hidden)] - #[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()) - } - - #[doc(hidden)] - #[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()) - } - - #[doc(hidden)] - #[track_caller] - pub fn try_borrow_mut(&self) -> Result, BorrowMutError> { - if option_env!("TRACK_THREAD_BORROWS").is_some() { - let thread_id = std::thread::current().id(); - eprintln!("borrowed {thread_id:?}"); - } - Ok(AppRefMut(self.app.try_borrow_mut()?)) - } -} - -#[doc(hidden)] -#[derive(Deref, DerefMut)] -pub struct AppRef<'a>(Ref<'a, App>); - -impl Drop for AppRef<'_> { - 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:?}"); - } - } -} - -#[doc(hidden)] -#[derive(Deref, DerefMut)] -pub struct AppRefMut<'a>(RefMut<'a, App>); - -impl Drop for AppRefMut<'_> { - fn drop(&mut self) { - if option_env!("TRACK_THREAD_BORROWS").is_some() { - let thread_id = std::thread::current().id(); - eprintln!("dropped {thread_id:?}"); - } - } -} - -/// A reference to a GPUI application, typically constructed in the `main` function of your app. -/// You won't interact with this type much outside of initial configuration and startup. -pub struct Application(Rc); - -/// A strong handle to an [`Application`] started with [`Application::run_embedded`]. -/// -/// Dropping this handle releases the app, so an embedder must hold it for as long as the -/// app should run. While held, it is the embedder's entry point back into GPUI each time -/// the external run loop gives it control. -pub struct ApplicationHandle { - app: Rc, -} - -impl ApplicationHandle { - /// Invoke `f` with the app context. Must not be called re-entrantly from code that - /// is already inside an update; the app state is a `RefCell` and will panic on a - /// double borrow. - pub fn update(&self, f: impl FnOnce(&mut App) -> R) -> R { - let cx = &mut *self.app.borrow_mut(); - f(cx) - } - - /// An [`AsyncApp`] for use across await points. It holds the app weakly; keeping the - /// app alive remains this handle's job. - pub fn to_async(&self) -> AsyncApp { - self.update(|cx| cx.to_async()) - } -} - -/// Represents an application before it is fully launched. Once your app is -/// configured, you'll start the app with `App::run`. -impl Application { - /// Builds an app with a caller-provided platform implementation. - pub fn with_platform(platform: Rc) -> Self { - Self(App::new_app( - platform, - Arc::new(()), - Arc::new(NullHttpClient), - )) - } - - /// Builds an app with accessibility (AccessKit) integration forcibly - /// disabled. - /// - /// In this mode, accessibility APIs (e.g. - /// [`div().role()`][crate::StatefulInteractiveElement::role]) silently - /// no-op. - /// - /// See the [accessibility guide](crate::_accessibility) for an overview of - /// the features this disables. - pub fn new_inaccessible(platform: Rc) -> Self { - let this = Self::with_platform(platform); - this.0.borrow_mut().accessibility_force_disabled = true; - this - } - - /// Assigns the source of assets for the application. - pub fn with_assets(self, asset_source: impl AssetSource) -> Self { - let mut context_lock = self.0.borrow_mut(); - let asset_source = Arc::new(asset_source); - context_lock.asset_source = asset_source.clone(); - context_lock.svg_renderer = SvgRenderer::new(asset_source); - drop(context_lock); - self - } - - /// Configures arguments to pass when restarting the application. - pub fn with_restart_arguments(self, arguments: Vec) -> Self { - self.0.borrow_mut().restart_arguments = arguments; - self - } - - /// Sets the HTTP client for the application. - pub fn with_http_client(self, http_client: Arc) -> Self { - let mut context_lock = self.0.borrow_mut(); - context_lock.http_client = http_client; - drop(context_lock); - self - } - - /// Configures when the application should automatically quit. - /// By default, [`QuitMode::Default`] is used. - pub fn with_quit_mode(self, mode: QuitMode) -> Self { - self.0.borrow_mut().quit_mode = mode; - self - } - - /// Start the application. The provided callback will be called once the - /// app is fully launched. - pub fn run(self, on_finish_launching: F) - where - F: 'static + FnOnce(&mut App), - { - 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); - })); - } - - /// Start the application for an embedder that drives the run loop itself. - /// - /// On ordinary platforms `Platform::run` blocks for the lifetime of the app, and the - /// app state is kept alive by [`Application::run`]'s stack frame. Embedded platforms — - /// where the run loop belongs to someone else, e.g. GPUI compiled into a Wasm guest, - /// or a GPUI view hosted inside a foreign native application — implement - /// `Platform::run` to invoke the launch callback and return immediately. This method - /// supports that shape: it returns an [`ApplicationHandle`] that keeps the app alive - /// and lets the embedder re-enter it whenever the external run loop yields control. - pub fn run_embedded(self, on_finish_launching: F) -> ApplicationHandle - where - F: 'static + FnOnce(&mut App), - { - 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); - })); - ApplicationHandle { app: self.0 } - } - - /// Register a handler to be invoked when the platform instructs the application - /// to open one or more URLs. - pub fn on_open_urls(&self, mut callback: F) -> &Self - where - F: 'static + FnMut(Vec), - { - self.0.borrow().platform.on_open_urls(Box::new(callback)); - self - } - - /// Invokes a handler when an already-running application is launched. - /// On macOS, this can occur when the application icon is double-clicked or the app is launched via the dock. - pub fn on_reopen(&self, mut callback: F) -> &Self - where - F: 'static + FnMut(&mut App), - { - 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 - } - - /// Returns a handle to the [`BackgroundExecutor`] associated with this app, which can be used to spawn futures in the background. - pub fn background_executor(&self) -> BackgroundExecutor { - self.0.borrow().background_executor.clone() - } - - /// Returns a handle to the [`ForegroundExecutor`] associated with this app, which can be used to spawn futures in the foreground. - pub fn foreground_executor(&self) -> ForegroundExecutor { - self.0.borrow().foreground_executor.clone() - } - - /// Returns a reference to the [`TextSystem`] associated with this app. - pub fn text_system(&self) -> Arc { - self.0.borrow().text_system.clone() - } - - /// Returns the file URL of the executable with the specified name in the application bundle - pub fn path_for_auxiliary_executable(&self, name: &str) -> Result { - self.0.borrow().path_for_auxiliary_executable(name) - } -} - -type Handler = Box bool + 'static>; -type Listener = Box bool + 'static>; -pub(crate) type KeystrokeObserver = - Box bool + 'static>; -type QuitHandler = Box LocalBoxFuture<'static, ()> + 'static>; -type WindowClosedHandler = Box; -type ReleaseListener = Box; -type NewEntityListener = Box, &mut App) + 'static>; - -/// Defines when the application should automatically quit. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub enum QuitMode { - /// Use [`QuitMode::Explicit`] on macOS and [`QuitMode::LastWindowClosed`] on other platforms. - #[default] - Default, - /// Quit automatically when the last window is closed. - LastWindowClosed, - /// Quit only when requested via [`App::quit`]. - Explicit, -} - -/// Controls when GPUI hides the mouse cursor in response to keyboard input. -/// -/// Restoration on mouse motion is handled by the platform layer; this enum -/// only describes the policy for *triggering* a hide. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub enum CursorHideMode { - /// Never hide the cursor automatically. - Never, - /// Hide on character-producing key presses (typing). - OnTyping, - /// Hide on character-producing key presses, *and* when a key binding - /// resolves to an action that consumes the keystroke. - #[default] - OnTypingAndAction, -} - -#[doc(hidden)] -#[derive(Clone, PartialEq, Eq)] -pub struct SystemWindowTab { - pub id: WindowId, - pub title: SharedString, - pub handle: AnyWindowHandle, - pub last_active_at: Instant, -} - -impl SystemWindowTab { - /// Create a new instance of the window tab. - pub fn new(title: SharedString, handle: AnyWindowHandle) -> Self { - Self { - id: handle.id, - title, - handle, - last_active_at: Instant::now(), - } - } -} - -/// A controller for managing window tabs. -#[derive(Default)] -pub struct SystemWindowTabController { - visible: Option, - tab_groups: FxHashMap>, -} - -impl Global for SystemWindowTabController {} - -impl SystemWindowTabController { - /// Create a new instance of the window tab controller. - pub fn new() -> Self { - Self { - visible: None, - tab_groups: FxHashMap::default(), - } - } - - /// Initialize the global window tab controller. - pub fn init(cx: &mut App) { - cx.set_global(SystemWindowTabController::new()); - } - - /// Get all tab groups. - pub fn tab_groups(&self) -> &FxHashMap> { - &self.tab_groups - } - - /// Get the next tab group window handle. - pub fn get_next_tab_group_window(cx: &mut App, id: WindowId) -> Option<&AnyWindowHandle> { - let controller = cx.global::(); - let current_group = controller - .tab_groups - .iter() - .find_map(|(group, tabs)| tabs.iter().find(|tab| tab.id == id).map(|_| group)); - - let current_group = current_group?; - // TODO: `.keys()` returns arbitrary order, what does "next" mean? - let mut group_ids: Vec<_> = controller.tab_groups.keys().collect(); - let idx = group_ids.iter().position(|g| *g == current_group)?; - let next_idx = (idx + 1) % group_ids.len(); - - controller - .tab_groups - .get(group_ids[next_idx]) - .and_then(|tabs| { - tabs.iter() - .max_by_key(|tab| tab.last_active_at) - .or_else(|| tabs.first()) - .map(|tab| &tab.handle) - }) - } - - /// Get the previous tab group window handle. - pub fn get_prev_tab_group_window(cx: &mut App, id: WindowId) -> Option<&AnyWindowHandle> { - let controller = cx.global::(); - let current_group = controller - .tab_groups - .iter() - .find_map(|(group, tabs)| tabs.iter().find(|tab| tab.id == id).map(|_| group)); - - let current_group = current_group?; - // TODO: `.keys()` returns arbitrary order, what does "previous" mean? - let mut group_ids: Vec<_> = controller.tab_groups.keys().collect(); - let idx = group_ids.iter().position(|g| *g == current_group)?; - let prev_idx = if idx == 0 { - group_ids.len() - 1 - } else { - idx - 1 - }; - - controller - .tab_groups - .get(group_ids[prev_idx]) - .and_then(|tabs| { - tabs.iter() - .max_by_key(|tab| tab.last_active_at) - .or_else(|| tabs.first()) - .map(|tab| &tab.handle) - }) - } - - /// Get all tabs in the same window. - pub fn tabs(&self, id: WindowId) -> Option<&Vec> { - self.tab_groups - .values() - .find(|tabs| tabs.iter().any(|tab| tab.id == id)) - } - - /// Initialize the visibility of the system window tab controller. - pub fn init_visible(cx: &mut App, visible: bool) { - let mut controller = cx.global_mut::(); - if controller.visible.is_none() { - controller.visible = Some(visible); - } - } - - /// Get the visibility of the system window tab controller. - pub fn is_visible(&self) -> bool { - self.visible.unwrap_or(false) - } - - /// Set the visibility of the system window tab controller. - pub fn set_visible(cx: &mut App, visible: bool) { - let mut controller = cx.global_mut::(); - controller.visible = Some(visible); - } - - /// Update the last active of a window. - pub fn update_last_active(cx: &mut App, id: WindowId) { - let mut controller = cx.global_mut::(); - for windows in controller.tab_groups.values_mut() { - for tab in windows.iter_mut() { - if tab.id == id { - tab.last_active_at = Instant::now(); - } - } - } - } - - /// Update the position of a tab within its group. - pub fn update_tab_position(cx: &mut App, id: WindowId, ix: usize) { - let mut controller = cx.global_mut::(); - for (_, windows) in controller.tab_groups.iter_mut() { - if let Some(current_pos) = windows.iter().position(|tab| tab.id == id) { - if ix < windows.len() && current_pos != ix { - let window_tab = windows.remove(current_pos); - windows.insert(ix, window_tab); - } - break; - } - } - } - - /// Update the title of a tab. - pub fn update_tab_title(cx: &mut App, id: WindowId, title: SharedString) { - let controller = cx.global::(); - let tab = controller - .tab_groups - .values() - .flat_map(|windows| windows.iter()) - .find(|tab| tab.id == id); - - if tab.map_or(true, |t| t.title == title) { - return; - } - - let mut controller = cx.global_mut::(); - for windows in controller.tab_groups.values_mut() { - for tab in windows.iter_mut() { - if tab.id == id { - tab.title = title; - return; - } - } - } - } - - /// Insert a tab into a tab group. - pub fn add_tab(cx: &mut App, id: WindowId, tabs: Vec) { - let mut controller = cx.global_mut::(); - let Some(tab) = tabs.iter().find(|tab| tab.id == id).cloned() else { - return; - }; - - let mut expected_tab_ids: Vec<_> = tabs - .iter() - .filter(|tab| tab.id != id) - .map(|tab| tab.id) - .sorted() - .collect(); - - let mut tab_group_id = None; - for (group_id, group_tabs) in &controller.tab_groups { - let tab_ids: Vec<_> = group_tabs.iter().map(|tab| tab.id).sorted().collect(); - if tab_ids == expected_tab_ids { - tab_group_id = Some(*group_id); - break; - } - } - - if let Some(tab_group_id) = tab_group_id { - if let Some(tabs) = controller.tab_groups.get_mut(&tab_group_id) { - tabs.push(tab); - } - } else { - let new_group_id = controller.tab_groups.len(); - controller.tab_groups.insert(new_group_id, tabs); - } - } - - /// Remove a tab from a tab group. - pub fn remove_tab(cx: &mut App, id: WindowId) -> Option { - let mut controller = cx.global_mut::(); - let mut removed_tab = None; - - controller.tab_groups.retain(|_, tabs| { - if let Some(pos) = tabs.iter().position(|tab| tab.id == id) { - removed_tab = Some(tabs.remove(pos)); - } - !tabs.is_empty() - }); - - removed_tab - } - - /// Move a tab to a new tab group. - pub fn move_tab_to_new_window(cx: &mut App, id: WindowId) { - let mut removed_tab = Self::remove_tab(cx, id); - let mut controller = cx.global_mut::(); - - if let Some(tab) = removed_tab { - let new_group_id = controller.tab_groups.keys().max().map_or(0, |k| k + 1); - controller.tab_groups.insert(new_group_id, vec![tab]); - } - } - - /// Merge all tab groups into a single group. - pub fn merge_all_windows(cx: &mut App, id: WindowId) { - let mut controller = cx.global_mut::(); - let Some(initial_tabs) = controller.tabs(id) else { - return; - }; - - let initial_tabs_len = initial_tabs.len(); - let mut all_tabs = initial_tabs.clone(); - - for (_, mut tabs) in controller.tab_groups.drain() { - tabs.retain(|tab| !all_tabs[..initial_tabs_len].contains(tab)); - all_tabs.extend(tabs); - } - - controller.tab_groups.insert(0, all_tabs); - } - - /// Selects the next tab in the tab group in the trailing direction. - pub fn select_next_tab(cx: &mut App, id: WindowId) { - let mut controller = cx.global_mut::(); - let Some(tabs) = controller.tabs(id) else { - return; - }; - - let current_index = tabs.iter().position(|tab| tab.id == id).unwrap(); - let next_index = (current_index + 1) % tabs.len(); - - let _ = &tabs[next_index].handle.update(cx, |_, window, _| { - window.activate_window(); - }); - } - - /// Selects the previous tab in the tab group in the leading direction. - pub fn select_previous_tab(cx: &mut App, id: WindowId) { - let mut controller = cx.global_mut::(); - let Some(tabs) = controller.tabs(id) else { - return; - }; - - let current_index = tabs.iter().position(|tab| tab.id == id).unwrap(); - let previous_index = if current_index == 0 { - tabs.len() - 1 - } else { - current_index - 1 - }; - - let _ = &tabs[previous_index].handle.update(cx, |_, window, _| { - window.activate_window(); - }); - } -} - -pub(crate) enum GpuiMode { - #[cfg(any(test, feature = "test-support"))] - Test { - skip_drawing: bool, - }, - Production, -} - -impl GpuiMode { - #[cfg(any(test, feature = "test-support"))] - pub fn test() -> Self { - GpuiMode::Test { - skip_drawing: false, - } - } - - #[inline] - pub(crate) fn skip_drawing(&self) -> bool { - match self { - #[cfg(any(test, feature = "test-support"))] - GpuiMode::Test { skip_drawing } => *skip_drawing, - GpuiMode::Production => false, - } - } -} - -struct PlatformOwnedDrag { - source_window: WindowId, - state: PlatformOwnedDragState, -} - -enum PlatformOwnedDragState { - Suspended(AnyDrag), - // A source-window drop consumes `active_drag` before AppKit ends the dragging session, so this - // marker can outlive the active drag and is cleaned up by `FileDropEvent::Ended`. - RestoredInSourceWindow, -} - -/// Contains the state of the full application, and passed as a reference to a variety of callbacks. -/// Other [Context] derefs to this type. -/// You need a reference to an `App` to access the state of a [Entity]. -pub struct App { - pub(crate) this: Weak, - pub(crate) platform: Rc, - text_system: Arc, - - pub(crate) actions: Rc, - pub(crate) active_drag: Option, - platform_owned_drag: Option, - pub(crate) background_executor: BackgroundExecutor, - pub(crate) foreground_executor: ForegroundExecutor, - #[cfg(feature = "profiler")] - foreground_journal: crate::profiler::journal::ForegroundJournal, - pub(crate) entities: EntityMap, - pub(crate) new_entity_observers: SubscriberSet, - pub(crate) windows: SlotMap>>, - pub(crate) window_handles: FxHashMap, - pub(crate) focus_handles: Arc, - pub(crate) keymap: Rc>, - pub(crate) keyboard_layout: Box, - pub(crate) keyboard_mapper: Rc, - pub(crate) global_action_listeners: - TypeIdHashMap>>, - pending_effects: VecDeque, - - pub(crate) observers: SubscriberSet, - pub(crate) event_listeners: SubscriberSet, - pub(crate) keystroke_observers: SubscriberSet<(), KeystrokeObserver>, - pub(crate) keystroke_interceptors: SubscriberSet<(), KeystrokeObserver>, - pub(crate) keyboard_layout_observers: SubscriberSet<(), Handler>, - pub(crate) thermal_state_observers: SubscriberSet<(), Handler>, - pub(crate) system_wake_observers: SubscriberSet<(), Handler>, - pub(crate) release_listeners: SubscriberSet, - pub(crate) global_observers: SubscriberSet, - pub(crate) quit_observers: SubscriberSet<(), QuitHandler>, - pub(crate) restart_observers: SubscriberSet<(), Handler>, - pub(crate) window_closed_observers: SubscriberSet<(), WindowClosedHandler>, - - /// Per-App element arena. This isolates element allocations between different - /// App instances (important for tests where multiple Apps run concurrently). - pub(crate) element_arena: RefCell, - /// Per-App event arena. - pub(crate) event_arena: Arena, - - // Drop globals last. We need to ensure all tasks owned by entities and - // callbacks are marked cancelled at this point as this will also shutdown - // the tokio runtime. As any task attempting to spawn a blocking tokio task, - // might panic. - pub(crate) globals_by_type: TypeIdHashMap>, - - // assets - pub(crate) loading_assets: FxHashMap<(TypeId, u64), Box>, - asset_source: Arc, - pub(crate) svg_renderer: SvgRenderer, - http_client: Arc, - - // below is plain data, the drop order is insignificant here - pub(crate) pending_notifications: FxHashSet, - pub(crate) pending_global_notifications: TypeIdHashSet, - pub(crate) restart_path: Option, - pub(crate) restart_arguments: Vec, - pub(crate) layout_id_buffer: Vec, // We recycle this memory across layout requests. - pub(crate) propagate_event: bool, - pub(crate) prompt_builder: Option, - pub(crate) window_invalidators_by_entity: - FxHashMap>, - pub(crate) tracked_entities: FxHashMap>, - pub(crate) current_window_by_entity: FxHashMap, - #[cfg(any(feature = "inspector", debug_assertions))] - pub(crate) inspector_renderer: Option, - #[cfg(any(feature = "inspector", debug_assertions))] - pub(crate) inspector_element_registry: InspectorElementRegistry, - #[cfg(any(test, feature = "test-support", debug_assertions))] - pub(crate) name: Option<&'static str>, - pub(crate) text_rendering_mode: Rc>, - - pub(crate) window_update_stack: Vec, - pub(crate) mode: GpuiMode, - pub(crate) cursor_hide_mode: CursorHideMode, - pub(crate) reduce_motion: bool, - /// Origin of the shared clock that phase-locks synced repeating animations. - pub(crate) synced_animation_epoch: Instant, - /// Whether the app was created by [`Application::new_inaccessible`]. No - /// accesskit APIs will be called when this flag is set. - pub(crate) accessibility_force_disabled: bool, - flushing_effects: bool, - pending_updates: usize, - quit_mode: QuitMode, - quitting: bool, - - // We need to ensure the leak detector drops last, after all tasks, callbacks and things have been dropped. - // Otherwise it may report false positives. - #[cfg(any(test, feature = "leak-detection"))] - _ref_counts: Arc>, -} - -impl App { - #[allow(clippy::new_ret_no_self)] - pub(crate) fn new_app( - platform: Rc, - asset_source: Arc, - http_client: Arc, - ) -> Rc { - let background_executor = platform.background_executor(); - let foreground_executor = platform.foreground_executor(); - assert!( - background_executor.is_main_thread(), - "must construct App on main thread" - ); - #[cfg(feature = "profiler")] - let foreground_journal = crate::profiler::journal::install_foreground_journal(); - let synced_animation_epoch = background_executor.now(); - - let text_system = Arc::new(TextSystem::new(platform.text_system())); - let entities = EntityMap::new(); - let keyboard_layout = platform.keyboard_layout(); - let keyboard_mapper = platform.keyboard_mapper(); - - #[cfg(any(test, feature = "leak-detection"))] - let _ref_counts = entities.ref_counts_drop_handle(); - - let app = Rc::new_cyclic(|this| AppCell { - app: RefCell::new(App { - this: this.clone(), - platform: platform.clone(), - text_system, - text_rendering_mode: Rc::new(Cell::new(TextRenderingMode::default())), - mode: GpuiMode::Production, - actions: Rc::new(ActionRegistry::default()), - flushing_effects: false, - pending_updates: 0, - active_drag: None, - platform_owned_drag: None, - background_executor, - foreground_executor, - #[cfg(feature = "profiler")] - foreground_journal, - svg_renderer: SvgRenderer::new(asset_source.clone()), - loading_assets: Default::default(), - asset_source, - http_client, - globals_by_type: Default::default(), - entities, - new_entity_observers: SubscriberSet::new(), - windows: SlotMap::with_key(), - window_update_stack: Vec::new(), - window_handles: FxHashMap::default(), - focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), - keymap: Rc::new(RefCell::new(Keymap::default())), - keyboard_layout, - keyboard_mapper, - global_action_listeners: Default::default(), - pending_effects: VecDeque::new(), - pending_notifications: FxHashSet::default(), - pending_global_notifications: Default::default(), - observers: SubscriberSet::new(), - tracked_entities: FxHashMap::default(), - window_invalidators_by_entity: FxHashMap::default(), - current_window_by_entity: FxHashMap::default(), - event_listeners: SubscriberSet::new(), - release_listeners: SubscriberSet::new(), - keystroke_observers: SubscriberSet::new(), - keystroke_interceptors: SubscriberSet::new(), - keyboard_layout_observers: SubscriberSet::new(), - thermal_state_observers: SubscriberSet::new(), - system_wake_observers: SubscriberSet::new(), - global_observers: SubscriberSet::new(), - quit_observers: SubscriberSet::new(), - restart_observers: SubscriberSet::new(), - restart_path: None, - restart_arguments: Vec::new(), - window_closed_observers: SubscriberSet::new(), - layout_id_buffer: Default::default(), - propagate_event: true, - prompt_builder: Some(PromptBuilder::Default), - #[cfg(any(feature = "inspector", debug_assertions))] - inspector_renderer: None, - #[cfg(any(feature = "inspector", debug_assertions))] - inspector_element_registry: InspectorElementRegistry::default(), - quit_mode: QuitMode::default(), - quitting: false, - cursor_hide_mode: CursorHideMode::default(), - reduce_motion: false, - synced_animation_epoch, - accessibility_force_disabled: false, - - #[cfg(any(test, feature = "test-support", debug_assertions))] - name: None, - element_arena: RefCell::new(Arena::new(1024 * 1024)), - event_arena: Arena::new(1024 * 1024), - - #[cfg(any(test, feature = "leak-detection"))] - _ref_counts, - }), - }); - - init_app_menus(platform.as_ref(), &app.borrow()); - SystemWindowTabController::init(&mut app.borrow_mut()); - - platform.on_keyboard_layout_change(Box::new({ - let app = Rc::downgrade(&app); - move || { - if let Some(app) = app.upgrade() { - let cx = &mut app.borrow_mut(); - cx.keyboard_layout = cx.platform.keyboard_layout(); - cx.keyboard_mapper = cx.platform.keyboard_mapper(); - cx.keyboard_layout_observers - .clone() - .retain(&(), move |callback| (callback)(cx)); - } - } - })); - - platform.on_thermal_state_change(Box::new({ - let app = Rc::downgrade(&app); - move || { - if let Some(app) = app.upgrade() { - let cx = &mut app.borrow_mut(); - cx.thermal_state_observers - .clone() - .retain(&(), move |callback| (callback)(cx)); - } - } - })); - - platform.on_system_wake(Box::new({ - let app = Rc::downgrade(&app); - move || { - if let Some(app) = app.upgrade() { - let cx = &mut app.borrow_mut(); - cx.system_wake_observers - .clone() - .retain(&(), move |callback| (callback)(cx)); - } - } - })); - - platform.on_quit(Box::new({ - let cx = Rc::downgrade(&app); - move || { - let Some(cx) = cx.upgrade() else { - return true; - }; - match cx.try_borrow_mut() { - Ok(mut cx) => { - cx.shutdown(); - true - } - Err(_) => { - // Quit was requested while the AppCell was borrowed, so we can't shut down synchronously. - // The platform decides how to proceed. - false - } - } - } - })); - - app - } - - #[doc(hidden)] - pub fn ref_counts_drop_handle(&self) -> impl Sized + use<> { - self.entities.ref_counts_drop_handle() - } - - /// Captures a snapshot of all entities that currently have alive handles. - /// - /// The returned [`LeakDetectorSnapshot`] can later be passed to - /// [`assert_no_new_leaks`](Self::assert_no_new_leaks) to verify that no - /// entities created after the snapshot are still alive. - #[cfg(any(test, feature = "leak-detection"))] - pub fn leak_detector_snapshot(&self) -> LeakDetectorSnapshot { - self.entities.leak_detector_snapshot() - } - - /// Asserts that no entities created after `snapshot` still have alive handles. - /// - /// Entities that were already tracked at the time of the snapshot are ignored, - /// even if they still have handles. Only *new* entities (those whose - /// `EntityId` was not present in the snapshot) are considered leaks. - /// - /// # Panics - /// - /// Panics if any new entity handles exist. The panic message lists every - /// leaked entity with its type name, and includes allocation-site backtraces - /// when `LEAK_BACKTRACE` is set. - #[cfg(any(test, feature = "leak-detection"))] - pub fn assert_no_new_leaks(&self, snapshot: &LeakDetectorSnapshot) { - self.entities.assert_no_new_leaks(snapshot) - } - - /// Quit the application gracefully. - /// - /// Native applications give handlers registered with [`Context::on_app_quit`] - /// [`SHUTDOWN_TIMEOUT`] to complete. WebAssembly runs them asynchronously as best-effort cleanup - /// because its event-loop thread cannot block. - 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.window_handles.clear(); - self.flush_effects(); - self.quitting = true; - - let futures = futures::future::join_all(futures); - #[cfg(not(target_family = "wasm"))] - if self - .foreground_executor - .block_with_timeout(SHUTDOWN_TIMEOUT, futures) - .is_err() - { - log::error!("timed out waiting on app_will_quit"); - } - #[cfg(target_family = "wasm")] - self.foreground_executor.spawn(futures).detach(); - - self.quitting = false; - } - - /// Get the id of the current keyboard layout - pub fn keyboard_layout(&self) -> &dyn PlatformKeyboardLayout { - self.keyboard_layout.as_ref() - } - - /// Get the current keyboard mapper. - pub fn keyboard_mapper(&self) -> &Rc { - &self.keyboard_mapper - } - - /// Invokes a handler when the current keyboard layout changes - pub fn on_keyboard_layout_change(&self, mut callback: F) -> Subscription - where - F: 'static + FnMut(&mut App), - { - let (subscription, activate) = self.keyboard_layout_observers.insert( - (), - Box::new(move |cx| { - callback(cx); - true - }), - ); - activate(); - subscription - } - - /// Gracefully quit the application via the platform's standard routine. - pub fn quit(&self) { - self.platform.quit(); - } - - /// Returns the current policy for hiding the cursor in response to - /// keyboard input. - pub fn cursor_hide_mode(&self) -> CursorHideMode { - self.cursor_hide_mode - } - - /// Sets the policy controlling when GPUI hides the cursor in response - /// to keyboard input. - pub fn set_cursor_hide_mode(&mut self, mode: CursorHideMode) { - self.cursor_hide_mode = mode; - } - - /// Returns whether the cursor is currently visible according to the - /// platform. This will report `false` after a keyboard input has hidden - /// the cursor and the user has not yet moved the mouse to restore it. - /// - /// See [`App::set_cursor_hide_mode`]. - pub fn is_cursor_visible(&self) -> bool { - self.platform.is_cursor_visible() - } - - /// Returns whether non-essential animations (e.g. loading spinners) should - /// be rendered in a static state instead of animating. - pub fn reduce_motion(&self) -> bool { - self.reduce_motion - } - - /// Sets whether non-essential animations (e.g. loading spinners) should be - /// rendered in a static state instead of animating. - pub fn set_reduce_motion(&mut self, reduce_motion: bool) { - if self.reduce_motion != reduce_motion { - self.reduce_motion = reduce_motion; - self.refresh_windows(); - } - } - - /// 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_windows(&mut self) { - self.pending_effects.push_back(Effect::RefreshWindows); - } - - pub(crate) fn update(&mut self, update: impl FnOnce(&mut Self) -> R) -> R { - self.start_update(); - let result = update(self); - self.finish_update(); - result - } - - pub(crate) fn start_update(&mut self) { - self.pending_updates += 1; - } - - pub(crate) fn finish_update(&mut self) { - if !self.flushing_effects && self.pending_updates == 1 { - self.flushing_effects = true; - self.flush_effects(); - self.flushing_effects = false; - } - self.pending_updates -= 1; - } - - /// Arrange a callback to be invoked when the given entity calls `notify` on its respective context. - pub fn observe( - &mut self, - entity: &Entity, - mut on_notify: impl FnMut(Entity, &mut App) + 'static, - ) -> Subscription - where - W: 'static, - { - self.observe_internal(entity, move |e, cx| { - on_notify(e, cx); - true - }) - } - - pub(crate) fn detect_accessed_entities( - &mut self, - callback: impl FnOnce(&mut App) -> R, - ) -> (R, FxHashSet) { - let accessed_entities_start = self.entities.accessed_entities.get_mut().clone(); - let result = callback(self); - let entities_accessed_in_callback = self - .entities - .accessed_entities - .get_mut() - .difference(&accessed_entities_start) - .copied() - .collect::>(); - (result, entities_accessed_in_callback) - } - - pub(crate) fn record_entities_accessed( - &mut self, - window_handle: AnyWindowHandle, - invalidator: WindowInvalidator, - entities: &FxHashSet, - ) { - let mut tracked_entities = - std::mem::take(self.tracked_entities.entry(window_handle.id).or_default()); - for entity in tracked_entities.iter() { - self.window_invalidators_by_entity - .entry(*entity) - .and_modify(|windows| { - windows.remove(&window_handle.id); - }); - } - for entity in entities.iter() { - self.window_invalidators_by_entity - .entry(*entity) - .or_default() - .insert(window_handle.id, invalidator.clone()); - self.current_window_by_entity - .insert(*entity, window_handle.id); - } - tracked_entities.clear(); - tracked_entities.extend(entities.iter().copied()); - self.tracked_entities - .insert(window_handle.id, tracked_entities); - } - - pub(crate) fn new_observer(&mut self, key: EntityId, value: Handler) -> Subscription { - let (subscription, activate) = self.observers.insert(key, value); - self.defer(move |_| activate()); - subscription - } - - pub(crate) fn observe_internal( - &mut self, - entity: &Entity, - mut on_notify: impl FnMut(Entity, &mut App) -> bool + 'static, - ) -> Subscription - where - W: 'static, - { - let entity_id = entity.entity_id(); - let handle = entity.downgrade(); - self.new_observer( - entity_id, - Box::new(move |cx| { - if let Some(entity) = handle.upgrade() { - on_notify(entity, cx) - } else { - false - } - }), - ) - } - - /// Arrange for the given callback to be invoked whenever the given entity emits an event of a given type. - /// The callback is provided a handle to the emitting entity and a reference to the emitted event. - pub fn subscribe( - &mut self, - entity: &Entity, - mut on_event: impl FnMut(Entity, &Event, &mut App) + 'static, - ) -> Subscription - where - T: 'static + EventEmitter, - Event: 'static, - { - self.subscribe_internal(entity, move |entity, event, cx| { - on_event(entity, event, cx); - true - }) - } - - pub(crate) fn new_subscription( - &mut self, - key: EntityId, - value: (TypeId, Listener), - ) -> Subscription { - let (subscription, activate) = self.event_listeners.insert(key, value); - self.defer(move |_| activate()); - subscription - } - pub(crate) fn subscribe_internal( - &mut self, - entity: &Entity, - mut on_event: impl FnMut(Entity, &Evt, &mut App) -> bool + 'static, - ) -> Subscription - where - T: 'static + EventEmitter, - Evt: 'static, - { - let entity_id = entity.entity_id(); - let handle = entity.downgrade(); - self.new_subscription( - entity_id, - ( - TypeId::of::(), - Box::new(move |event, cx| { - let event: &Evt = event.downcast_ref().expect("invalid event type"); - if let Some(entity) = handle.upgrade() { - on_event(entity, event, cx) - } else { - false - } - }), - ), - ) - } - - /// Returns handles to all open windows in the application. - /// Each handle could be downcast to a handle typed for the root view of that window. - /// To find all windows of a given type, you could filter on - pub fn windows(&self) -> Vec { - self.windows - .keys() - .flat_map(|window_id| self.window_handles.get(&window_id).copied()) - .collect() - } - - /// Returns the window handles ordered by their appearance on screen, front to back. - /// - /// The first window in the returned list is the active/topmost window of the application. - /// - /// This method returns None if the platform doesn't implement the method yet. - pub fn window_stack(&self) -> Option> { - self.platform.window_stack() - } - - /// Returns a handle to the window that is currently focused at the platform level, if one exists. - pub fn active_window(&self) -> Option { - 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 `Window`, which can be used to interact with window-specific - /// functionality. - pub fn open_window( - &mut self, - options: crate::WindowOptions, - build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity, - ) -> anyhow::Result> { - self.update(|cx| { - let id = cx.windows.insert(None); - let handle = WindowHandle::new(id); - match Window::new(handle.into(), options, cx) { - Ok(mut window) => { - cx.window_update_stack.push(id); - let root_view = build_root_view(&mut window, cx); - cx.window_update_stack.pop(); - window.root.replace(root_view.into()); - window.defer(cx, |window: &mut Window, cx| window.appearance_changed(cx)); - - // allow a window to draw at least once before returning - // this didn't cause any issues on non windows platforms as it seems we always won the race to on_request_frame - // on windows we quite frequently lose the race and return a window that has never rendered, which leads to a crash - // where DispatchTree::root_node_id asserts on empty nodes - let clear = window.draw(cx); - clear.clear(cx); - - cx.window_handles.insert(id, window.handle); - cx.windows.get_mut(id).unwrap().replace(Box::new(window)); - Ok(handle) - } - Err(e) => { - cx.windows.remove(id); - Err(e) - } - } - }) - } - - /// 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); - } - - /// Hide the application at the platform level. - pub fn hide(&self) { - self.platform.hide(); - } - - /// Hide other applications at the platform level. - pub fn hide_other_apps(&self) { - self.platform.hide_other_apps(); - } - - /// Unhide other applications at the platform level. - pub fn unhide_other_apps(&self) { - self.platform.unhide_other_apps(); - } - - /// Returns the list of currently active displays. - pub fn displays(&self) -> Vec> { - self.platform.displays() - } - - /// Returns the primary display that will be used for new windows. - pub fn primary_display(&self) -> Option> { - self.platform.primary_display() - } - - /// Returns whether `screen_capture_sources` may work. - pub fn is_screen_capture_supported(&self) -> bool { - self.platform.is_screen_capture_supported() - } - - /// Returns a list of available screen capture sources. - pub fn screen_capture_sources( - &self, - ) -> oneshot::Receiver>>> { - self.platform.screen_capture_sources() - } - - /// Returns the display with the given ID, if one exists. - pub fn find_display(&self, id: DisplayId) -> Option> { - self.displays() - .iter() - .find(|display| display.id() == id) - .cloned() - } - - /// Returns the current thermal state of the system. - pub fn thermal_state(&self) -> ThermalState { - self.platform.thermal_state() - } - - /// Invokes a handler when the thermal state changes - pub fn on_thermal_state_change(&self, mut callback: F) -> Subscription - where - F: 'static + FnMut(&mut App), - { - let (subscription, activate) = self.thermal_state_observers.insert( - (), - Box::new(move |cx| { - callback(cx); - true - }), - ); - activate(); - subscription - } - - /// Invokes a handler when the system wakes from sleep. - pub fn on_system_wake(&self, mut callback: F) -> Subscription - where - F: 'static + FnMut(&mut App), - { - let (subscription, activate) = self.system_wake_observers.insert( - (), - Box::new(move |cx| { - callback(cx); - true - }), - ); - activate(); - subscription - } - - /// Returns the appearance of the application's windows. - pub fn window_appearance(&self) -> WindowAppearance { - self.platform.window_appearance() - } - - /// Overrides the appearance (light/dark) applied to the app's windows, independent of - /// the OS-wide setting. Pass `None` to clear the override and follow the system again. - /// The current value is reported by [`App::window_appearance`]. - /// - /// On macOS this sets the underlying `NSApplication.appearance`, which controls the - /// native window chrome (the window border and titlebar) of every window. Use this - /// when the app uses a dark theme while the system is in light mode (or vice versa) - /// so the window edges render to match the theme. While an appearance is forced, - /// windows stop tracking system light/dark changes; pass `None` to resume following - /// the system. On other platforms this is a no-op. - pub fn set_window_appearance(&self, appearance: Option) { - self.platform.set_window_appearance(appearance); - } - - /// Returns the window button layout configuration when supported. - pub fn button_layout(&self) -> Option { - self.platform.button_layout() - } - - /// Reads data from the platform clipboard. - pub fn read_from_clipboard(&self) -> Option { - self.platform.read_from_clipboard() - } - - /// Reads data from the platform clipboard, resolving once the contents - /// are available. - /// - /// Prefer this over [`App::read_from_clipboard`] in code that can await: - /// on platforms where clipboard access is asynchronous and - /// permission-gated (e.g. web), the synchronous read always returns - /// `None` while this method performs a real read. - pub fn read_from_clipboard_async( - &self, - ) -> Task, ClipboardReadError>> { - self.platform.read_from_clipboard_async() - } - - /// Sets the text rendering mode for the application. - pub fn set_text_rendering_mode(&mut self, mode: TextRenderingMode) { - self.text_rendering_mode.set(mode); - } - - /// Returns the current text rendering mode for the application. - pub fn text_rendering_mode(&self) -> TextRenderingMode { - self.text_rendering_mode.get() - } - - /// Writes data to the platform clipboard. - pub fn write_to_clipboard(&self, item: ClipboardItem) { - self.platform.write_to_clipboard(item) - } - - /// Reads data from the primary selection buffer. - /// Only available on Linux. - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - pub fn read_from_primary(&self) -> Option { - self.platform.read_from_primary() - } - - /// Writes data to the primary selection buffer. - /// Only available on Linux. - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - pub fn write_to_primary(&self, item: ClipboardItem) { - self.platform.write_to_primary(item) - } - - /// Reads data from macOS's "Find" pasteboard. - /// - /// Used to share the current search string between apps. - /// - /// https://developer.apple.com/documentation/appkit/nspasteboard/name-swift.struct/find - #[cfg(target_os = "macos")] - pub fn read_from_find_pasteboard(&self) -> Option { - self.platform.read_from_find_pasteboard() - } - - /// Writes data to macOS's "Find" pasteboard. - /// - /// Used to share the current search string between apps. - /// - /// https://developer.apple.com/documentation/appkit/nspasteboard/name-swift.struct/find - #[cfg(target_os = "macos")] - pub fn write_to_find_pasteboard(&self, item: ClipboardItem) { - self.platform.write_to_find_pasteboard(item) - } - - /// Writes credentials to the platform keychain. - pub fn write_credentials( - &self, - url: &str, - username: &str, - password: &[u8], - ) -> Task> { - self.platform.write_credentials(url, username, password) - } - - /// Reads credentials from the platform keychain. - pub fn read_credentials(&self, url: &str) -> Task)>>> { - self.platform.read_credentials(url) - } - - /// Deletes credentials from the platform keychain. - pub fn delete_credentials(&self, url: &str) -> Task> { - 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); - } - - /// Registers the given URL scheme (e.g. `zed` for `zed://` urls) to be - /// opened by the current app. - /// - /// On some platforms (e.g. macOS) you may be able to register URL schemes - /// as part of app distribution, but this method exists to let you register - /// schemes at runtime. - pub fn register_url_scheme(&self, scheme: &str) -> Task> { - self.platform.register_url_scheme(scheme) - } - - /// Sets the application's process-wide identity and user-visible name. - /// - /// The identifier is used for platform identity mechanisms such as the - /// Windows AppUserModelID. The name is used wherever the operating system - /// presents the application to the user. Call this once, early in startup, - /// before opening windows or posting notifications. - pub fn set_app_identity(&self, identifier: &str, name: &str) { - self.platform.set_app_identity(identifier, name); - } - - /// Posts a notification to the operating system's notification center. - /// - /// Posting a notification whose [`SystemNotification::tag`] matches an - /// earlier one replaces that notification where the platform supports it. - /// No-op on platforms without notification support, or when delivery is - /// unavailable (e.g. authorization was denied). - pub fn show_system_notification(&self, notification: SystemNotification) { - self.platform.show_system_notification(notification); - } - - /// Removes the delivered or pending notification with this tag. - /// - /// Best-effort: some platforms cannot retract a notification once shown, - /// in which case it ages out of the notification center on its own. - pub fn dismiss_system_notification(&self, tag: &str) { - self.platform.dismiss_system_notification(tag); - } - - /// Registers the handler invoked when the user activates a system - /// notification, either by clicking its body or one of its action - /// buttons. Subsequent registrations replace the handler. - pub fn on_system_notification_response(&self, mut callback: F) - where - F: 'static + FnMut(SystemNotificationResponse, &mut App), - { - let this = self.this.clone(); - self.platform - .on_system_notification_response(Box::new(move |response| { - if let Some(app) = this.upgrade() { - callback(response, &mut app.borrow_mut()); - } - })); - } - - /// Returns the full pathname of the current app bundle. - /// - /// Returns an error if the app is not being run from a bundle. - pub fn app_path(&self) -> Result { - self.platform.app_path() - } - - /// On Linux, returns the name of the compositor in use. - /// - /// Returns an empty string on other platforms. - pub fn compositor_name(&self) -> &'static str { - self.platform.compositor_name() - } - - /// Returns the file URL of the executable with the specified name in the application bundle - pub fn path_for_auxiliary_executable(&self, name: &str) -> Result { - self.platform.path_for_auxiliary_executable(name) - } - - /// Displays a platform modal for selecting paths. - /// - /// When one or more paths are selected, they'll be relayed asynchronously via the returned oneshot channel. - /// If cancelled, a `None` will be relayed instead. - /// May return an error on Linux if the file picker couldn't be opened. - pub fn prompt_for_paths( - &self, - options: PathPromptOptions, - ) -> oneshot::Receiver>>> { - self.platform.prompt_for_paths(options) - } - - /// Displays a platform modal for selecting a new path where a file can be saved. - /// - /// The provided directory will be used to set the initial location. - /// When a path is selected, it is relayed asynchronously via the returned oneshot channel. - /// If cancelled, a `None` will be relayed instead. - /// May return an error on Linux if the file picker couldn't be opened. - pub fn prompt_for_new_path( - &self, - directory: &Path, - suggested_name: Option<&str>, - ) -> oneshot::Receiver>> { - self.platform.prompt_for_new_path(directory, suggested_name) - } - - /// Reveals the specified path at the platform level, such as in Finder on macOS. - pub fn reveal_path(&self, path: &Path) { - self.platform.reveal_path(path) - } - - /// Opens the specified path with the system's default application. - pub fn open_with_system(&self, path: &Path) { - self.platform.open_with_system(path) - } - - /// Returns whether the user has configured scrollbars to auto-hide at the platform level. - pub fn should_auto_hide_scrollbars(&self) -> bool { - self.platform.should_auto_hide_scrollbars() - } - - /// Restarts the application. - pub fn restart(&mut self) { - self.restart_observers - .clone() - .retain(&(), |observer| observer(self)); - self.platform.restart( - self.restart_path.take(), - std::mem::take(&mut self.restart_arguments), - ) - } - - /// Sets the path to use when restarting the application. - pub fn set_restart_path(&mut self, path: PathBuf) { - self.restart_path = Some(path); - } - - /// Returns the HTTP client for the application. - pub fn http_client(&self) -> Arc { - self.http_client.clone() - } - - /// Sets the HTTP client for the application. - pub fn set_http_client(&mut self, new_client: Arc) { - self.http_client = new_client; - } - - /// Configures when the application should automatically quit. - /// By default, [`QuitMode::Default`] is used. - pub fn set_quit_mode(&mut self, mode: QuitMode) { - self.quit_mode = mode; - } - - /// Returns the SVG renderer used by the application. - pub fn svg_renderer(&self) -> SvgRenderer { - self.svg_renderer.clone() - } - - 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 [`App::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::RefreshWindows => { - self.apply_refresh_effect(); - } - - Effect::NotifyGlobalObservers { global_type } => { - self.apply_notify_global_observers_effect(global_type); - } - - Effect::Defer { callback } => { - self.apply_defer_effect(callback); - } - Effect::EntityCreated { - entity, - tid, - window, - } => { - self.apply_entity_created_effect(entity, tid, window); - } - } - } else { - #[cfg(any(test, feature = "test-support", feature = "bench-support"))] - for window in self - .windows - .values() - .filter_map(|window| { - let window = window.as_deref()?; - window.invalidator.is_dirty().then_some(window.handle) - }) - .collect::>() - { - self.update_window(window, |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - } - - if self.pending_effects.is_empty() { - for window in self.windows.values().filter_map(|window| window.as_deref()) { - if window.invalidator.is_dirty() - || window.needs_present.get() - || !window.next_frame_callbacks.borrow().is_empty() - { - window.platform_window.schedule_frame(); - } - } - - self.event_arena.clear(); - 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); - self.window_invalidators_by_entity.remove(&entity_id); - self.current_window_by_entity.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) { - self.focus_handles - .clone() - .write() - .retain(|handle_id, focus| { - if focus.ref_count.load(SeqCst) == 0 { - for window_handle in self.windows() { - window_handle - .update(self, |_, window, cx| { - if window.focus == Some(handle_id) { - window.blur(cx); - } - }) - .unwrap(); - } - false - } else { - true - } - }); - } - - 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: &dyn Any) { - self.event_listeners - .clone() - .retain(&emitter, |(stored_type, handler)| { - if *stored_type == event_type { - handler(event, self) - } else { - true - } - }); - } - - fn apply_refresh_effect(&mut self) { - for window in self.windows.values_mut() { - if let Some(window) = window.as_deref_mut() { - window.refreshing = true; - window.invalidator.set_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) { - callback(self); - } - - fn apply_entity_created_effect( - &mut self, - entity: AnyEntity, - tid: TypeId, - window: Option, - ) { - // Seed the entity's current window from its creation context so - // `with_window` resolves correctly before the entity has ever been - // rendered. - if let Some(id) = window { - self.current_window_by_entity.insert(entity.entity_id(), id); - } - - self.new_entity_observers.clone().retain(&tid, |observer| { - if let Some(id) = window { - self.update_window_id(id, { - let entity = entity.clone(); - |_, window, cx| (observer)(entity, &mut Some(window), cx) - }) - .expect("All windows should be off the stack when flushing effects"); - } else { - (observer)(entity.clone(), &mut None, self) - } - true - }); - } - - /// Run `f` against the entity's *current* window — the most recently - /// rendered window that referenced the entity, or its creation window if - /// it has yet to be rendered. Returns `None` if the entity has no - /// current window, or if that window has been closed, or if it is - /// already on the update stack. - pub fn with_window( - &mut self, - entity_id: EntityId, - f: impl FnOnce(&mut Window, &mut App) -> R, - ) -> Option { - let window_id = *self.current_window_by_entity.get(&entity_id)?; - self.update_window_id(window_id, |_, window, cx| f(window, cx)) - .ok() - } - - fn ensure_window(&mut self, entity_id: EntityId, window: WindowId) { - self.current_window_by_entity - .entry(entity_id) - .or_insert(window); - } - - pub(crate) fn update_window_id(&mut self, id: WindowId, update: F) -> Result - where - F: FnOnce(AnyView, &mut Window, &mut App) -> T, - { - self.update(|cx| { - let mut window = cx.windows.get_mut(id)?.take()?; - - let root_view = window.root.clone().unwrap(); - - cx.window_update_stack.push(window.handle.id); - let result = update(root_view, &mut window, cx); - fn trail(id: WindowId, window: Box, cx: &mut App) -> Option<()> { - cx.window_update_stack.pop(); - - if window.removed { - cx.end_platform_drag(id); - cx.window_handles.remove(&id); - cx.windows.remove(id); - if let Some(tracked) = cx.tracked_entities.remove(&id) { - for entity_id in tracked { - if let Some(windows) = - cx.window_invalidators_by_entity.get_mut(&entity_id) - { - windows.remove(&id); - } - if cx.current_window_by_entity.get(&entity_id) == Some(&id) { - cx.current_window_by_entity.remove(&entity_id); - } - } - } - - cx.window_closed_observers.clone().retain(&(), |callback| { - callback(cx, id); - true - }); - - let quit_on_empty = match cx.quit_mode { - QuitMode::Explicit => false, - QuitMode::LastWindowClosed => true, - QuitMode::Default => cfg!(not(target_os = "macos")), - }; - - if quit_on_empty && cx.windows.is_empty() { - cx.quit(); - } - } else { - cx.windows.get_mut(id)?.replace(window); - } - Some(()) - } - trail(id, window, cx)?; - - Some(result) - }) - .context("window not found") - } - - /// Creates an `AsyncApp`, which can be cloned and has a static lifetime - /// so it can be held across `await` points. - pub fn to_async(&self) -> AsyncApp { - AsyncApp { - app: 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 { - if self.quitting { - panic!("Can't spawn on main thread after on_app_quit") - }; - &self.foreground_executor - } - - /// Returns the foreground work journal for this app's foreground thread. - /// Apps constructed on the same thread share the stream. - #[cfg(feature = "profiler")] - pub fn foreground_journal(&self) -> crate::profiler::journal::ForegroundJournal { - self.foreground_journal.clone() - } - - /// Spawns the future returned by the given function on the main thread. The closure will be invoked - /// with [AsyncApp], which allows the application state to be accessed across await points. - #[track_caller] - pub fn spawn(&self, f: AsyncFn) -> Task - where - AsyncFn: AsyncFnOnce(&mut AsyncApp) -> R + 'static, - R: 'static, - { - if self.quitting { - debug_panic!("Can't spawn on main thread after on_app_quit") - }; - - let mut cx = self.to_async(); - - self.foreground_executor - .spawn(async move { f(&mut cx).await }.boxed_local()) - } - - /// Spawns the future returned by the given function on the main thread with - /// the given priority. The closure will be invoked with [AsyncApp], which - /// allows the application state to be accessed across await points. - pub fn spawn_with_priority(&self, priority: Priority, f: AsyncFn) -> Task - where - AsyncFn: AsyncFnOnce(&mut AsyncApp) -> R + 'static, - R: 'static, - { - if self.quitting { - debug_panic!("Can't spawn on main thread after on_app_quit") - }; - - let mut cx = self.to_async(); - - self.foreground_executor - .spawn_with_priority(priority, async move { f(&mut cx).await }.boxed_local()) - } - - /// 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 App) + '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 { - &self.asset_source - } - - /// Accessor for the text system. - pub fn text_system(&self) -> &Arc { - &self.text_system - } - - /// Check whether a global of the given type has been assigned. - pub fn has_global(&self) -> bool { - self.globals_by_type.contains_key(&TypeId::of::()) - } - - /// Access the global of the given type. Panics if a global for that type has not been assigned. - #[track_caller] - pub fn global(&self) -> &G { - self.globals_by_type - .get(&TypeId::of::()) - .map(|any_state| any_state.downcast_ref::().unwrap()) - .unwrap_or_else(|| panic!("no state of type {} exists", type_name::())) - } - - /// Access the global of the given type if a value has been assigned. - pub fn try_global(&self) -> Option<&G> { - self.globals_by_type - .get(&TypeId::of::()) - .map(|any_state| any_state.downcast_ref::().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(&mut self) -> &mut G { - let global_type = TypeId::of::(); - self.push_effect(Effect::NotifyGlobalObservers { global_type }); - self.globals_by_type - .get_mut(&global_type) - .and_then(|any_state| any_state.downcast_mut::()) - .unwrap_or_else(|| panic!("no state of type {} exists", type_name::())) - } - - /// 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(&mut self) -> &mut G { - let global_type = TypeId::of::(); - self.push_effect(Effect::NotifyGlobalObservers { global_type }); - self.globals_by_type - .entry(global_type) - .or_insert_with(|| Box::::default()) - .downcast_mut::() - .unwrap() - } - - /// Sets the value of the global of the given type. - pub fn set_global(&mut self, global: G) { - let global_type = TypeId::of::(); - 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(&mut self) -> G { - let global_type = TypeId::of::(); - self.push_effect(Effect::NotifyGlobalObservers { global_type }); - *self - .globals_by_type - .remove(&global_type) - .unwrap_or_else(|| panic!("no global added for {}", type_name::())) - .downcast() - .unwrap() - } - - /// Register a callback to be invoked when a global of the given type is updated. - pub fn observe_global( - &mut self, - mut f: impl FnMut(&mut Self) + 'static, - ) -> Subscription { - let (subscription, activate) = self.global_observers.insert( - TypeId::of::(), - Box::new(move |cx| { - f(cx); - true - }), - ); - self.defer(move |_| activate()); - subscription - } - - /// Move the global of the given type to the stack. - #[track_caller] - pub(crate) fn lease_global(&mut self) -> GlobalLease { - GlobalLease::new( - self.globals_by_type - .remove(&TypeId::of::()) - .with_context(|| format!("no global registered of type {}", type_name::())) - .unwrap(), - ) - } - - /// Restore the global of the given type after it is moved to the stack. - pub(crate) fn end_global_lease(&mut self, lease: GlobalLease) { - let global_type = TypeId::of::(); - - self.push_effect(Effect::NotifyGlobalObservers { global_type }); - self.globals_by_type.insert(global_type, lease.global); - } - - pub(crate) fn new_entity_observer( - &self, - key: TypeId, - value: NewEntityListener, - ) -> Subscription { - let (subscription, activate) = self.new_entity_observers.insert(key, value); - activate(); - subscription - } - - /// Arrange for the given function to be invoked whenever a view of the specified type is created. - /// The function will be passed a mutable reference to the view along with an appropriate context. - pub fn observe_new( - &self, - on_new: impl 'static + Fn(&mut T, Option<&mut Window>, &mut Context), - ) -> Subscription { - self.new_entity_observer( - TypeId::of::(), - Box::new( - move |any_entity: AnyEntity, window: &mut Option<&mut Window>, cx: &mut App| { - any_entity - .downcast::() - .unwrap() - .update(cx, |entity_state, cx| { - on_new(entity_state, window.as_deref_mut(), cx) - }) - }, - ), - ) - } - - /// Observe the release of a entity. The callback is invoked after the entity - /// has no more strong references but before it has been dropped. - pub fn observe_release( - &self, - handle: &Entity, - on_release: impl FnOnce(&mut T, &mut App) + 'static, - ) -> Subscription - where - 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 - } - - /// Observe the release of a entity. The callback is invoked after the entity - /// has no more strong references but before it has been dropped. - pub fn observe_release_in( - &self, - handle: &Entity, - window: &Window, - on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static, - ) -> Subscription - where - T: 'static, - { - let window_handle = window.handle; - self.observe_release(handle, move |entity, cx| { - let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx)); - }) - } - - /// Register a callback to be invoked when a keystroke is received by the application - /// in any window. Note that this fires after all other action and event mechanisms have resolved - /// and that this API will not be invoked if the event's propagation is stopped. - pub fn observe_keystrokes( - &mut self, - mut f: impl FnMut(&KeystrokeEvent, &mut Window, &mut App) + 'static, - ) -> Subscription { - fn inner( - keystroke_observers: &SubscriberSet<(), KeystrokeObserver>, - handler: KeystrokeObserver, - ) -> Subscription { - let (subscription, activate) = keystroke_observers.insert((), handler); - activate(); - subscription - } - - inner( - &self.keystroke_observers, - Box::new(move |event, window, cx| { - f(event, window, cx); - true - }), - ) - } - - /// Register a callback to be invoked when a keystroke is received by the application - /// in any window. Note that this fires _before_ all other action and event mechanisms have resolved - /// unlike [`App::observe_keystrokes`] which fires after. This means that `cx.stop_propagation` calls - /// within interceptors will prevent action dispatch - pub fn intercept_keystrokes( - &mut self, - mut f: impl FnMut(&KeystrokeEvent, &mut Window, &mut App) + 'static, - ) -> Subscription { - fn inner( - keystroke_interceptors: &SubscriberSet<(), KeystrokeObserver>, - handler: KeystrokeObserver, - ) -> Subscription { - let (subscription, activate) = keystroke_interceptors.insert((), handler); - activate(); - subscription - } - - inner( - &self.keystroke_interceptors, - Box::new(move |event, window, cx| { - f(event, window, cx); - true - }), - ) - } - - /// Register key bindings. - pub fn bind_keys(&mut self, bindings: impl IntoIterator) { - self.keymap.borrow_mut().add_bindings(bindings); - self.pending_effects.push_back(Effect::RefreshWindows); - } - - /// Clear all key bindings in the app. - pub fn clear_key_bindings(&mut self) { - self.keymap.borrow_mut().clear(); - self.pending_effects.push_back(Effect::RefreshWindows); - } - - /// Get all key bindings in the app. - pub fn key_bindings(&self) -> Rc> { - self.keymap.clone() - } - - /// Register a global handler for actions invoked via the keyboard. These handlers are run at - /// the end of the bubble phase for actions, and so will only be invoked if there are no other - /// handlers or if they called `cx.propagate()`. - pub fn on_action( - &mut self, - listener: impl Fn(&A, &mut Self) + 'static, - ) -> &mut Self { - self.global_action_listeners - .entry(TypeId::of::()) - .or_default() - .push(Rc::new(move |action, phase, cx| { - if phase == DispatchPhase::Bubble { - let action = action.downcast_ref().unwrap(); - listener(action, cx) - } - })); - self - } - - /// 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 [`Self::propagate`]. It's also possible to cancel a call to [`Self::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 - /// [`Self::stop_propagation`]. It's also possible to cancel a call to [`Self::stop_propagation`] by calling - /// this method before effects are flushed. - pub fn propagate(&mut self) { - self.propagate_event = true; - } - - /// Build an action from some arbitrary data, typically a keymap entry. - pub fn build_action( - &self, - name: &str, - data: Option, - ) -> std::result::Result, ActionBuildError> { - self.actions.build_action(name, data) - } - - /// Get all action names that have been registered. Note that registration only allows for - /// actions to be built dynamically, and is unrelated to binding actions in the element tree. - pub fn all_action_names(&self) -> &[&'static str] { - self.actions.all_action_names() - } - - /// Returns key bindings that invoke the given action on the currently focused element, without - /// checking context. Bindings are returned in the order they were added. For display, the last - /// binding should take precedence. - pub fn all_bindings_for_input(&self, input: &[Keystroke]) -> Vec { - RefCell::borrow(&self.keymap).all_bindings_for_input(input) - } - - /// Get all non-internal actions that have been registered, along with their schemas. - pub fn action_schemas( - &self, - generator: &mut schemars::SchemaGenerator, - ) -> Vec<(&'static str, Option)> { - self.actions.action_schemas(generator) - } - - /// Get the schema for a specific action by name. - /// Returns `None` if the action is not found. - /// Returns `Some(None)` if the action exists but has no schema. - /// Returns `Some(Some(schema))` if the action exists and has a schema. - pub fn action_schema_by_name( - &self, - name: &str, - generator: &mut schemars::SchemaGenerator, - ) -> Option> { - self.actions.action_schema_by_name(name, generator) - } - - /// Get a map from a deprecated action name to the canonical name. - pub fn deprecated_actions_to_preferred_actions(&self) -> &HashMap<&'static str, &'static str> { - self.actions.deprecated_aliases() - } - - /// Get a map from an action name to the deprecation messages. - pub fn action_deprecation_messages(&self) -> &HashMap<&'static str, &'static str> { - self.actions.deprecation_messages() - } - - /// Get a map from an action name to the documentation. - pub fn action_documentation(&self) -> &HashMap<&'static str, &'static str> { - self.actions.documentation() - } - - /// Register a callback to be invoked when the application is about to quit. - /// It is not possible to cancel the quit event at this point. - pub fn on_app_quit( - &self, - mut on_quit: impl FnMut(&mut App) -> Fut + 'static, - ) -> Subscription - where - Fut: 'static + Future, - { - let (subscription, activate) = self.quit_observers.insert( - (), - Box::new(move |cx| { - let future = on_quit(cx); - future.boxed_local() - }), - ); - activate(); - subscription - } - - /// Register a callback to be invoked when the application is about to restart. - /// - /// These callbacks are called before any `on_app_quit` callbacks. - pub fn on_app_restart(&self, mut on_restart: impl 'static + FnMut(&mut App)) -> Subscription { - let (subscription, activate) = self.restart_observers.insert( - (), - Box::new(move |cx| { - on_restart(cx); - true - }), - ); - activate(); - subscription - } - - /// Register a callback to be invoked when a window is closed - /// The window is no longer accessible at the point this callback is invoked. - pub fn on_window_closed( - &self, - mut on_closed: impl FnMut(&mut App, WindowId) + 'static, - ) -> Subscription { - let (subscription, activate) = self.window_closed_observers.insert((), Box::new(on_closed)); - activate(); - subscription - } - - pub(crate) fn clear_pending_keystrokes(&mut self) { - for window in self.windows() { - window - .update(self, |_, window, cx| { - window.clear_pending_keystrokes(cx); - }) - .ok(); - } - } - - /// Checks if the given action is bound in the current context, as defined by the app's current focus, - /// the bindings in the element tree, and any global action listeners. - pub fn is_action_available(&mut self, action: &dyn Action) -> bool { - let mut action_available = false; - if let Some(window) = self.active_window() - && let Ok(window_action_available) = - window.update(self, |_, window, cx| window.is_action_available(action, cx)) - { - action_available = window_action_available; - } - - action_available - || self - .global_action_listeners - .contains_key(&action.as_any().type_id()) - } - - /// Sets the menu bar for this application. This will replace any existing menu bar. - pub fn set_menus(&self, menus: impl IntoIterator) { - let menus: Vec = menus.into_iter().collect(); - self.platform.set_menus(menus, &self.keymap.borrow()); - } - - /// Gets the menu bar for this application. - pub fn get_menus(&self) -> Option> { - self.platform.get_menus() - } - - /// Sets the right click menu for the app icon in the dock - pub fn set_dock_menu(&self, menus: Vec) { - self.platform.set_dock_menu(menus, &self.keymap.borrow()) - } - - /// Performs the action associated with the given dock menu item, only used on Windows for now. - pub fn perform_dock_menu_action(&self, action: usize) { - self.platform.perform_dock_menu_action(action); - } - - /// Adds given path to the bottom of the list of recent paths for the application. - /// The list is usually shown on the application icon's context menu in the dock, - /// and allows to open the recent files via that context menu. - /// If the path is already in the list, it will be moved to the bottom of the list. - pub fn add_recent_document(&self, path: &Path) { - self.platform.add_recent_document(path); - } - - /// Updates the jump list with the updated list of recent paths for the application, only used on Windows for now. - /// Note that this also sets the dock menu on Windows. - pub fn update_jump_list( - &self, - menus: Vec, - entries: Vec>, - ) -> Task>> { - self.platform.update_jump_list(menus, entries) - } - - /// Dispatch an action to the currently active window or global action handler - /// See [`crate::Action`] for more information on how actions work - pub fn dispatch_action(&mut self, action: &dyn Action) { - if let Some(active_window) = self.active_window() { - active_window - .update(self, |_, window, cx| { - window.dispatch_action(action.boxed_clone(), cx) - }) - .log_err(); - } else { - self.dispatch_global_action(action); - } - } - - fn dispatch_global_action(&mut self, action: &dyn Action) { - 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 - && 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); - } - } - - /// Is there currently something being dragged? - pub fn has_active_drag(&self) -> bool { - self.active_drag.is_some() - } - - /// Gets the cursor style of the currently active drag operation. - pub fn active_drag_cursor_style(&self) -> Option { - self.active_drag.as_ref().and_then(|drag| drag.cursor_style) - } - - /// Stops active drag and clears any related effects. - pub fn stop_active_drag(&mut self, window: &mut Window) -> bool { - if self.active_drag.is_some() { - self.active_drag = None; - if self.platform_owned_drag.as_ref().is_some_and(|drag| { - drag.source_window == window.window_handle().window_id() - && matches!(&drag.state, PlatformOwnedDragState::RestoredInSourceWindow) - }) { - self.platform_owned_drag = None; - } - window.refresh(); - true - } else { - false - } - } - - pub(crate) fn hand_active_drag_to_platform(&mut self, source_window: WindowId) -> bool { - let Some(drag) = self.active_drag.take() else { - return false; - }; - self.platform_owned_drag = Some(PlatformOwnedDrag { - source_window, - state: PlatformOwnedDragState::Suspended(drag), - }); - true - } - - pub(crate) fn restore_platform_drag(&mut self, source_window: WindowId) -> bool { - let Some(platform_drag) = self - .platform_owned_drag - .as_mut() - .filter(|drag| drag.source_window == source_window) - else { - return false; - }; - let state = std::mem::replace( - &mut platform_drag.state, - PlatformOwnedDragState::RestoredInSourceWindow, - ); - let PlatformOwnedDragState::Suspended(drag) = state else { - return false; - }; - self.active_drag = Some(drag); - true - } - - pub(crate) fn hand_restored_drag_to_platform(&mut self, source_window: WindowId) -> bool { - let Some(platform_drag) = self.platform_owned_drag.as_mut().filter(|drag| { - drag.source_window == source_window - && matches!(&drag.state, PlatformOwnedDragState::RestoredInSourceWindow) - }) else { - return false; - }; - let Some(drag) = self.active_drag.take() else { - return false; - }; - platform_drag.state = PlatformOwnedDragState::Suspended(drag); - true - } - - pub(crate) fn end_platform_drag(&mut self, source_window: WindowId) -> bool { - if !self - .platform_owned_drag - .as_ref() - .is_some_and(|drag| drag.source_window == source_window) - { - return false; - } - self.platform_owned_drag = None; - self.active_drag = None; - true - } - - /// Sets the cursor style for the currently active drag operation. - pub fn set_active_drag_cursor_style( - &mut self, - cursor_style: CursorStyle, - window: &mut Window, - ) -> bool { - if let Some(ref mut drag) = self.active_drag { - drag.cursor_style = Some(cursor_style); - window.refresh(); - true - } else { - false - } - } - - /// Set the prompt renderer for GPUI. This will replace the default or platform specific - /// prompts with this custom implementation. - pub fn set_prompt_builder( - &mut self, - renderer: impl Fn( - PromptLevel, - &str, - Option<&str>, - &[PromptButton], - PromptHandle, - &mut Window, - &mut App, - ) -> RenderablePromptHandle - + 'static, - ) { - self.prompt_builder = Some(PromptBuilder::Custom(Box::new(renderer))); - } - - /// Reset the prompt builder to the default implementation. - pub fn reset_prompt_builder(&mut self) { - self.prompt_builder = Some(PromptBuilder::Default); - } - - /// Remove an asset from GPUI's cache - pub fn remove_asset(&mut self, source: &A::Source) { - let asset_id = (TypeId::of::(), hash(source)); - self.loading_assets.remove(&asset_id); - } - - /// Check whether an asset is present in GPUI's cache (loading or loaded), - /// without fetching it. - #[cfg(any(test, feature = "test-support"))] - pub fn has_asset(&self, source: &A::Source) -> bool { - let asset_id = (TypeId::of::(), hash(source)); - self.loading_assets.contains_key(&asset_id) - } - - /// Asynchronously load an asset, if the asset hasn't finished loading this will return None. - /// - /// Note that the multiple calls to this method will only result in one `Asset::load` call at a - /// time, and the results of this call will be cached - pub fn fetch_asset(&mut self, source: &A::Source) -> (Shared>, bool) { - let asset_id = (TypeId::of::(), hash(source)); - let mut is_first = false; - let task = self - .loading_assets - .remove(&asset_id) - .map(|boxed_task| *boxed_task.downcast::>>().unwrap()) - .unwrap_or_else(|| { - is_first = true; - let future = A::load(source.clone(), self); - - self.background_executor().spawn(future).shared() - }); - - self.loading_assets.insert(asset_id, Box::new(task.clone())); - - (task, is_first) - } - - /// Obtain a new [`FocusHandle`], which allows you to track and manipulate the keyboard focus - /// for elements rendered within this window. - #[track_caller] - pub fn focus_handle(&self) -> FocusHandle { - FocusHandle::new(&self.focus_handles) - } - - /// Tell GPUI that an entity has changed and observers of it should be notified. - pub fn notify(&mut self, entity_id: EntityId) { - let window_invalidators = mem::take( - self.window_invalidators_by_entity - .entry(entity_id) - .or_default(), - ); - - // `window_invalidators_by_entity` is monotonic, so an entry alone - // doesn't mean the window is currently rendering the entity. Filter - // through `tracked_entities` to keep invalidation tight to windows - // that actually display this entity right now. - let live_invalidators: SmallVec<[WindowInvalidator; 2]> = window_invalidators - .iter() - .filter(|(window_id, _)| { - self.tracked_entities - .get(window_id) - .is_some_and(|set| set.contains(&entity_id)) - }) - .map(|(_, invalidator)| invalidator.clone()) - .collect(); - - if live_invalidators.is_empty() { - if self.pending_notifications.insert(entity_id) { - self.pending_effects - .push_back(Effect::Notify { emitter: entity_id }); - } - } else { - for invalidator in &live_invalidators { - invalidator.invalidate_view(entity_id, self); - } - } - - self.window_invalidators_by_entity - .insert(entity_id, window_invalidators); - } - - /// Returns the name for this [`App`]. - #[cfg(any(test, feature = "test-support", debug_assertions))] - pub fn get_name(&self) -> Option<&'static str> { - self.name - } - - /// Returns `true` if the platform file picker supports selecting a mix of files and directories. - pub fn can_select_mixed_files_and_dirs(&self) -> bool { - self.platform.can_select_mixed_files_and_dirs() - } - - /// Removes an image from the sprite atlas on all windows. - /// - /// If the current window is being updated, it will be removed from `App.windows`, you can use `current_window` to specify the current window. - /// This is a no-op if the image is not in the sprite atlas. - pub fn drop_image(&mut self, image: Arc, current_window: Option<&mut Window>) { - // remove the texture from all other windows - for window in self.windows.values_mut().flatten() { - _ = window.drop_image(image.clone()); - } - - // remove the texture from the current window - if let Some(window) = current_window { - _ = window.drop_image(image); - } - } - - /// Sets the renderer for the inspector. - #[cfg(any(feature = "inspector", debug_assertions))] - pub fn set_inspector_renderer(&mut self, f: crate::InspectorRenderer) { - self.inspector_renderer = Some(f); - } - - /// Registers a renderer specific to an inspector state. - #[cfg(any(feature = "inspector", debug_assertions))] - pub fn register_inspector_element( - &mut self, - f: impl 'static + Fn(crate::InspectorElementId, &T, &mut Window, &mut App) -> R, - ) { - self.inspector_element_registry.register(f); - } - - /// Initializes gpui's default colors for the application. - /// - /// These colors can be accessed through `cx.default_colors()`. - pub fn init_colors(&mut self) { - self.set_global(GlobalColors(Arc::new(Colors::default()))); - } -} - -impl AppContext for App { - /// Builds an entity that is owned by the application. - /// - /// The given function will be invoked with a [`Context`] and must return an object representing the entity. An - /// [`Entity`] handle will be returned, which can be used to access the entity in a context. - fn new(&mut self, build_entity: impl FnOnce(&mut Context) -> T) -> Entity { - self.update(|cx| { - let slot = cx.entities.reserve(); - let handle = slot.clone(); - let entity = build_entity(&mut Context::new_context(cx, slot.downgrade())); - - cx.push_effect(Effect::EntityCreated { - entity: handle.into_any(), - tid: TypeId::of::(), - window: cx.window_update_stack.last().cloned(), - }); - - cx.entities.insert(slot, entity) - }) - } - - fn reserve_entity(&mut self) -> Reservation { - Reservation(self.entities.reserve()) - } - - fn insert_entity( - &mut self, - reservation: Reservation, - build_entity: impl FnOnce(&mut Context) -> T, - ) -> Entity { - self.update(|cx| { - let slot = reservation.0; - let entity = build_entity(&mut Context::new_context(cx, slot.downgrade())); - cx.entities.insert(slot, entity) - }) - } - - /// Updates the entity referenced by the given handle. The function is passed a mutable reference to the - /// entity along with a `Context` for the entity. - fn update_entity( - &mut self, - handle: &Entity, - update: impl FnOnce(&mut T, &mut Context) -> R, - ) -> R { - self.update(|cx| { - let mut entity = cx.entities.lease(handle); - let result = update( - &mut entity, - &mut Context::new_context(cx, handle.downgrade()), - ); - cx.entities.end_lease(entity); - result - }) - } - - fn as_mut<'a, T>(&'a mut self, handle: &Entity) -> GpuiBorrow<'a, T> - where - T: 'static, - { - GpuiBorrow::new(handle.clone(), self) - } - - fn read_entity(&self, handle: &Entity, read: impl FnOnce(&T, &App) -> R) -> R - where - T: 'static, - { - let entity = self.entities.read(handle); - read(entity, self) - } - - fn update_window(&mut self, handle: AnyWindowHandle, update: F) -> Result - where - F: FnOnce(AnyView, &mut Window, &mut App) -> T, - { - self.update_window_id(handle.id, update) - } - - fn with_window( - &mut self, - entity_id: EntityId, - f: impl FnOnce(&mut Window, &mut App) -> R, - ) -> Option { - App::with_window(self, entity_id, f) - } - - fn read_window( - &self, - window: &WindowHandle, - read: impl FnOnce(Entity, &App) -> R, - ) -> Result - where - T: 'static, - { - let window = self - .windows - .get(window.id) - .context("window not found")? - .as_deref() - .expect("attempted to read a window that is already on the stack"); - - let root_view = window.root.clone().unwrap(); - let view = root_view - .downcast::() - .map_err(|_| anyhow!("root view's type has changed"))?; - - Ok(read(view, self)) - } - - fn background_spawn(&self, future: impl Future + Send + 'static) -> Task - where - R: Send + 'static, - { - self.background_executor.spawn(future) - } - - fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> R - where - G: Global, - { - let mut g = self.global::(); - callback(g, 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: ArenaBox, - }, - RefreshWindows, - NotifyGlobalObservers { - global_type: TypeId, - }, - Defer { - callback: Box, - }, - EntityCreated { - entity: AnyEntity, - tid: TypeId, - window: Option, - }, -} - -impl std::fmt::Debug for Effect { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Effect::Notify { emitter } => write!(f, "Notify({})", emitter), - Effect::Emit { emitter, .. } => write!(f, "Emit({:?})", emitter), - Effect::RefreshWindows => write!(f, "RefreshWindows"), - Effect::NotifyGlobalObservers { global_type } => { - write!(f, "NotifyGlobalObservers({:?})", global_type) - } - Effect::Defer { .. } => write!(f, "Defer(..)"), - Effect::EntityCreated { entity, .. } => write!(f, "EntityCreated({:?})", entity), - } - } -} - -/// Wraps a global variable value during `update_global` while the value has been moved to the stack. -pub(crate) struct GlobalLease { - global: Box, - global_type: PhantomData, -} - -impl GlobalLease { - fn new(global: Box) -> Self { - GlobalLease { - global, - global_type: PhantomData, - } - } -} - -impl Deref for GlobalLease { - type Target = G; - - fn deref(&self) -> &Self::Target { - self.global.downcast_ref().unwrap() - } -} - -impl DerefMut for GlobalLease { - 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 { - /// The view used to render this drag - pub view: AnyView, - - /// The value of the dragged item, to be dropped - pub value: Arc, - - /// This is used to render the dragged item in the same place - /// on the original element that the drag was initiated - pub cursor_offset: Point, - - /// The cursor style to use while dragging - pub cursor_style: Option, - - /// Resolves the payload to offer the platform if the drag leaves the window. - /// Invoked at most once per drag gesture, at promotion time. - pub external_payload_source: Option, -} - -/// Lazily resolves the payload handed to the platform when an internal drag is -/// promoted to a native drag session. -pub type ExternalDragPayloadSource = - Box Option + 'static>; - -/// Contains state associated with a tooltip. You'll only need this struct if you're implementing -/// tooltip behavior on a custom element. Otherwise, use [Div::tooltip](crate::Interactivity::tooltip). -#[derive(Clone)] -pub struct AnyTooltip { - /// The view used to display the tooltip - pub view: AnyView, - - /// The absolute position of the mouse when the tooltip was deployed. - pub mouse_position: Point, - - /// Given the bounds of the tooltip, checks whether the tooltip should still be visible and - /// updates its state accordingly. This is needed atop the hovered element's mouse move handler - /// to handle the case where the element is not painted (e.g. via use of `visible_on_hover`). - pub check_visible_and_update: Rc, &mut Window, &mut App) -> bool>, -} - -/// A keystroke event, and potentially the associated action -#[derive(Debug)] -pub struct KeystrokeEvent { - /// The keystroke that occurred - pub keystroke: Keystroke, - - /// The action that was resolved for the keystroke, if any - pub action: Option>, - - /// The context stack at the time - pub context_stack: Vec, -} - -struct NullHttpClient; - -impl HttpClient for NullHttpClient { - fn send( - &self, - _req: http_client::Request, - ) -> futures::future::BoxFuture< - 'static, - anyhow::Result>, - > { - async move { - anyhow::bail!("No HttpClient available"); - } - .boxed() - } - - fn user_agent(&self) -> Option<&http_client::http::HeaderValue> { - None - } - - fn proxy(&self) -> Option<&Url> { - None - } -} - -/// A mutable reference to an entity owned by GPUI -pub struct GpuiBorrow<'a, T> { - inner: Option>, - app: &'a mut App, -} - -impl<'a, T: 'static> GpuiBorrow<'a, T> { - fn new(inner: Entity, app: &'a mut App) -> Self { - app.start_update(); - let lease = app.entities.lease(&inner); - Self { - inner: Some(lease), - app, - } - } -} - -impl<'a, T: 'static> std::borrow::Borrow for GpuiBorrow<'a, T> { - fn borrow(&self) -> &T { - self.inner.as_ref().unwrap().borrow() - } -} - -impl<'a, T: 'static> std::borrow::BorrowMut for GpuiBorrow<'a, T> { - fn borrow_mut(&mut self) -> &mut T { - self.inner.as_mut().unwrap().borrow_mut() - } -} - -impl<'a, T: 'static> std::ops::Deref for GpuiBorrow<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.inner.as_ref().unwrap() - } -} - -impl<'a, T: 'static> std::ops::DerefMut for GpuiBorrow<'a, T> { - fn deref_mut(&mut self) -> &mut T { - self.inner.as_mut().unwrap() - } -} - -impl<'a, T> Drop for GpuiBorrow<'a, T> { - fn drop(&mut self) { - let lease = self.inner.take().unwrap(); - self.app.notify(lease.id); - self.app.entities.end_lease(lease); - self.app.finish_update(); - } -} - -#[cfg(test)] -mod test { - use std::{ - cell::{Cell, RefCell}, - ffi::OsString, - path::PathBuf, - rc::Rc, - }; - - #[cfg(unix)] - use std::os::unix::ffi::OsStringExt; - - use crate::{AppContext, Context, Empty, IntoElement, Render, TestAppContext, Window}; - - struct RenderCounter(Rc>); - - impl Render for RenderCounter { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - self.0.set(self.0.get() + 1); - Empty - } - } - - #[gpui::test] - fn async_app_refresh_flushes_refresh_effect(cx: &mut TestAppContext) { - let render_count = Rc::new(Cell::new(0)); - - let _window = cx.add_window({ - let render_count = render_count.clone(); - move |_, _| RenderCounter(render_count) - }); - - cx.run_until_parked(); - let render_count_before_refresh = render_count.get(); - - cx.to_async().refresh(); - - assert_eq!(render_count.get(), render_count_before_refresh + 1); - } - - #[test] - fn test_gpui_borrow() { - let cx = TestAppContext::single(); - let observation_count = Rc::new(RefCell::new(0)); - - let state = cx.update(|cx| { - let state = cx.new(|_| false); - cx.observe(&state, { - let observation_count = observation_count.clone(); - move |_, _| { - let mut count = observation_count.borrow_mut(); - *count += 1; - } - }) - .detach(); - - state - }); - - cx.update(|cx| { - // Calling this like this so that we don't clobber the borrow_mut above - *std::borrow::BorrowMut::borrow_mut(&mut state.as_mut(cx)) = true; - }); - - cx.update(|cx| { - state.write(cx, false); - }); - - assert_eq!(*observation_count.borrow(), 2); - } - - #[gpui::test] - async fn test_restart_preserves_path_and_arguments(cx: &mut TestAppContext) { - #[cfg(unix)] - let user_data_dir = OsString::from_vec(b"/tmp/zed data/\xff".to_vec()); - #[cfg(not(unix))] - let user_data_dir = OsString::from("C:\\zed data"); - let arguments = vec![OsString::from("--user-data-dir"), user_data_dir]; - let restart_path = PathBuf::from("updated-zed"); - let _application = - super::Application(cx.app.clone()).with_restart_arguments(arguments.clone()); - let restart = cx.expect_restart(); - - cx.update(|cx| { - cx.set_restart_path(restart_path.clone()); - cx.restart(); - }); - - let (path, restart_arguments) = restart.await.expect("restart was not requested"); - assert_eq!(path, Some(restart_path)); - assert_eq!(restart_arguments, arguments); - } -} diff --git a/crates/gpui_pre/src/app/async_context.rs b/crates/gpui_pre/src/app/async_context.rs deleted file mode 100644 index 48c084a6..00000000 --- a/crates/gpui_pre/src/app/async_context.rs +++ /dev/null @@ -1,537 +0,0 @@ -use crate::{ - AnyView, AnyWindowHandle, App, AppCell, AppContext, BackgroundExecutor, BorrowAppContext, - Entity, EntityId, EventEmitter, Focusable, ForegroundExecutor, Global, GpuiBorrow, - PromptButton, PromptLevel, Render, Reservation, Result, Subscription, Task, VisualContext, - Window, WindowHandle, -}; -use anyhow::{Context as _, bail}; -use derive_more::{Deref, DerefMut}; -use futures::channel::oneshot; -use futures::future::FutureExt; -use std::{future::Future, rc::Weak}; - -use super::{Context, WeakEntity}; - -/// An async-friendly version of [App] with a static lifetime so it can be held across `await` points in async code. -/// You're provided with an instance when calling [App::spawn], and you can also create one with [App::to_async]. -/// -/// Internally, this holds a weak reference to an `App`. Methods will panic if the app has been dropped, -/// but this should not happen in practice when using foreground tasks spawned via `cx.spawn()`, -/// as the executor checks if the app is alive before running each task. -#[derive(Clone)] -pub struct AsyncApp { - pub(crate) app: Weak, - pub(crate) background_executor: BackgroundExecutor, - pub(crate) foreground_executor: ForegroundExecutor, -} - -impl AsyncApp { - fn app(&self) -> std::rc::Rc { - self.app - .upgrade() - .expect("app was released before async operation completed") - } -} - -impl AppContext for AsyncApp { - fn new(&mut self, build_entity: impl FnOnce(&mut Context) -> T) -> Entity { - let app = self.app(); - let mut app = app.borrow_mut(); - app.new(build_entity) - } - - fn reserve_entity(&mut self) -> Reservation { - let app = self.app(); - let mut app = app.borrow_mut(); - app.reserve_entity() - } - - fn insert_entity( - &mut self, - reservation: Reservation, - build_entity: impl FnOnce(&mut Context) -> T, - ) -> Entity { - let app = self.app(); - let mut app = app.borrow_mut(); - app.insert_entity(reservation, build_entity) - } - - fn update_entity( - &mut self, - handle: &Entity, - update: impl FnOnce(&mut T, &mut Context) -> R, - ) -> R { - let app = self.app(); - let mut app = app.borrow_mut(); - app.update_entity(handle, update) - } - - fn as_mut<'a, T>(&'a mut self, _handle: &Entity) -> GpuiBorrow<'a, T> - where - T: 'static, - { - panic!("Cannot as_mut with an async context. Try calling update() first") - } - - fn read_entity(&self, handle: &Entity, callback: impl FnOnce(&T, &App) -> R) -> R - where - T: 'static, - { - let app = self.app(); - let lock = app.borrow(); - lock.read_entity(handle, callback) - } - - fn update_window(&mut self, window: AnyWindowHandle, f: F) -> Result - where - F: FnOnce(AnyView, &mut Window, &mut App) -> T, - { - let app = self.app.upgrade().context("app was released")?; - let mut lock = app.try_borrow_mut()?; - if lock.quitting { - bail!("app is quitting"); - } - lock.update_window(window, f) - } - - fn with_window( - &mut self, - entity_id: EntityId, - f: impl FnOnce(&mut Window, &mut App) -> R, - ) -> Option { - let app = self.app.upgrade()?; - let mut lock = app.try_borrow_mut().ok()?; - if lock.quitting { - return None; - } - lock.with_window(entity_id, f) - } - - fn read_window( - &self, - window: &WindowHandle, - read: impl FnOnce(Entity, &App) -> R, - ) -> Result - where - T: 'static, - { - let app = self.app.upgrade().context("app was released")?; - let lock = app.borrow(); - if lock.quitting { - bail!("app is quitting"); - } - lock.read_window(window, read) - } - - #[track_caller] - fn background_spawn(&self, future: impl Future + Send + 'static) -> Task - where - R: Send + 'static, - { - self.background_executor.spawn(future) - } - - fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> R - where - G: Global, - { - let app = self.app(); - let mut lock = app.borrow_mut(); - lock.update(|this| this.read_global(callback)) - } -} - -impl AsyncApp { - /// Schedules all windows in the application to be redrawn. - pub fn refresh(&self) { - let app = self.app(); - let mut lock = app.borrow_mut(); - // A direct call would leave the refresh effect queued, which cannot wake - // a platform render loop that has already parked. - lock.update(|cx| cx.refresh_windows()); - } - - /// Get an executor which can be used to spawn futures in the background. - pub fn background_executor(&self) -> &BackgroundExecutor { - &self.background_executor - } - - /// Get an executor which can be used to spawn futures in the foreground. - pub fn foreground_executor(&self) -> &ForegroundExecutor { - &self.foreground_executor - } - - /// Invoke the given function in the context of the app, then flush any effects produced during its invocation. - pub fn update(&self, f: impl FnOnce(&mut App) -> R) -> R { - let app = self.app(); - let mut lock = app.borrow_mut(); - lock.update(f) - } - - /// Arrange for the given callback to be invoked whenever the given entity emits an event of a given type. - /// The callback is provided a handle to the emitting entity and a reference to the emitted event. - pub fn subscribe( - &mut self, - entity: &Entity, - on_event: impl FnMut(Entity, &Event, &mut App) + 'static, - ) -> Subscription - where - T: 'static + EventEmitter, - Event: 'static, - { - let app = self.app(); - let mut lock = app.borrow_mut(); - lock.subscribe(entity, on_event) - } - - /// Open a window with the given options based on the root view returned by the given function. - pub fn open_window( - &self, - options: crate::WindowOptions, - build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity, - ) -> Result> - where - V: 'static + Render, - { - let app = self.app(); - let mut lock = app.borrow_mut(); - if lock.quitting { - bail!("app is quitting"); - } - lock.open_window(options, build_root_view) - } - - /// Schedule a future to be polled in the foreground. - #[track_caller] - pub fn spawn(&self, f: AsyncFn) -> Task - where - AsyncFn: AsyncFnOnce(&mut AsyncApp) -> R + 'static, - R: 'static, - { - let mut cx = self.clone(); - self.foreground_executor - .spawn(async move { f(&mut cx).await }.boxed_local()) - } - - /// Determine whether global state of the specified type has been assigned. - pub fn has_global(&self) -> bool { - let app = self.app(); - let app = app.borrow_mut(); - app.has_global::() - } - - /// Reads the global state of the specified type, passing it to the given callback. - /// - /// Panics if no global state of the specified type has been assigned. - pub fn read_global(&self, read: impl FnOnce(&G, &App) -> R) -> R { - let app = self.app(); - let app = app.borrow_mut(); - read(app.global(), &app) - } - - /// Reads the global state of the specified type, passing it to the given callback. - /// - /// Similar to [`AsyncApp::read_global`], but returns an error instead of panicking - pub fn try_read_global(&self, read: impl FnOnce(&G, &App) -> R) -> Option { - let app = self.app(); - let app = app.borrow_mut(); - if app.quitting { - return None; - } - Some(read(app.try_global()?, &app)) - } - - /// Reads the global state of the specified type, passing it to the given callback. - /// A default value is assigned if a global of this type has not yet been assigned. - pub fn read_default_global( - &self, - read: impl FnOnce(&G, &App) -> R, - ) -> R { - let app = self.app(); - let mut app = app.borrow_mut(); - app.update(|cx| { - cx.default_global::(); - }); - read(app.global(), &app) - } - - /// A convenience method for [`App::update_global`](BorrowAppContext::update_global) - /// for updating the global state of the specified type. - pub fn update_global(&self, update: impl FnOnce(&mut G, &mut App) -> R) -> R { - let app = self.app(); - let mut app = app.borrow_mut(); - app.update(|cx| cx.update_global(update)) - } - - /// Run something using this entity and cx, when the returned struct is dropped - pub fn on_drop) + 'static>( - &self, - entity: &WeakEntity, - f: Callback, - ) -> gpui_util::Deferred> { - let entity = entity.clone(); - let mut cx = self.clone(); - gpui_util::defer(move || { - entity.update(&mut cx, f).ok(); - }) - } -} - -/// A cloneable, owned handle to the application context, -/// composed with the window associated with the current task. -#[derive(Clone, Deref, DerefMut)] -pub struct AsyncWindowContext { - #[deref] - #[deref_mut] - app: AsyncApp, - window: AnyWindowHandle, -} - -impl AsyncWindowContext { - pub(crate) fn new_context(app: AsyncApp, window: AnyWindowHandle) -> Self { - Self { app, window } - } - - /// Get the handle of the window this context is associated with. - pub fn window_handle(&self) -> AnyWindowHandle { - self.window - } - - /// A convenience method for [`App::update_window`]. - pub fn update(&mut self, update: impl FnOnce(&mut Window, &mut App) -> R) -> Result { - self.app - .update_window(self.window, |_, window, cx| update(window, cx)) - } - - /// A convenience method for [`App::update_window`]. - pub fn update_root( - &mut self, - update: impl FnOnce(AnyView, &mut Window, &mut App) -> R, - ) -> Result { - self.app.update_window(self.window, update) - } - - /// A convenience method for [`Window::on_next_frame`]. - pub fn on_next_frame(&mut self, f: impl FnOnce(&mut Window, &mut App) + 'static) { - self.app - .update_window(self.window, |_, window, _| window.on_next_frame(f)) - .ok(); - } - - /// A convenience method for [`App::global`]. - pub fn read_global( - &mut self, - read: impl FnOnce(&G, &Window, &App) -> R, - ) -> Result { - self.app - .update_window(self.window, |_, window, cx| read(cx.global(), window, cx)) - } - - /// A convenience method for [`App::update_global`](BorrowAppContext::update_global). - /// for updating the global state of the specified type. - pub fn update_global( - &mut self, - update: impl FnOnce(&mut G, &mut Window, &mut App) -> R, - ) -> Result - where - G: Global, - { - self.app.update_window(self.window, |_, window, cx| { - cx.update_global(|global, cx| update(global, window, cx)) - }) - } - - /// Schedule a future to be executed on the main thread. This is used for collecting - /// the results of background tasks and updating the UI. - #[track_caller] - pub fn spawn(&self, f: AsyncFn) -> Task - where - AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static, - R: 'static, - { - let mut cx = self.clone(); - self.foreground_executor - .spawn(async move { f(&mut cx).await }.boxed_local()) - } - - /// Present a platform dialog. - /// The provided message will be presented, along with buttons for each answer. - /// When a button is clicked, the returned Receiver will receive the index of the clicked button. - pub fn prompt( - &mut self, - level: PromptLevel, - message: &str, - detail: Option<&str>, - answers: &[T], - ) -> oneshot::Receiver - where - T: Clone + Into, - { - self.app - .update_window(self.window, |_, window, cx| { - window.prompt(level, message, detail, answers, cx) - }) - .unwrap_or_else(|_| oneshot::channel().1) - } -} - -impl AppContext for AsyncWindowContext { - fn new(&mut self, build_entity: impl FnOnce(&mut Context) -> T) -> Entity - where - T: 'static, - { - let mut build_entity = Some(build_entity); - match self.app.update_window(self.window, |_, _, cx| { - cx.new( - build_entity - .take() - .expect("build_entity is taken exactly once"), - ) - }) { - Ok(entity) => entity, - Err(_) => self.app.new( - build_entity - .take() - .expect("update_window returned Err without invoking the closure"), - ), - } - } - - fn reserve_entity(&mut self) -> Reservation { - self.app.reserve_entity() - } - - fn insert_entity( - &mut self, - reservation: Reservation, - build_entity: impl FnOnce(&mut Context) -> T, - ) -> Entity { - let mut args = Some((reservation, build_entity)); - match self.app.update_window(self.window, |_, _, cx| { - let (reservation, build_entity) = args.take().expect("args are taken exactly once"); - cx.insert_entity(reservation, build_entity) - }) { - Ok(entity) => entity, - Err(_) => { - let (reservation, build_entity) = args - .take() - .expect("update_window returned Err without invoking the closure"); - self.app.insert_entity(reservation, build_entity) - } - } - } - - fn update_entity( - &mut self, - handle: &Entity, - update: impl FnOnce(&mut T, &mut Context) -> R, - ) -> R { - self.app.update_entity(handle, update) - } - - fn as_mut<'a, T>(&'a mut self, _: &Entity) -> GpuiBorrow<'a, T> - where - T: 'static, - { - panic!("Cannot use as_mut() from an async context, call `update`") - } - - fn read_entity(&self, handle: &Entity, read: impl FnOnce(&T, &App) -> R) -> R - where - T: 'static, - { - self.app.read_entity(handle, read) - } - - fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result - where - F: FnOnce(AnyView, &mut Window, &mut App) -> T, - { - self.app.update_window(window, update) - } - - fn with_window( - &mut self, - entity_id: EntityId, - f: impl FnOnce(&mut Window, &mut App) -> R, - ) -> Option { - self.app.with_window(entity_id, f) - } - - fn read_window( - &self, - window: &WindowHandle, - read: impl FnOnce(Entity, &App) -> R, - ) -> Result - where - T: 'static, - { - self.app.read_window(window, read) - } - - #[track_caller] - fn background_spawn(&self, future: impl Future + Send + 'static) -> Task - where - R: Send + 'static, - { - self.app.background_executor.spawn(future) - } - - fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> R - where - G: Global, - { - self.app.read_global(callback) - } -} - -impl VisualContext for AsyncWindowContext { - type Result = Result; - - fn window_handle(&self) -> AnyWindowHandle { - self.window - } - - fn new_window_entity( - &mut self, - build_entity: impl FnOnce(&mut Window, &mut Context) -> T, - ) -> Result> { - self.app.update_window(self.window, |_, window, cx| { - cx.new(|cx| build_entity(window, cx)) - }) - } - - fn update_window_entity( - &mut self, - view: &Entity, - update: impl FnOnce(&mut T, &mut Window, &mut Context) -> R, - ) -> Result { - let view = view.clone(); - self.app - .with_window(view.entity_id(), |window, app| { - view.update(app, |entity, cx| update(entity, window, cx)) - }) - .context("entity has no current window") - } - - fn replace_root_view( - &mut self, - build_view: impl FnOnce(&mut Window, &mut Context) -> V, - ) -> Result> - where - V: 'static + Render, - { - self.app.update_window(self.window, |_, window, cx| { - window.replace_root(cx, build_view) - }) - } - - fn focus(&mut self, view: &Entity) -> Result<()> - where - V: Focusable, - { - self.app.update_window(self.window, |_, window, cx| { - view.read(cx).focus_handle(cx).focus(window, cx); - }) - } -} diff --git a/crates/gpui_pre/src/app/bench_context.rs b/crates/gpui_pre/src/app/bench_context.rs deleted file mode 100644 index 278f26cb..00000000 --- a/crates/gpui_pre/src/app/bench_context.rs +++ /dev/null @@ -1,1283 +0,0 @@ -use std::{ - cell::{OnceCell, RefCell}, - future::Future, - rc::Rc, - sync::Arc, - time::Duration, -}; - -use anyhow::{Result, anyhow}; -use hdrhistogram::Histogram; - -use crate::{ - AnyView, AnyWindowHandle, App, AppCell, AppContext, BackgroundExecutor, Bounds, Context, Empty, - Entity, EntityId, Focusable, ForegroundExecutor, Global, Platform, PlatformHeadlessRenderer, - PlatformTextSystem, Render, Reservation, Task, TestPlatform, ThreadedDispatcher, VisualContext, - Window, WindowBounds, WindowHandle, WindowOptions, - app::GpuiBorrow, - profiler::{ - self, FrameEvent, FrameTimingCollector, - journal::{ForegroundEvent, ForegroundJournalCollector, ForegroundJournalEntry}, - }, -}; - -/// Returns a benchmark platform backed by this thread's shared dispatcher. -/// -/// The platform uses this thread's shared multithreaded [`ThreadedDispatcher`], so -/// background work runs with production concurrency in real time. The dispatcher -/// is cached per thread and reused across benchmark invocations so worker and -/// timer threads persist for the whole process instead of being recreated for -/// every Criterion calibration pass. -/// -/// Text is shaped with the provided platform text system. Benchmarks generated -/// by `#[gpui::bench]` use the current platform's text system, so text-heavy -/// benchmark measurements include production shaping and glyph rasterization. -/// -/// `headless_renderer_factory` supplies a renderer for benchmark windows, e.g. -/// `gpui_platform::current_headless_renderer`. When present, scenes drawn by -/// benchmarks are rasterized through the real sprite atlas and submitted to -/// the GPU on present, so quad/sprite regressions show up in measurements. -/// When `None`, presenting discards the scene. Currently only macOS provides -/// a headless renderer (Metal), so GPU submission is excluded from benchmark -/// measurements on other platforms. -pub fn bench_platform( - headless_renderer_factory: Option Option>>>, - text_system: Arc, -) -> Rc { - thread_local! { - static DISPATCHER: OnceCell> = const { OnceCell::new() }; - } - let dispatcher = DISPATCHER.with(|cell| { - cell.get_or_init(|| Arc::new(ThreadedDispatcher::new())) - .clone() - }); - let background_executor = BackgroundExecutor::new(dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(dispatcher); - TestPlatform::with_platform( - background_executor, - foreground_executor, - text_system, - headless_renderer_factory, - ) as Rc -} - -/// Default target frame rate when a benchmark doesn't specify `fps = N`. -const DEFAULT_FPS: u64 = 120; - -const NANOS_PER_SECOND: u128 = 1_000_000_000; - -/// Aggregate statistics for total foreground executor work observed during a -/// measured interval, returned by [`BenchReport::foreground_work`]. -#[derive(Clone, Copy, Debug)] -pub struct ForegroundWorkSummary { - /// Number of foreground work items recorded: task polls, action - /// handlers, input dispatches, and folded sub-floor poll flushes. - pub count: u64, - /// Sum of every recorded item's duration. - pub total: Duration, - /// The longest single recorded item. - pub max: Duration, - /// 50th percentile duration. - pub p50: Duration, - /// 90th percentile duration. - pub p90: Duration, - /// 95th percentile duration. - pub p95: Duration, - /// 99th percentile duration. - pub p99: Duration, - /// How many whole frame budgets (at the report's configured FPS) were - /// exceeded in total, summed across every recorded item. - pub frame_budget_overruns_total: u64, - /// How many whole frame budgets the longest recorded item exceeded. - pub frame_budget_overruns_max: u64, -} - -/// A small report produced by GPUI benchmarks. -#[derive(Clone)] -pub struct BenchReport { - frame_snapshot: Rc>, - frame_budget_nanos: u128, -} - -impl Default for BenchReport { - fn default() -> Self { - Self::with_fps(DEFAULT_FPS) - } -} - -impl BenchReport { - /// Creates a report whose per-frame budget is one frame at `fps` when - /// counting frame budget overruns. - pub fn with_fps(fps: u64) -> Self { - assert!(fps > 0, "frame rate must be greater than zero"); - Self::with_frame_budget_nanos(NANOS_PER_SECOND / fps as u128) - } - - /// Creates a report that treats `frame_budget_nanos` as the per-frame budget - /// when counting frame budget overruns. - pub fn with_frame_budget_nanos(frame_budget_nanos: u128) -> Self { - Self { - frame_snapshot: Rc::new(RefCell::new(WindowFrameSnapshot::new())), - frame_budget_nanos, - } - } - - fn record_frame_timings<'i>(&self, events: impl IntoIterator) { - let mut snapshot = self.frame_snapshot.borrow_mut(); - // `.ok()` on `record`: this operation is infallible (the histograms auto-resize). - for event in events { - match event { - FrameEvent::Draw(timing) => { - snapshot - .draw - .record(timing.draw_duration().as_nanos() as u64) - .ok(); - if let Some(dirty_to_draw) = timing.dirty_to_draw_duration() { - snapshot - .dirty_to_draw - .record(dirty_to_draw.as_nanos() as u64) - .ok(); - } - if timing.invalidations > 0 { - snapshot - .invalidations_per_frame - .record(timing.invalidations) - .ok(); - } - } - FrameEvent::Present(timing) => { - if let Some(animation_interval) = timing.animation_interval { - snapshot - .present_interval - .record(animation_interval.as_nanos() as u64) - .ok(); - } - } - } - } - } - - /// Records total foreground executor work observed during a measured - /// interval: task polls, action handlers, and input dispatches, whether - /// or not they produced a window draw. Draws and presents are excluded - /// here since [`Self::record_frame_timings`] already accounts for them. - fn record_foreground_events<'i>(&self, events: impl IntoIterator) { - let mut snapshot = self.frame_snapshot.borrow_mut(); - for event in events { - let duration = match event { - ForegroundEvent::Draw(_) | ForegroundEvent::Present(_) => continue, - // A flush's span (used by `ForegroundEvent::duration`) is not - // the time spent polling; its summary total is. - ForegroundEvent::SmallPolls(flush) => flush.summary.total, - _ => event.duration(), - }; - snapshot.foreground_work.record(duration); - } - } - - fn total_budget_overruns(&self, histogram: &Histogram) -> u64 { - histogram - .iter_recorded() - .map(|value| { - self.budget_overruns(Duration::from_nanos(value.value_iterated_to())) - * value.count_at_value() - }) - .sum() - } - - /// Returns how many whole frame budgets `foreground_time` exceeded the - /// per frame budget by. This is a synthetic proxy for missed frames: the - /// benchmark harness has no vsync, so it counts how many frame deadlines - /// would have elapsed while the foreground thread was busy. - fn budget_overruns(&self, foreground_time: Duration) -> u64 { - let foreground_nanos = foreground_time.as_nanos(); - if foreground_nanos <= self.frame_budget_nanos { - return 0; - } - - let over_budget_nanos = foreground_nanos - self.frame_budget_nanos; - over_budget_nanos.div_ceil(self.frame_budget_nanos) as u64 - } - - /// Returns aggregate statistics for total foreground executor work - /// observed during the measured interval: every task poll, action - /// handler, and input dispatch on the foreground thread, whether or not - /// it produced a window draw. This is captured through GPUI's foreground - /// journal, so it requires no window and surfaces a slow or stalled task - /// even when nothing was drawn while it ran. - /// - /// Returns `None` when no foreground work was recorded, e.g. a - /// [`BenchAppContext::bench_iter`] measurement that does no async work. - pub fn foreground_work(&self) -> Option { - let frame_snapshot = self.frame_snapshot.borrow(); - let foreground_work = &frame_snapshot.foreground_work; - if foreground_work.histogram.is_empty() { - return None; - } - - let max = Duration::from_nanos(foreground_work.histogram.max()); - Some(ForegroundWorkSummary { - count: foreground_work.histogram.len(), - total: Duration::from_nanos(foreground_work.total_nanos), - max, - p50: Duration::from_nanos(foreground_work.histogram.value_at_quantile(0.50)), - p90: Duration::from_nanos(foreground_work.histogram.value_at_quantile(0.90)), - p95: Duration::from_nanos(foreground_work.histogram.value_at_quantile(0.95)), - p99: Duration::from_nanos(foreground_work.histogram.value_at_quantile(0.99)), - frame_budget_overruns_total: self.total_budget_overruns(&foreground_work.histogram), - frame_budget_overruns_max: self.budget_overruns(max), - }) - } - - /// Prints this report to stderr. - pub fn print(&self, benchmark_name: Option<&'static str>) { - let frame_snapshot = self.frame_snapshot.borrow(); - if frame_snapshot.is_empty() { - return; - } - - let benchmark_name = benchmark_name.unwrap_or("unknown benchmark"); - eprintln!("GPUI bench report (all observed iterations): {benchmark_name}"); - eprintln!(" note: includes Criterion warmup/calibration"); - self.print_histogram("window dirty-to-draw", &frame_snapshot.dirty_to_draw); - self.print_histogram("window draw", &frame_snapshot.draw); - self.print_histogram("window present interval", &frame_snapshot.present_interval); - if !frame_snapshot.invalidations_per_frame.is_empty() { - eprintln!( - " invalidations per frame: mean {:.2}, max {}", - frame_snapshot.invalidations_per_frame.mean(), - frame_snapshot.invalidations_per_frame.max() - ); - } - self.print_foreground_work(&frame_snapshot.foreground_work); - } - - fn print_histogram(&self, name: &str, histogram: &Histogram) { - if histogram.is_empty() { - return; - } - - eprintln!(" {name}:"); - self.print_histogram_body(histogram); - } - - fn print_foreground_work(&self, foreground_work: &DurationHistogram) { - if foreground_work.histogram.is_empty() { - return; - } - - eprintln!(" foreground executor work (task polls, actions, input dispatch):"); - eprintln!(" note: excludes window draw/present, reported separately above"); - eprintln!( - " total: {}", - format_duration(Duration::from_nanos(foreground_work.total_nanos)) - ); - self.print_histogram_body(&foreground_work.histogram); - } - - fn print_histogram_body(&self, histogram: &Histogram) { - let max_foreground_time = Duration::from_nanos(histogram.max()); - eprintln!(" samples: {}", histogram.len()); - eprintln!( - " mean: {}", - format_duration(Duration::from_nanos(histogram.mean() as u64)) - ); - eprintln!( - " p50: {}", - format_duration(Duration::from_nanos(histogram.value_at_quantile(0.50))) - ); - eprintln!( - " p90: {}", - format_duration(Duration::from_nanos(histogram.value_at_quantile(0.90))) - ); - eprintln!( - " p95: {}", - format_duration(Duration::from_nanos(histogram.value_at_quantile(0.95))) - ); - eprintln!( - " p99: {}", - format_duration(Duration::from_nanos(histogram.value_at_quantile(0.99))) - ); - eprintln!(" max: {}", format_duration(max_foreground_time)); - eprintln!( - " frame budget overruns total: {}", - self.total_budget_overruns(histogram) - ); - eprintln!( - " frame budget overruns max: {}", - self.budget_overruns(max_foreground_time) - ); - } -} - -struct WindowFrameSnapshot { - dirty_to_draw: Histogram, - draw: Histogram, - present_interval: Histogram, - invalidations_per_frame: Histogram, - foreground_work: DurationHistogram, -} - -impl WindowFrameSnapshot { - fn new() -> Self { - Self { - dirty_to_draw: Histogram::new(3).expect("3 significant digits is valid"), - draw: Histogram::new(3).expect("3 significant digits is valid"), - present_interval: Histogram::new(3).expect("3 significant digits is valid"), - invalidations_per_frame: Histogram::new(3).expect("3 significant digits is valid"), - foreground_work: DurationHistogram::new(), - } - } - - fn is_empty(&self) -> bool { - self.dirty_to_draw.is_empty() - && self.draw.is_empty() - && self.present_interval.is_empty() - && self.foreground_work.histogram.is_empty() - } -} - -/// A duration histogram paired with an exact running total, since the -/// histogram's bucketed values (3 significant digits) approximate a sum less -/// precisely than tracking it directly. -struct DurationHistogram { - histogram: Histogram, - total_nanos: u64, -} - -impl DurationHistogram { - fn new() -> Self { - Self { - histogram: Histogram::new(3).expect("3 significant digits is valid"), - total_nanos: 0, - } - } - - fn record(&mut self, duration: Duration) { - let nanos = duration.as_nanos() as u64; - // Infallible: the histogram auto-resizes. - self.histogram.record(nanos).ok(); - self.total_nanos += nanos; - } -} - -fn format_duration(duration: Duration) -> String { - format!("{:.3}ms", duration.as_secs_f64() * 1000.) -} - -/// Enables profiler tracing for a measurement and collects its frame events -/// and foreground journal entries. -/// -/// The previous tracing state is restored on drop, so a panicking measurement -/// doesn't leave tracing enabled for unrelated code such as a later benchmark -/// in the same process. -/// -/// The foreground journal collector is created at the same point, so -/// foreground work recorded before the scope starts (e.g. per-iteration -/// setup) is excluded from what [`Self::finish`] returns: a collector only -/// observes entries recorded after its creation. -struct TraceScope { - collector: FrameTimingCollector, - journal_collector: ForegroundJournalCollector, - _trace_guard: profiler::TraceGuard, -} - -impl TraceScope { - fn start(journal_collector: ForegroundJournalCollector) -> Self { - let trace_guard = profiler::trace_scope(); - Self { - collector: FrameTimingCollector::new(), - journal_collector, - _trace_guard: trace_guard, - } - } - - fn finish(mut self) -> TracedEvents { - TracedEvents { - frame_events: self.collector.collect_unseen(), - journal_entries: self.journal_collector.collect_unseen().entries, - } - } -} - -/// Events observed during one [`TraceScope`]. -struct TracedEvents { - frame_events: Vec, - journal_entries: Vec, -} - -impl TracedEvents { - /// Foreground journal entries that describe completed work (task polls, - /// action handlers, input dispatches, draws, presents, and folded - /// sub-floor polls), excluding interval boundaries and metadata. - fn foreground_events(&self) -> impl Iterator { - self.journal_entries.iter().filter_map(|entry| match entry { - ForegroundJournalEntry::Event(event) => Some(event), - _ => None, - }) - } -} - -struct MeasuredTaskInput { - input: Input, - trace_scope: Option, -} - -struct MeasuredTaskOutput { - trace_scope: Option, - report: BenchReport, - _output: Output, -} - -impl Drop for MeasuredTaskOutput { - fn drop(&mut self) { - let trace_scope = self - .trace_scope - .take() - .expect("measured task output should retain its trace scope"); - let events = trace_scope.finish(); - self.report.record_frame_timings(events.frame_events.iter()); - self.report - .record_foreground_events(events.foreground_events()); - } -} - -fn run_task_to_completion( - foreground_executor: &ForegroundExecutor, - task: Task, -) -> Output -where - Output: 'static, -{ - let output = Rc::new(RefCell::new(None)); - foreground_executor - .spawn({ - let output = output.clone(); - async move { - *output.borrow_mut() = Some(task.await); - } - }) - .detach(); - - foreground_executor - .dispatcher() - .as_threaded() - .expect("BenchAppContext requires a ThreadedDispatcher") - .run_until(|| output.borrow_mut().take()) -} - -/// A GPUI app context for Criterion benchmarks. -/// -/// `BenchAppContext` is intentionally separate from `TestAppContext`: it owns a -/// benchmark app instance and exposes only the app/window operations needed by -/// benchmark setup. Criterion remains responsible for the measured loop via its -/// `Bencher` API. -#[derive(Clone)] -pub struct BenchAppContext<'a, 'measurement> { - app: Rc, - background_executor: BackgroundExecutor, - foreground_executor: ForegroundExecutor, - benchmark_name: Option<&'static str>, - bencher: Rc>>>, - report: BenchReport, -} - -impl<'a, 'measurement> BenchAppContext<'a, 'measurement> { - /// Creates a new benchmark app context backed by the provided platform. - /// - /// The platform's executors must be backed by a [`ThreadedDispatcher`] - /// (see [`bench_platform`]) so the context can drain foreground work via - /// [`Self::run_until_idle`]; panics otherwise. - pub fn new( - platform: Rc, - benchmark_name: Option<&'static str>, - bencher: &'a mut criterion::Bencher<'measurement>, - ) -> Self { - Self::build(platform, benchmark_name, bencher, BenchReport::default()) - } - - /// Creates a new benchmark app context backed by the provided platform. - /// - /// The platform's executors must be backed by a [`ThreadedDispatcher`] - /// (see [`bench_platform`]) so the context can drain foreground work via - /// [`Self::run_until_idle`]; panics otherwise. - #[doc(hidden)] - pub fn new_with_platform_and_report( - platform: Rc, - benchmark_name: Option<&'static str>, - bencher: &'a mut criterion::Bencher<'measurement>, - report: BenchReport, - ) -> Self { - Self::build(platform, benchmark_name, bencher, report) - } - - fn build( - platform: Rc, - benchmark_name: Option<&'static str>, - bencher: &'a mut criterion::Bencher<'measurement>, - report: BenchReport, - ) -> Self { - let background_executor = platform.background_executor(); - // Validate up front so misconfiguration fails at construction with a - // clear message instead of deep inside `run_until_idle`. - assert!( - background_executor.dispatcher().as_threaded().is_some(), - "BenchAppContext requires a platform whose executors are backed by a \ - ThreadedDispatcher; construct one with gpui::bench_platform" - ); - let foreground_executor = platform.foreground_executor(); - let asset_source = Arc::new(()); - // Benchmark setup must not make accidental network requests. The - // production `BlockedHttpClient` reports them without enabling a - // configurable test double through `test-support`. - let http_client: Arc = - Arc::new(http_client::BlockedHttpClient::new()); - let app = App::new_app(platform, asset_source, http_client); - - Self { - app, - background_executor, - foreground_executor, - benchmark_name, - bencher: Rc::new(RefCell::new(Some(bencher))), - report, - } - } - - /// The benchmark function name that created this context. - pub fn benchmark_name(&self) -> Option<&'static str> { - self.benchmark_name - } - - /// Returns the background executor used by this benchmark app. - pub fn background_executor(&self) -> &BackgroundExecutor { - &self.background_executor - } - - /// Returns the foreground executor used by this benchmark app. - pub fn foreground_executor(&self) -> &ForegroundExecutor { - &self.foreground_executor - } - - /// Updates the app and flushes synchronous GPUI effects afterward. - pub fn update(&mut self, update: impl FnOnce(&mut App) -> R) -> R { - let mut app = self.app.borrow_mut(); - app.update(update) - } - - /// Reads app state. - pub fn read(&self, read: impl FnOnce(&App) -> R) -> R { - let app = self.app.borrow(); - read(&app) - } - - /// Runs queued foreground tasks on this thread and waits for in flight - /// background work to finish. Timers that aren't due yet are not waited - /// for (see [`ThreadedDispatcher::run_until_idle`]). - pub fn run_until_idle(&self) { - self.background_executor - .dispatcher() - .as_threaded() - .expect("validated in BenchAppContext::build") - .run_until_idle(); - } - - /// Alternates draining queued work with GPUI update cycles until neither - /// makes progress, so state dropped by benchmark code is fully released. - /// - /// Dropped entities are released only inside an update's effect flush, and - /// releases cascade: one flush drops the entities whose handles are gone, - /// their drops release further handles and can queue foreground work, and - /// a later flush collects those. Executor pumping alone never runs a - /// flush, so without this dropped state would linger in the entity map - /// until some woken task happened to run an update. Production gets this - /// cadence for free from frames and input events. - pub fn settle(&mut self) { - let dispatcher = self.background_executor.dispatcher().clone(); - let dispatcher = dispatcher - .as_threaded() - .expect("validated in BenchAppContext::build"); - loop { - self.run_until_idle(); - self.update(|_| ()); - if dispatcher.is_idle() { - return; - } - } - } - - /// Runs main-thread tasks until `ready` returns a value. - /// - /// Unlike [`Self::run_until_idle`], this returns as soon as `ready` - /// reports completion, leaving any remaining queued work pending. - pub fn run_until(&self, ready: impl FnMut() -> Option) -> R { - self.background_executor - .dispatcher() - .as_threaded() - .expect("validated in BenchAppContext::build") - .run_until(ready) - } - - /// Creates a collector observing foreground journal entries recorded - /// from this point on, for use by a new [`TraceScope`]. - fn foreground_journal_collector(&self) -> ForegroundJournalCollector { - self.read(|app| app.foreground_journal().collector()) - } - - /// Measures a generic benchmark workload using Criterion's iteration loop. - /// - /// The closure is invoked once per Criterion iteration with this - /// benchmark app context so it can update GPUI state. - /// - /// Any window draws triggered by the workload are recorded into the - /// benchmark's frame report through the GPUI frame profiler. - pub fn bench_iter(&mut self, mut benchmark: impl FnMut(&mut Self)) { - let bencher = self.take_bencher("bench_iter"); - let collector = TraceScope::start(self.foreground_journal_collector()); - let mut benchmark = || benchmark(self); - bencher.iter(&mut benchmark); - let events = collector.finish(); - self.report.record_frame_timings(events.frame_events.iter()); - self.report - .record_foreground_events(events.foreground_events()); - self.replace_bencher(bencher); - } - - /// Measures a GPUI task to completion using Criterion's iteration loop. - /// - /// The closure is invoked once per Criterion iteration. The returned task - /// may depend on foreground work, background work, timers, or external - /// workers that wake GPUI tasks. Its output is dropped after the timed - /// interval. - /// - /// Any window draws triggered by the task are recorded into the benchmark's - /// frame report through the GPUI frame profiler. - pub fn bench_task(&mut self, mut benchmark: impl FnMut(&mut Self) -> Task) - where - Output: 'static, - { - self.bench_batched_task_internal("bench_task", |_| (), |_, cx| benchmark(cx)); - } - - /// Measures a GPUI task with per-iteration setup outside the timed interval. - /// - /// `setup` runs before timing starts. The returned input is passed by mutable - /// reference to `benchmark`, which returns the task whose completion is - /// measured. Both the setup input and task output are dropped after timing - /// stops. - /// - /// Each iteration is kept in its own Criterion batch so profiler tracing and - /// destruction cannot overlap adjacent measurements. - pub fn bench_batched_task( - &mut self, - setup: impl FnMut(&mut Self) -> Input, - benchmark: impl FnMut(&mut Input, &mut Self) -> Task, - ) where - Output: 'static, - { - self.bench_batched_task_internal("bench_batched_task", setup, benchmark); - } - - fn bench_batched_task_internal( - &mut self, - benchmark_kind: &str, - mut setup: impl FnMut(&mut Self) -> Input, - mut benchmark: impl FnMut(&mut Input, &mut Self) -> Task, - ) where - Output: 'static, - { - let bencher = self.take_bencher(benchmark_kind); - let mut setup_context = self.clone(); - let mut benchmark_context = self.clone(); - let foreground_executor = self.foreground_executor.clone(); - let report = self.report.clone(); - - bencher.iter_batched_ref( - || { - // The previous iteration's input and output were just - // dropped; settling here releases their entities before the - // next setup, so per-iteration state cannot accumulate - // across a measurement. - setup_context.settle(); - MeasuredTaskInput { - input: setup(&mut setup_context), - trace_scope: Some(TraceScope::start( - setup_context.foreground_journal_collector(), - )), - } - }, - |measured_input| { - let task = benchmark(&mut measured_input.input, &mut benchmark_context); - let output = run_task_to_completion(&foreground_executor, task); - MeasuredTaskOutput { - trace_scope: measured_input.trace_scope.take(), - report: report.clone(), - _output: output, - } - }, - criterion::BatchSize::PerIteration, - ); - self.replace_bencher(bencher); - } - - /// Measures frame latency after updating a GPUI entity in its current window. - /// - /// Each iteration runs `update` against the entity in its current window. In - /// bench builds, flushing the update's effects synchronously draws dirty - /// windows. The entity should be part of the window's render tree, such as the - /// root view or a child of it. - /// - /// Frame events are collected through the GPUI frame profiler - /// ([`crate::profiler::record_frame_event`]), which is enabled for the - /// duration of the measurement. - pub fn bench_renderer( - &mut self, - view: Entity, - mut update: impl FnMut(&mut V, &mut Window, &mut Context), - ) where - V: 'static + Render, - { - let bencher = self.take_bencher("bench_renderer"); - let window_id = self - .with_window(view.entity_id(), |window, _| { - window.window_handle().window_id() - }) - .expect("cannot benchmark renderer for entity without a current window"); - - let dispatcher = self.background_executor.dispatcher().clone(); - let collector = TraceScope::start(self.foreground_journal_collector()); - - let mut benchmark = || { - // Work already queued at frame start delays the frame in - // production too, so run it inside the measured interval. - dispatcher - .as_threaded() - .expect("validated in BenchAppContext::build") - .run_ready_main_tasks(); - self.with_window(view.entity_id(), |window, cx| { - view.update(cx, |view, cx| update(view, window, cx)); - }) - .expect("cannot benchmark renderer for entity without a current window"); - // Submit the frame drawn by the update's effect flush, mirroring - // production where every drawn frame is presented. With a headless - // renderer this includes scene submission to the GPU. - self.with_window(view.entity_id(), |window, _| { - window.present_if_needed(); - }) - .expect("cannot benchmark renderer for entity without a current window"); - }; - bencher.iter(&mut benchmark); - - let events = collector.finish(); - self.report - .record_frame_timings(events.frame_events.iter().filter(|event| match event { - FrameEvent::Draw(timing) => timing.window_id == window_id, - FrameEvent::Present(timing) => timing.window_id == window_id, - })); - // Foreground work isn't attributed to a window, so unlike frame - // timings above it isn't filtered by `window_id`. A benchmark app - // hosts one window at a time, so this cannot pick up unrelated - // windows' work. - self.report - .record_foreground_events(events.foreground_events()); - self.replace_bencher(bencher); - } - - /// Adds a window with an empty root view for benchmark setup. - pub fn add_empty_window(&mut self) -> BenchWindowContext<'a, 'measurement> { - let bounds = { - let app = self.app.borrow(); - Bounds::maximized(None, &app) - }; - let window = { - let mut app = self.app.borrow_mut(); - let window: AnyWindowHandle = app - .open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| cx.new(|_| Empty), - ) - .expect("failed to open benchmark window") - .into(); - window - }; - - self.run_until_idle(); - BenchWindowContext { - cx: self.clone(), - window, - } - } - - fn take_bencher(&self, benchmark_kind: &str) -> &'a mut criterion::Bencher<'measurement> { - self.bencher.borrow_mut().take().unwrap_or_else(|| { - panic!("cannot start {benchmark_kind}: benchmark measurement is already running") - }) - } - - fn replace_bencher(&self, bencher: &'a mut criterion::Bencher<'measurement>) { - let previous = self.bencher.borrow_mut().replace(bencher); - assert!( - previous.is_none(), - "benchmark bencher was unexpectedly present after measurement" - ); - } - - /// Runs GPUI benchmark teardown. - /// - /// Cancels any timers still armed on the shared dispatcher and drains the - /// work that cancellation unblocks so they can't fire during a later - /// benchmark; assumes no other `BenchAppContext` is live on this thread. - pub fn teardown(mut self) { - self.run_until_idle(); - self.update(|cx| { - cx.quit(); - }); - self.run_until_idle(); - - let dispatcher = self.background_executor.dispatcher(); - let dispatcher = dispatcher - .as_threaded() - .expect("validated in BenchAppContext::build"); - - drop(self.app); - drop(self.foreground_executor); - - for _ in 0..100 { - if dispatcher.cancel_pending_timers() == 0 { - return; - } - dispatcher.run_until_idle(); - } - panic!( - "benchmark teardown kept scheduling timers: {}", - dispatcher.debug_state() - ); - } -} - -impl AppContext for BenchAppContext<'_, '_> { - fn new(&mut self, build_entity: impl FnOnce(&mut Context) -> T) -> Entity { - let mut app = self.app.borrow_mut(); - app.new(build_entity) - } - - fn reserve_entity(&mut self) -> Reservation { - let mut app = self.app.borrow_mut(); - app.reserve_entity() - } - - fn insert_entity( - &mut self, - reservation: Reservation, - build_entity: impl FnOnce(&mut Context) -> T, - ) -> Entity { - let mut app = self.app.borrow_mut(); - app.insert_entity(reservation, build_entity) - } - - fn update_entity( - &mut self, - handle: &Entity, - update: impl FnOnce(&mut T, &mut Context) -> R, - ) -> R { - let mut app = self.app.borrow_mut(); - app.update_entity(handle, update) - } - - fn as_mut<'b, T>(&'b mut self, _: &Entity) -> GpuiBorrow<'b, T> - where - T: 'static, - { - panic!("Cannot use as_mut with BenchAppContext. Call update() instead.") - } - - fn read_entity(&self, handle: &Entity, read: impl FnOnce(&T, &App) -> R) -> R - where - T: 'static, - { - let app = self.app.borrow(); - app.read_entity(handle, read) - } - - fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result - where - F: FnOnce(AnyView, &mut Window, &mut App) -> T, - { - let mut app = self.app.borrow_mut(); - app.update_window(window, update) - } - - fn with_window( - &mut self, - entity_id: EntityId, - update: impl FnOnce(&mut Window, &mut App) -> R, - ) -> Option { - let mut app = self.app.borrow_mut(); - app.with_window(entity_id, update) - } - - fn read_window( - &self, - window: &WindowHandle, - read: impl FnOnce(Entity, &App) -> R, - ) -> Result - where - T: 'static, - { - let app = self.app.borrow(); - app.read_window(window, read) - } - - fn background_spawn(&self, future: impl Future + Send + 'static) -> Task - where - R: Send + 'static, - { - self.background_executor.spawn(future) - } - - fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> R - where - G: Global, - { - let app = self.app.borrow(); - app.read_global(callback) - } -} - -/// A window-specific context for GPUI benchmarks. -/// -/// This is separate from `VisualTestContext`; it provides access to a benchmark -/// window without exposing test-only helpers such as input simulation. -#[derive(Clone)] -pub struct BenchWindowContext<'a, 'measurement> { - cx: BenchAppContext<'a, 'measurement>, - window: AnyWindowHandle, -} - -impl<'a, 'measurement> BenchWindowContext<'a, 'measurement> { - /// Returns the underlying benchmark app context. - pub fn app_context(&mut self) -> &mut BenchAppContext<'a, 'measurement> { - &mut self.cx - } - - /// Returns the window associated with this context. - pub fn window_handle(&self) -> AnyWindowHandle { - self.window - } - - /// Runs queued foreground tasks on this thread and waits for in-flight - /// background work to finish. Pending timers are not waited for. - pub fn run_until_idle(&self) { - self.cx.run_until_idle(); - } - - /// Updates the benchmark window. - pub fn update(&mut self, update: impl FnOnce(&mut Window, &mut App) -> R) -> R { - self.cx - .update_window(self.window, |_, window, cx| update(window, cx)) - .expect("benchmark window was unexpectedly closed") - } -} - -impl AppContext for BenchWindowContext<'_, '_> { - fn new(&mut self, build_entity: impl FnOnce(&mut Context) -> T) -> Entity { - self.window - .update(&mut self.cx, |_, _, cx| cx.new(build_entity)) - .expect("benchmark window was unexpectedly closed") - } - - fn reserve_entity(&mut self) -> Reservation { - self.cx.reserve_entity() - } - - fn insert_entity( - &mut self, - reservation: Reservation, - build_entity: impl FnOnce(&mut Context) -> T, - ) -> Entity { - self.window - .update(&mut self.cx, |_, _, cx| { - cx.insert_entity(reservation, build_entity) - }) - .expect("benchmark window was unexpectedly closed") - } - - fn update_entity( - &mut self, - handle: &Entity, - update: impl FnOnce(&mut T, &mut Context) -> R, - ) -> R { - self.cx.update_entity(handle, update) - } - - fn as_mut<'b, T>(&'b mut self, handle: &Entity) -> GpuiBorrow<'b, T> - where - T: 'static, - { - self.cx.as_mut(handle) - } - - fn read_entity(&self, handle: &Entity, read: impl FnOnce(&T, &App) -> R) -> R - where - T: 'static, - { - self.cx.read_entity(handle, read) - } - - fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result - where - F: FnOnce(AnyView, &mut Window, &mut App) -> T, - { - self.cx.update_window(window, update) - } - - fn with_window( - &mut self, - entity_id: EntityId, - update: impl FnOnce(&mut Window, &mut App) -> R, - ) -> Option { - self.cx.with_window(entity_id, update) - } - - fn read_window( - &self, - window: &WindowHandle, - read: impl FnOnce(Entity, &App) -> R, - ) -> Result - where - T: 'static, - { - self.cx.read_window(window, read) - } - - fn background_spawn(&self, future: impl Future + Send + 'static) -> Task - where - R: Send + 'static, - { - self.cx.background_spawn(future) - } - - fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> R - where - G: Global, - { - self.cx.read_global(callback) - } -} - -impl VisualContext for BenchWindowContext<'_, '_> { - type Result = Result; - - fn window_handle(&self) -> AnyWindowHandle { - self.window - } - - fn update_window_entity( - &mut self, - entity: &Entity, - update: impl FnOnce(&mut T, &mut Window, &mut Context) -> R, - ) -> Result { - let entity = entity.clone(); - self.cx - .app - .borrow_mut() - .with_window(entity.entity_id(), |window, app| { - entity.update(app, |entity, cx| update(entity, window, cx)) - }) - .ok_or_else(|| { - anyhow!("entity has no current window; use `update` instead of `update_in`") - }) - } - - fn new_window_entity( - &mut self, - build_entity: impl FnOnce(&mut Window, &mut Context) -> T, - ) -> Result> { - self.window.update(&mut self.cx, |_, window, cx| { - cx.new(|cx| build_entity(window, cx)) - }) - } - - fn replace_root_view( - &mut self, - build_view: impl FnOnce(&mut Window, &mut Context) -> V, - ) -> Result> - where - V: 'static + Render, - { - self.window.update(&mut self.cx, |_, window, cx| { - window.replace_root(cx, build_view) - }) - } - - fn focus(&mut self, entity: &Entity) -> Result<()> - where - V: Focusable, - { - self.window.update(&mut self.cx, |_, window, cx| { - entity.read(cx).focus_handle(cx).focus(window, cx) - }) - } -} - -#[cfg(test)] -mod tests { - use std::{rc::Rc, sync::Arc}; - - use super::*; - use crate::profiler::journal::install_test_foreground_journal; - - #[test] - fn foreground_work_reports_long_task_without_window_draw() { - let (journal, _journal_guard) = install_test_foreground_journal(1024, 64); - let dispatcher = Arc::new(ThreadedDispatcher::new()); - let foreground_executor = ForegroundExecutor::new(dispatcher); - - let trace_scope = TraceScope::start(journal.collector()); - - // A single foreground task poll that never touches a window, akin - // to the stall a debounced background computation can cause. - let task = foreground_executor.spawn(async move { - std::thread::sleep(Duration::from_millis(60)); - }); - run_task_to_completion(&foreground_executor, task); - - let events = trace_scope.finish(); - assert!( - events.frame_events.is_empty(), - "no window was involved, so no frame events should be recorded" - ); - - let report = BenchReport::default(); - report.record_foreground_events(events.foreground_events()); - - let summary = report - .foreground_work() - .expect("a long task poll should be reported even without a window draw"); - // The spawned task's own poll is one sample; the tiny wrapper poll - // that observes its completion in `run_task_to_completion` folds - // into a second, near-zero sample rather than being dropped. - assert!(summary.count >= 1, "expected at least one recorded item"); - assert!( - summary.max >= Duration::from_millis(55), - "expected the long poll's duration to be recorded, got {:?}", - summary.max - ); - // `total` is an exact sum, while `max` may be rounded up to its - // histogram bucket's boundary, so compare each against the expected - // floor directly instead of against each other. - assert!( - summary.total >= Duration::from_millis(55), - "expected the long poll's duration to be included in the total, got {:?}", - summary.total - ); - } - - #[test] - fn foreground_work_excludes_setup_before_trace_scope_starts() { - let (journal, _journal_guard) = install_test_foreground_journal(1024, 64); - let dispatcher = Arc::new(ThreadedDispatcher::new()); - let foreground_executor = ForegroundExecutor::new(dispatcher); - - // Fixture/setup work that must not be attributed to the measurement: - // a long poll recorded before the trace scope (and its journal - // collector) is created. - let setup_task = foreground_executor.spawn(async move { - std::thread::sleep(Duration::from_millis(80)); - }); - run_task_to_completion(&foreground_executor, setup_task); - - let trace_scope = TraceScope::start(journal.collector()); - - let measured_task = foreground_executor.spawn(async move { - std::thread::sleep(Duration::from_millis(10)); - }); - run_task_to_completion(&foreground_executor, measured_task); - - let events = trace_scope.finish(); - let report = BenchReport::default(); - report.record_foreground_events(events.foreground_events()); - - let summary = report - .foreground_work() - .expect("the measured task's poll should be reported"); - assert!( - summary.max < Duration::from_millis(40), - "setup work's 80ms poll must not leak into the measured summary, got {:?}", - summary.max - ); - assert!( - summary.total < Duration::from_millis(40), - "setup work's 80ms poll must not leak into the measured total, got {:?}", - summary.total - ); - } - - #[test] - fn bench_task_reports_long_task_without_window() { - let platform = bench_platform(None, Arc::new(crate::NoopTextSystem::new())); - let report = BenchReport::default(); - let name = "bench_task_reports_long_task_without_window"; - - let mut criterion = criterion::Criterion::default() - .without_plots() - .sample_size(10) - .warm_up_time(Duration::from_millis(1)) - .measurement_time(Duration::from_millis(1)); - - criterion.bench_function(name, |bencher| { - let mut cx = BenchAppContext::new_with_platform_and_report( - platform.clone(), - Some(name), - bencher, - report.clone(), - ); - cx.bench_task(|cx| { - cx.foreground_executor().spawn(async move { - std::thread::sleep(Duration::from_millis(20)); - }) - }); - cx.teardown(); - }); - - let summary = report - .foreground_work() - .expect("bench_task should report foreground work with no window involved"); - assert!( - summary.max >= Duration::from_millis(15), - "expected a ~20ms task poll to be recorded, got {:?}", - summary.max - ); - } - - #[test] - fn task_completion_supports_non_send_foreground_output() { - let dispatcher = Arc::new(ThreadedDispatcher::new()); - let background_executor = BackgroundExecutor::new(dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(dispatcher); - let (sender, receiver) = futures::channel::oneshot::channel(); - - background_executor - .spawn(async move { - sender - .send(()) - .expect("foreground receiver should remain alive"); - }) - .detach(); - let expected_output = Rc::new(42); - let task_output = expected_output.clone(); - let task = foreground_executor.spawn(async move { - receiver.await.expect("background task should send a value"); - task_output - }); - - let output = run_task_to_completion(&foreground_executor, task); - assert!( - Rc::ptr_eq(&output, &expected_output), - "task runner should preserve non-Send foreground output" - ); - } -} diff --git a/crates/gpui_pre/src/app/context.rs b/crates/gpui_pre/src/app/context.rs deleted file mode 100644 index 0d1ee47c..00000000 --- a/crates/gpui_pre/src/app/context.rs +++ /dev/null @@ -1,883 +0,0 @@ -use crate::{ - AnyView, AnyWindowHandle, AppContext, AsyncApp, DispatchPhase, Effect, EntityId, EventEmitter, - FocusHandle, FocusOutEvent, Focusable, Global, KeystrokeObserver, Priority, Reservation, - SubscriberSet, Subscription, Task, WeakEntity, WeakFocusHandle, Window, WindowHandle, -}; -use anyhow::Result; -use futures::FutureExt; -use gpui_util::Deferred; -use std::{ - any::{Any, TypeId}, - borrow::{Borrow, BorrowMut}, - future::Future, - ops, - sync::Arc, -}; - -use super::{App, AsyncWindowContext, Entity, KeystrokeEvent}; - -/// The app context, with specialized behavior for the given entity. -pub struct Context<'a, T> { - app: &'a mut App, - entity_state: WeakEntity, -} - -impl<'a, T> ops::Deref for Context<'a, T> { - type Target = App; - - fn deref(&self) -> &Self::Target { - self.app - } -} - -impl<'a, T> ops::DerefMut for Context<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.app - } -} - -impl<'a, T: 'static> Context<'a, T> { - pub(crate) fn new_context(app: &'a mut App, entity_state: WeakEntity) -> Self { - Self { app, entity_state } - } - - /// The entity id of the entity backing this context. - pub fn entity_id(&self) -> EntityId { - self.entity_state.entity_id - } - - /// Returns a handle to the entity belonging to this context. - pub fn entity(&self) -> Entity { - self.weak_entity() - .upgrade() - .expect("The entity must be alive if we have a entity context") - } - - /// Returns a weak handle to the entity belonging to this context. - pub fn weak_entity(&self) -> WeakEntity { - self.entity_state.clone() - } - - /// Arranges for the given function to be called whenever [`Context::notify`] is - /// called with the given entity. - pub fn observe( - &mut self, - entity: &Entity, - mut on_notify: impl FnMut(&mut T, Entity, &mut Context) + 'static, - ) -> Subscription - where - T: 'static, - W: 'static, - { - let this = self.weak_entity(); - self.app.observe_internal(entity, move |e, cx| { - if let Some(this) = this.upgrade() { - this.update(cx, |this, cx| on_notify(this, e, cx)); - true - } else { - false - } - }) - } - - /// Observe changes to ourselves - pub fn observe_self( - &mut self, - mut on_event: impl FnMut(&mut T, &mut Context) + 'static, - ) -> Subscription - where - T: 'static, - { - let this = self.entity(); - self.app.observe(&this, move |this, cx| { - this.update(cx, |this, cx| on_event(this, cx)) - }) - } - - /// Subscribe to an event type from another entity - pub fn subscribe( - &mut self, - entity: &Entity, - mut on_event: impl FnMut(&mut T, Entity, &Evt, &mut Context) + 'static, - ) -> Subscription - where - T: 'static, - T2: 'static + EventEmitter, - Evt: 'static, - { - let this = self.weak_entity(); - self.app.subscribe_internal(entity, move |e, event, cx| { - if let Some(this) = this.upgrade() { - this.update(cx, |this, cx| on_event(this, e, event, cx)); - true - } else { - false - } - }) - } - - /// Subscribe to an event type from ourself - pub fn subscribe_self( - &mut self, - mut on_event: impl FnMut(&mut T, &Evt, &mut Context) + 'static, - ) -> Subscription - where - T: 'static + EventEmitter, - Evt: 'static, - { - let this = self.entity(); - self.app.subscribe(&this, move |this, evt, cx| { - this.update(cx, |this, cx| on_event(this, evt, cx)) - }) - } - - /// Register a callback to be invoked when GPUI releases this entity. - pub fn on_release(&self, on_release: impl FnOnce(&mut T, &mut App) + 'static) -> Subscription - where - T: 'static, - { - let (subscription, activate) = self.app.release_listeners.insert( - self.entity_state.entity_id, - Box::new(move |this, cx| { - let this = this.downcast_mut().expect("invalid entity type"); - on_release(this, cx); - }), - ); - activate(); - subscription - } - - /// Register a callback to be run on the release of another entity - pub fn observe_release( - &self, - entity: &Entity, - on_release: impl FnOnce(&mut T, &mut T2, &mut Context) + 'static, - ) -> Subscription - where - T: Any, - T2: 'static, - { - let entity_id = entity.entity_id(); - let this = self.weak_entity(); - let (subscription, activate) = self.app.release_listeners.insert( - entity_id, - Box::new(move |entity, cx| { - let entity = entity.downcast_mut().expect("invalid entity type"); - if let Some(this) = this.upgrade() { - this.update(cx, |this, cx| on_release(this, entity, cx)); - } - }), - ); - activate(); - subscription - } - - /// Register a callback to for updates to the given global - pub fn observe_global( - &mut self, - mut f: impl FnMut(&mut T, &mut Context) + 'static, - ) -> Subscription - where - T: 'static, - { - let handle = self.weak_entity(); - let (subscription, activate) = self.global_observers.insert( - TypeId::of::(), - Box::new(move |cx| handle.update(cx, |view, cx| f(view, cx)).is_ok()), - ); - self.defer(move |_| activate()); - subscription - } - - /// Register a callback to be invoked when the application is about to restart. - pub fn on_app_restart( - &self, - mut on_restart: impl FnMut(&mut T, &mut App) + 'static, - ) -> Subscription - where - T: 'static, - { - let handle = self.weak_entity(); - self.app.on_app_restart(move |cx| { - handle.update(cx, |entity, cx| on_restart(entity, cx)).ok(); - }) - } - - /// Arrange for the given function to be invoked whenever the application is quit. - /// The future returned from this callback will be polled for up to [crate::SHUTDOWN_TIMEOUT] until the app fully quits. - pub fn on_app_quit( - &self, - mut on_quit: impl FnMut(&mut T, &mut Context) -> Fut + 'static, - ) -> Subscription - where - Fut: 'static + Future, - T: 'static, - { - let handle = self.weak_entity(); - self.app.on_app_quit(move |cx| { - let future = handle.update(cx, |entity, cx| on_quit(entity, cx)).ok(); - async move { - if let Some(future) = future { - future.await; - } - } - .boxed_local() - }) - } - - /// Tell GPUI that this entity has changed and observers of it should be notified. - pub fn notify(&mut self) { - self.app.notify(self.entity_state.entity_id); - } - - /// Spawn the future returned by the given function. - /// The function is provided a weak handle to the entity owned by this context and a context that can be held across await points. - /// The returned task must be held or detached. - #[track_caller] - pub fn spawn(&self, f: AsyncFn) -> Task - where - T: 'static, - AsyncFn: AsyncFnOnce(WeakEntity, &mut AsyncApp) -> R + 'static, - R: 'static, - { - let this = self.weak_entity(); - self.app.spawn(async move |cx| f(this, cx).await) - } - - /// Convenience method for accessing view state in an event callback. - /// - /// Many GPUI callbacks take the form of `Fn(&E, &mut Window, &mut App)`, - /// but it's often useful to be able to access view state in these - /// callbacks. This method provides a convenient way to do so. - pub fn listener( - &self, - f: impl Fn(&mut T, &E, &mut Window, &mut Context) + 'static, - ) -> impl Fn(&E, &mut Window, &mut App) + 'static { - let view = self.entity().downgrade(); - move |e: &E, window: &mut Window, cx: &mut App| { - view.update(cx, |view, cx| f(view, e, window, cx)).ok(); - } - } - - /// Convenience method for producing view state in a closure. - /// See `listener` for more details. - pub fn processor( - &self, - f: impl Fn(&mut T, E, &mut Window, &mut Context) -> R + 'static, - ) -> impl Fn(E, &mut Window, &mut App) -> R + 'static { - let view = self.entity(); - move |e: E, window: &mut Window, cx: &mut App| { - view.update(cx, |view, cx| f(view, e, window, cx)) - } - } - - /// Run something using this entity and cx, when the returned struct is dropped - pub fn on_drop( - &self, - f: impl FnOnce(&mut T, &mut Context) + 'static, - ) -> Deferred { - let this = self.weak_entity(); - let mut cx = self.to_async(); - gpui_util::defer(move || { - this.update(&mut cx, f).ok(); - }) - } - - /// Focus the given view in the given window. View type is required to implement Focusable. - pub fn focus_view(&mut self, view: &Entity, window: &mut Window) { - window.focus(&view.focus_handle(self), self); - } - - /// Sets a given callback to be run on the next frame. - pub fn on_next_frame( - &self, - window: &mut Window, - f: impl FnOnce(&mut T, &mut Window, &mut Context) + 'static, - ) where - T: 'static, - { - let view = self.entity(); - window.on_next_frame(move |window, cx| view.update(cx, |view, cx| f(view, window, cx))); - } - - /// 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_in( - &mut self, - window: &Window, - f: impl FnOnce(&mut T, &mut Window, &mut Context) + 'static, - ) { - let view = self.weak_entity(); - let entity_id = self.entity_id(); - self.ensure_window(entity_id, window.handle.id); - self.app.defer(move |cx| { - cx.with_window(entity_id, |window, cx| { - view.update(cx, |view, cx| f(view, window, cx)).ok(); - }); - }); - } - - /// Observe another entity for changes to its state, as tracked by [`Context::notify`]. - pub fn observe_in( - &mut self, - observed: &Entity, - window: &mut Window, - mut on_notify: impl FnMut(&mut T, Entity, &mut Window, &mut Context) + 'static, - ) -> Subscription - where - V2: 'static, - T: 'static, - { - let observed_id = observed.entity_id(); - let observed = observed.downgrade(); - let observer = self.weak_entity(); - let observer_id = self.entity_id(); - self.ensure_window(observer_id, window.handle.id); - self.new_observer( - observed_id, - Box::new(move |cx| { - let Some((observer, observed)) = observer.upgrade().zip(observed.upgrade()) else { - return false; - }; - cx.with_window(observer_id, |window, cx| { - observer.update(cx, |observer, cx| { - on_notify(observer, observed, window, cx); - }); - }); - true - }), - ) - } - - /// Subscribe to events emitted by another entity. - /// The entity to which you're subscribing must implement the [`EventEmitter`] trait. - /// The callback will be invoked with a reference to the current view, a handle to the emitting `Entity`, the event, a mutable reference to the `Window`, and the context for the entity. - pub fn subscribe_in( - &mut self, - emitter: &Entity, - window: &Window, - mut on_event: impl FnMut(&mut T, &Entity, &Evt, &mut Window, &mut Context) + 'static, - ) -> Subscription - where - Emitter: EventEmitter, - Evt: 'static, - { - let emitter = emitter.downgrade(); - let subscriber = self.weak_entity(); - let subscriber_id = self.entity_id(); - self.ensure_window(subscriber_id, window.handle.id); - self.new_subscription( - emitter.entity_id(), - ( - TypeId::of::(), - Box::new(move |event, cx| { - let Some((subscriber, emitter)) = subscriber.upgrade().zip(emitter.upgrade()) - else { - return false; - }; - let event = event.downcast_ref().expect("invalid event type"); - cx.with_window(subscriber_id, |window, cx| { - subscriber.update(cx, |subscriber, cx| { - on_event(subscriber, &emitter, event, window, cx); - }); - }); - true - }), - ), - ) - } - - /// Register a callback to be invoked when the view is released. - /// - /// The callback receives a handle to the view's window. This handle may be - /// invalid, if the window was closed before the view was released. - pub fn on_release_in( - &mut self, - window: &Window, - on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static, - ) -> Subscription { - let entity = self.entity(); - self.app.observe_release_in(&entity, window, on_release) - } - - /// Register a callback to be invoked when the given Entity is released. - pub fn observe_release_in( - &self, - observed: &Entity, - window: &Window, - mut on_release: impl FnMut(&mut T, &mut T2, &mut Window, &mut Context) + 'static, - ) -> Subscription - where - T: 'static, - T2: 'static, - { - let observer = self.weak_entity(); - self.app - .observe_release_in(observed, window, move |observed, window, cx| { - observer - .update(cx, |observer, cx| { - on_release(observer, observed, window, cx) - }) - .ok(); - }) - } - - /// Register a callback to be invoked when the window is resized. - pub fn observe_window_bounds( - &self, - window: &mut Window, - mut callback: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, - ) -> Subscription { - let view = self.weak_entity(); - let (subscription, activate) = window.bounds_observers.insert( - (), - Box::new(move |window, cx| { - view.update(cx, |view, cx| callback(view, window, cx)) - .is_ok() - }), - ); - activate(); - subscription - } - - /// Register a callback to be invoked when the window is activated or deactivated. - pub fn observe_window_activation( - &self, - window: &mut Window, - mut callback: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, - ) -> Subscription { - let view = self.weak_entity(); - let (subscription, activate) = window.activation_observers.insert( - (), - Box::new(move |window, cx| { - view.update(cx, |view, cx| callback(view, window, cx)) - .is_ok() - }), - ); - activate(); - subscription - } - - /// Registers a callback to be invoked when the window appearance changes. - pub fn observe_window_appearance( - &self, - window: &mut Window, - mut callback: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, - ) -> Subscription { - let view = self.weak_entity(); - let (subscription, activate) = window.appearance_observers.insert( - (), - Box::new(move |window, cx| { - view.update(cx, |view, cx| callback(view, window, cx)) - .is_ok() - }), - ); - activate(); - subscription - } - - /// Registers a callback to be invoked when the window button layout changes. - pub fn observe_button_layout_changed( - &self, - window: &mut Window, - mut callback: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, - ) -> Subscription { - let view = self.weak_entity(); - let (subscription, activate) = window.button_layout_observers.insert( - (), - Box::new(move |window, cx| { - view.update(cx, |view, cx| callback(view, window, cx)) - .is_ok() - }), - ); - activate(); - subscription - } - - /// Register a callback to be invoked when a keystroke is received by the application - /// in any window. Note that this fires after all other action and event mechanisms have resolved - /// and that this API will not be invoked if the event's propagation is stopped. - pub fn observe_keystrokes( - &mut self, - mut f: impl FnMut(&mut T, &KeystrokeEvent, &mut Window, &mut Context) + 'static, - ) -> Subscription { - fn inner( - keystroke_observers: &SubscriberSet<(), KeystrokeObserver>, - handler: KeystrokeObserver, - ) -> Subscription { - let (subscription, activate) = keystroke_observers.insert((), handler); - activate(); - subscription - } - - let view = self.weak_entity(); - inner( - &self.keystroke_observers, - Box::new(move |event, window, cx| { - if let Some(view) = view.upgrade() { - view.update(cx, |view, cx| f(view, event, window, cx)); - true - } else { - false - } - }), - ) - } - - /// Register a callback to be invoked when the window's pending input changes. - pub fn observe_pending_input( - &self, - window: &mut Window, - mut callback: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, - ) -> Subscription { - let view = self.weak_entity(); - let (subscription, activate) = window.pending_input_observers.insert( - (), - Box::new(move |window, cx| { - view.update(cx, |view, cx| callback(view, window, cx)) - .is_ok() - }), - ); - activate(); - subscription - } - - /// Register a listener to be called when the given focus handle receives focus. - /// Returns a subscription and persists until the subscription is dropped. - pub fn on_focus( - &mut self, - handle: &FocusHandle, - window: &mut Window, - mut listener: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, - ) -> Subscription { - let view = self.weak_entity(); - let focus_id = handle.id; - let (subscription, activate) = - window.new_focus_listener(Box::new(move |event, window, cx| { - view.update(cx, |view, cx| { - if event.previous_focus_path.last() != Some(&focus_id) - && event.current_focus_path.last() == Some(&focus_id) - { - listener(view, window, cx) - } - }) - .is_ok() - })); - self.defer(|_| activate()); - subscription - } - - /// Register a listener to be called when the given focus handle or one of its descendants receives focus. - /// This does not fire if the given focus handle - or one of its descendants - was previously focused. - /// Returns a subscription and persists until the subscription is dropped. - pub fn on_focus_in( - &mut self, - handle: &FocusHandle, - window: &mut Window, - mut listener: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, - ) -> Subscription { - let view = self.weak_entity(); - let focus_id = handle.id; - let (subscription, activate) = - window.new_focus_listener(Box::new(move |event, window, cx| { - view.update(cx, |view, cx| { - if event.is_focus_in(focus_id) { - listener(view, window, cx) - } - }) - .is_ok() - })); - self.defer(|_| activate()); - subscription - } - - /// Register a listener to be called when the given focus handle loses focus. - /// Returns a subscription and persists until the subscription is dropped. - pub fn on_blur( - &mut self, - handle: &FocusHandle, - window: &mut Window, - mut listener: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, - ) -> Subscription { - let view = self.weak_entity(); - let focus_id = handle.id; - let (subscription, activate) = - window.new_focus_listener(Box::new(move |event, window, cx| { - view.update(cx, |view, cx| { - if event.previous_focus_path.last() == Some(&focus_id) - && event.current_focus_path.last() != Some(&focus_id) - { - listener(view, window, cx) - } - }) - .is_ok() - })); - self.defer(|_| activate()); - subscription - } - - /// Register a listener to be called when nothing in the window has focus. - /// This typically happens when the node that was focused is removed from the tree, - /// and this callback lets you chose a default place to restore the users focus. - /// Returns a subscription and persists until the subscription is dropped. - pub fn on_focus_lost( - &mut self, - window: &mut Window, - mut listener: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, - ) -> Subscription { - let view = self.weak_entity(); - let (subscription, activate) = window.focus_lost_listeners.insert( - (), - Box::new(move |window, cx| { - view.update(cx, |view, cx| listener(view, window, cx)) - .is_ok() - }), - ); - self.defer(|_| activate()); - subscription - } - - /// Register a listener to be called when the given focus handle or one of its descendants loses focus. - /// Returns a subscription and persists until the subscription is dropped. - pub fn on_focus_out( - &mut self, - handle: &FocusHandle, - window: &mut Window, - mut listener: impl FnMut(&mut T, FocusOutEvent, &mut Window, &mut Context) + 'static, - ) -> Subscription { - let view = self.weak_entity(); - let focus_id = handle.id; - let (subscription, activate) = - window.new_focus_listener(Box::new(move |event, window, cx| { - view.update(cx, |view, cx| { - if let Some(blurred_id) = event.previous_focus_path.last().copied() - && event.is_focus_out(focus_id) - { - let event = FocusOutEvent { - blurred: WeakFocusHandle { - id: blurred_id, - handles: Arc::downgrade(&cx.focus_handles), - }, - }; - listener(view, event, window, cx) - } - }) - .is_ok() - })); - self.defer(|_| activate()); - subscription - } - - /// Schedule a future to be run asynchronously. - /// The given callback is invoked with a [`WeakEntity`] to avoid leaking the entity for a long-running process. - /// It's also given an [`AsyncWindowContext`], which can be used to access the state of the entity across await points. - /// The returned future will be polled on the main thread. - #[track_caller] - pub fn spawn_in(&self, window: &Window, f: AsyncFn) -> Task - where - R: 'static, - AsyncFn: AsyncFnOnce(WeakEntity, &mut AsyncWindowContext) -> R + 'static, - { - let view = self.weak_entity(); - window.spawn(self, async move |cx| f(view, cx).await) - } - - /// Schedule a future to be run asynchronously with the given priority. - /// The given callback is invoked with a [`WeakEntity`] to avoid leaking the entity for a long-running process. - /// It's also given an [`AsyncWindowContext`], which can be used to access the state of the entity across await points. - /// The returned future will be polled on the main thread. - #[track_caller] - pub fn spawn_in_with_priority( - &self, - priority: Priority, - window: &Window, - f: AsyncFn, - ) -> Task - where - R: 'static, - AsyncFn: AsyncFnOnce(WeakEntity, &mut AsyncWindowContext) -> R + 'static, - { - let view = self.weak_entity(); - window.spawn_with_priority(priority, self, async move |cx| f(view, cx).await) - } - - /// Register a callback to be invoked when the given global state changes. - pub fn observe_global_in( - &mut self, - window: &Window, - mut f: impl FnMut(&mut T, &mut Window, &mut Context) + 'static, - ) -> Subscription { - let window_handle = window.handle; - let view = self.weak_entity(); - let (subscription, activate) = self.global_observers.insert( - TypeId::of::(), - Box::new(move |cx| { - // If the entity has been dropped, remove this observer. - if view.upgrade().is_none() { - return false; - } - // If the window is unavailable (e.g. temporarily taken during a - // nested update, or already closed), skip this notification but - // keep the observer alive so it can fire on future changes. - let Ok(entity_alive) = window_handle.update(cx, |_, window, cx| { - view.update(cx, |view, cx| f(view, window, cx)).is_ok() - }) else { - return true; - }; - entity_alive - }), - ); - self.defer(move |_| activate()); - subscription - } - - /// Register a callback to be invoked when the given Action type is dispatched to the window. - pub fn on_action( - &mut self, - action_type: TypeId, - window: &mut Window, - listener: impl Fn(&mut T, &dyn Any, DispatchPhase, &mut Window, &mut Context) + 'static, - ) { - let handle = self.weak_entity(); - window.on_action(action_type, move |action, phase, window, cx| { - handle - .update(cx, |view, cx| { - listener(view, action, phase, window, cx); - }) - .ok(); - }); - } - - /// Move focus to the current view, assuming it implements [`Focusable`]. - pub fn focus_self(&mut self, window: &mut Window) - where - T: Focusable, - { - let view = self.entity(); - window.defer(self, move |window, cx| { - view.read(cx).focus_handle(cx).focus(window, cx) - }) - } -} - -impl Context<'_, T> { - /// Emit an event of the specified type, which can be handled by other entities that have subscribed via `subscribe` methods on their respective contexts. - pub fn emit(&mut self, event: Evt) - where - T: EventEmitter, - Evt: 'static, - { - let event = self - .event_arena - .alloc(|| event) - .map(|it| it as &mut dyn Any); - self.app.pending_effects.push_back(Effect::Emit { - emitter: self.entity_state.entity_id, - event_type: TypeId::of::(), - event, - }); - } -} - -impl AppContext for Context<'_, T> { - #[inline] - fn new(&mut self, build_entity: impl FnOnce(&mut Context) -> U) -> Entity { - self.app.new(build_entity) - } - - #[inline] - fn reserve_entity(&mut self) -> Reservation { - self.app.reserve_entity() - } - - #[inline] - fn insert_entity( - &mut self, - reservation: Reservation, - build_entity: impl FnOnce(&mut Context) -> U, - ) -> Entity { - self.app.insert_entity(reservation, build_entity) - } - - #[inline] - fn update_entity( - &mut self, - handle: &Entity, - update: impl FnOnce(&mut U, &mut Context) -> R, - ) -> R { - self.app.update_entity(handle, update) - } - - #[inline] - fn as_mut<'a, E>(&'a mut self, handle: &Entity) -> super::GpuiBorrow<'a, E> - where - E: 'static, - { - self.app.as_mut(handle) - } - - #[inline] - fn read_entity(&self, handle: &Entity, read: impl FnOnce(&U, &App) -> R) -> R - where - U: 'static, - { - self.app.read_entity(handle, read) - } - - #[inline] - fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result - where - F: FnOnce(AnyView, &mut Window, &mut App) -> R, - { - self.app.update_window(window, update) - } - - #[inline] - fn with_window( - &mut self, - entity_id: EntityId, - f: impl FnOnce(&mut Window, &mut App) -> R, - ) -> Option { - self.app.with_window(entity_id, f) - } - - #[inline] - fn read_window( - &self, - window: &WindowHandle, - read: impl FnOnce(Entity, &App) -> R, - ) -> Result - where - U: 'static, - { - self.app.read_window(window, read) - } - - #[inline] - fn background_spawn(&self, future: impl Future + Send + 'static) -> Task - where - R: Send + 'static, - { - self.app.background_executor.spawn(future) - } - - #[inline] - fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> R - where - G: Global, - { - self.app.read_global(callback) - } -} - -impl Borrow for Context<'_, T> { - fn borrow(&self) -> &App { - self.app - } -} - -impl BorrowMut for Context<'_, T> { - fn borrow_mut(&mut self) -> &mut App { - self.app - } -} diff --git a/crates/gpui_pre/src/app/entity_map.rs b/crates/gpui_pre/src/app/entity_map.rs deleted file mode 100644 index e4e9f3b5..00000000 --- a/crates/gpui_pre/src/app/entity_map.rs +++ /dev/null @@ -1,1278 +0,0 @@ -use crate::{App, AppContext, GpuiBorrow, VisualContext, Window, seal::Sealed}; -use anyhow::{Context as _, Result}; -use collections::FxHashSet; -use derive_more::{Deref, DerefMut}; -use parking_lot::{RwLock, RwLockUpgradableReadGuard}; -use slotmap::{KeyData, SecondaryMap, SlotMap}; -use std::{ - any::{Any, TypeId, type_name}, - cell::RefCell, - cmp::Ordering, - fmt::{self, Display}, - hash::{Hash, Hasher}, - marker::PhantomData, - num::NonZeroU64, - sync::{ - Arc, Weak, - atomic::{AtomicU64, AtomicUsize, Ordering::SeqCst}, - }, - thread::panicking, -}; - -use super::Context; -use crate::util::atomic_incr_if_not_zero; -#[cfg(any(test, feature = "leak-detection"))] -use collections::HashMap; - -slotmap::new_key_type! { - /// A unique identifier for a entity across the application. - pub struct EntityId; -} - -impl From for EntityId { - fn from(value: u64) -> Self { - Self(KeyData::from_ffi(value)) - } -} - -impl EntityId { - /// Converts this entity id to a [NonZeroU64] - pub fn as_non_zero_u64(self) -> NonZeroU64 { - NonZeroU64::new(self.0.as_ffi()).unwrap() - } - - /// Converts this entity id to a [u64] - pub fn as_u64(self) -> u64 { - self.0.as_ffi() - } -} - -impl Display for EntityId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_u64()) - } -} - -pub(crate) struct EntityMap { - entities: SecondaryMap>, - pub accessed_entities: RefCell>, - ref_counts: Arc>, -} - -#[doc(hidden)] -pub(crate) struct EntityRefCounts { - counts: SlotMap, - dropped_entity_ids: Vec, - #[cfg(any(test, feature = "leak-detection"))] - leak_detector: LeakDetector, -} - -impl EntityMap { - pub fn new() -> Self { - Self { - entities: SecondaryMap::new(), - accessed_entities: RefCell::new(FxHashSet::default()), - ref_counts: Arc::new(RwLock::new(EntityRefCounts { - counts: SlotMap::with_key(), - dropped_entity_ids: Vec::new(), - #[cfg(any(test, feature = "leak-detection"))] - leak_detector: LeakDetector { - next_handle_id: 0, - entity_handles: HashMap::default(), - }, - })), - } - } - - #[doc(hidden)] - pub fn ref_counts_drop_handle(&self) -> Arc> { - self.ref_counts.clone() - } - - /// Captures a snapshot of all entities that currently have alive handles. - /// - /// The returned [`LeakDetectorSnapshot`] can later be passed to - /// [`assert_no_new_leaks`](Self::assert_no_new_leaks) to verify that no - /// entities created after the snapshot are still alive. - #[cfg(any(test, feature = "leak-detection"))] - pub fn leak_detector_snapshot(&self) -> LeakDetectorSnapshot { - self.ref_counts.read().leak_detector.snapshot() - } - - /// Asserts that no entities created after `snapshot` still have alive handles. - /// - /// See [`LeakDetector::assert_no_new_leaks`] for details. - #[cfg(any(test, feature = "leak-detection"))] - pub fn assert_no_new_leaks(&self, snapshot: &LeakDetectorSnapshot) { - self.ref_counts - .read() - .leak_detector - .assert_no_new_leaks(snapshot) - } - - /// Reserve a slot for an entity, which you can subsequently use with `insert`. - pub fn reserve(&self) -> Slot { - let id = self.ref_counts.write().counts.insert(1.into()); - Slot(Entity::new(id, Arc::downgrade(&self.ref_counts))) - } - - /// Insert an entity into a slot obtained by calling `reserve`. - pub fn insert(&mut self, slot: Slot, entity: T) -> Entity - where - T: 'static, - { - let mut accessed_entities = self.accessed_entities.get_mut(); - accessed_entities.insert(slot.entity_id); - - let handle = slot.0; - self.entities.insert(handle.entity_id, Box::new(entity)); - handle - } - - /// Move an entity to the stack. - #[track_caller] - pub fn lease(&mut self, pointer: &Entity) -> Lease { - self.assert_valid_context(pointer); - let mut accessed_entities = self.accessed_entities.get_mut(); - accessed_entities.insert(pointer.entity_id); - - let entity = Some( - self.entities - .remove(pointer.entity_id) - .unwrap_or_else(|| double_lease_panic::("update")), - ); - Lease { - entity, - id: pointer.entity_id, - entity_type: PhantomData, - } - } - - /// Returns an entity after moving it to the stack. - pub fn end_lease(&mut self, mut lease: Lease) { - self.entities.insert(lease.id, lease.entity.take().unwrap()); - } - - pub fn read(&self, entity: &Entity) -> &T { - self.assert_valid_context(entity); - let mut accessed_entities = self.accessed_entities.borrow_mut(); - accessed_entities.insert(entity.entity_id); - - self.entities - .get(entity.entity_id) - .and_then(|entity| entity.downcast_ref()) - .unwrap_or_else(|| double_lease_panic::("read")) - } - - fn assert_valid_context(&self, entity: &AnyEntity) { - debug_assert!( - Weak::ptr_eq(&entity.entity_map, &Arc::downgrade(&self.ref_counts)), - "used a entity with the wrong context" - ); - } - - pub fn extend_accessed(&mut self, entities: &FxHashSet) { - self.accessed_entities - .get_mut() - .extend(entities.iter().copied()); - } - - pub fn clear_accessed(&mut self) { - self.accessed_entities.get_mut().clear(); - } - - pub fn take_dropped(&mut self) -> Vec<(EntityId, Box)> { - let mut ref_counts = &mut *self.ref_counts.write(); - let dropped_entity_ids = ref_counts.dropped_entity_ids.drain(..); - let mut accessed_entities = self.accessed_entities.get_mut(); - - dropped_entity_ids - .filter_map(|entity_id| { - let count = ref_counts.counts.remove(entity_id).unwrap(); - debug_assert_eq!( - count.load(SeqCst), - 0, - "dropped an entity that was referenced" - ); - accessed_entities.remove(&entity_id); - // If the EntityId was allocated with `Context::reserve`, - // the entity may not have been inserted. - Some((entity_id, self.entities.remove(entity_id)?)) - }) - .collect() - } -} - -#[track_caller] -fn double_lease_panic(operation: &str) -> ! { - panic!( - "cannot {operation} {} while it is already being updated", - std::any::type_name::() - ) -} - -pub(crate) struct Lease { - entity: Option>, - pub id: EntityId, - entity_type: PhantomData, -} - -impl core::ops::Deref for Lease { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.entity.as_ref().unwrap().downcast_ref().unwrap() - } -} - -impl core::ops::DerefMut for Lease { - fn deref_mut(&mut self) -> &mut Self::Target { - self.entity.as_mut().unwrap().downcast_mut().unwrap() - } -} - -impl Drop for Lease { - fn drop(&mut self) { - if self.entity.is_some() && !panicking() { - panic!("Leases must be ended with EntityMap::end_lease") - } - } -} - -#[derive(Deref, DerefMut)] -pub(crate) struct Slot(Entity); - -/// A dynamically typed reference to a entity, which can be downcast into a `Entity`. -pub struct AnyEntity { - pub(crate) entity_id: EntityId, - pub(crate) entity_type: TypeId, - entity_map: Weak>, - #[cfg(any(test, feature = "leak-detection"))] - handle_id: HandleId, -} - -impl AnyEntity { - fn new( - id: EntityId, - entity_type: TypeId, - entity_map: Weak>, - #[cfg(any(test, feature = "leak-detection"))] type_name: &'static str, - ) -> Self { - Self { - entity_id: id, - entity_type, - #[cfg(any(test, feature = "leak-detection"))] - handle_id: entity_map - .clone() - .upgrade() - .unwrap() - .write() - .leak_detector - .handle_created(id, Some(type_name)), - entity_map, - } - } - - /// Returns the id associated with this entity. - #[inline] - pub fn entity_id(&self) -> EntityId { - self.entity_id - } - - /// Returns the [TypeId] associated with this entity. - #[inline] - pub fn entity_type(&self) -> TypeId { - self.entity_type - } - - /// Converts this entity handle into a weak variant, which does not prevent it from being released. - pub fn downgrade(&self) -> AnyWeakEntity { - AnyWeakEntity { - entity_id: self.entity_id, - entity_type: self.entity_type, - entity_ref_counts: self.entity_map.clone(), - } - } - - /// Converts this entity handle into a strongly-typed entity handle of the given type. - /// If this entity handle is not of the specified type, returns itself as an error variant. - pub fn downcast(self) -> Result, AnyEntity> { - if TypeId::of::() == self.entity_type { - Ok(Entity { - any_entity: self, - entity_type: PhantomData, - }) - } else { - Err(self) - } - } -} - -impl Clone for AnyEntity { - fn clone(&self) -> Self { - if let Some(entity_map) = self.entity_map.upgrade() { - let entity_map = entity_map.read(); - let count = entity_map - .counts - .get(self.entity_id) - .expect("detected over-release of a entity"); - let prev_count = count.fetch_add(1, SeqCst); - assert_ne!(prev_count, 0, "Detected over-release of a entity."); - } - - Self { - entity_id: self.entity_id, - entity_type: self.entity_type, - entity_map: self.entity_map.clone(), - #[cfg(any(test, feature = "leak-detection"))] - handle_id: self - .entity_map - .upgrade() - .unwrap() - .write() - .leak_detector - .handle_created(self.entity_id, None), - } - } -} - -impl Drop for AnyEntity { - fn drop(&mut self) { - if let Some(entity_map) = self.entity_map.upgrade() { - let entity_map = entity_map.upgradable_read(); - let count = entity_map - .counts - .get(self.entity_id) - .expect("detected over-release of a handle."); - let prev_count = count.fetch_sub(1, SeqCst); - assert_ne!(prev_count, 0, "Detected over-release of a entity."); - if prev_count == 1 { - // We were the last reference to this entity, so we can remove it. - let mut entity_map = RwLockUpgradableReadGuard::upgrade(entity_map); - entity_map.dropped_entity_ids.push(self.entity_id); - } - } - - #[cfg(any(test, feature = "leak-detection"))] - if let Some(entity_map) = self.entity_map.upgrade() { - entity_map - .write() - .leak_detector - .handle_released(self.entity_id, self.handle_id) - } - } -} - -impl From> for AnyEntity { - #[inline] - fn from(entity: Entity) -> Self { - entity.any_entity - } -} - -impl Hash for AnyEntity { - #[inline] - fn hash(&self, state: &mut H) { - self.entity_id.hash(state); - } -} - -impl PartialEq for AnyEntity { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.entity_id == other.entity_id - } -} - -impl Eq for AnyEntity {} - -impl Ord for AnyEntity { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.entity_id.cmp(&other.entity_id) - } -} - -impl PartialOrd for AnyEntity { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl std::fmt::Debug for AnyEntity { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AnyEntity") - .field("entity_id", &self.entity_id.as_u64()) - .finish() - } -} - -/// A strong, well-typed reference to a struct which is managed -/// by GPUI -#[derive(Deref, DerefMut)] -pub struct Entity { - #[deref] - #[deref_mut] - pub(crate) any_entity: AnyEntity, - pub(crate) entity_type: PhantomData T>, -} - -impl Sealed for Entity {} - -impl Entity { - #[inline] - fn new(id: EntityId, entity_map: Weak>) -> Self - where - T: 'static, - { - Self { - any_entity: AnyEntity::new( - id, - TypeId::of::(), - entity_map, - #[cfg(any(test, feature = "leak-detection"))] - std::any::type_name::(), - ), - entity_type: PhantomData, - } - } - - /// Get the entity ID associated with this entity - #[inline] - pub fn entity_id(&self) -> EntityId { - self.any_entity.entity_id - } - - /// Downgrade this entity pointer to a non-retaining weak pointer - #[inline] - pub fn downgrade(&self) -> WeakEntity { - WeakEntity { - any_entity: self.any_entity.downgrade(), - entity_type: self.entity_type, - } - } - - /// Convert this into a dynamically typed entity. - #[inline] - pub fn into_any(self) -> AnyEntity { - self.any_entity - } - - /// Grab a reference to this entity from the context. - #[inline] - pub fn read<'a>(&self, cx: &'a App) -> &'a T { - cx.entities.read(self) - } - - /// Read the entity referenced by this handle with the given function. - #[inline] - pub fn read_with(&self, cx: &C, f: impl FnOnce(&T, &App) -> R) -> R { - cx.read_entity(self, f) - } - - /// Updates the entity referenced by this handle with the given function. - #[inline] - pub fn update( - &self, - cx: &mut C, - update: impl FnOnce(&mut T, &mut Context) -> R, - ) -> R { - cx.update_entity(self, update) - } - - /// Updates the entity referenced by this handle with the given function. - #[inline] - pub fn as_mut<'a, C: AppContext>(&self, cx: &'a mut C) -> GpuiBorrow<'a, T> { - cx.as_mut(self) - } - - /// Updates the entity referenced by this handle with the given function. - pub fn write(&self, cx: &mut C, value: T) { - self.update(cx, |entity, cx| { - *entity = value; - cx.notify(); - }) - } - - /// Updates the entity referenced by this handle with the given function if - /// the referenced entity still exists, within a visual context that has a window. - /// Returns an error if the window has been closed. - #[inline] - pub fn update_in( - &self, - cx: &mut C, - update: impl FnOnce(&mut T, &mut Window, &mut Context) -> R, - ) -> C::Result { - cx.update_window_entity(self, update) - } -} - -impl Clone for Entity { - #[inline] - fn clone(&self) -> Self { - Self { - any_entity: self.any_entity.clone(), - entity_type: self.entity_type, - } - } -} - -impl std::fmt::Debug for Entity { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Entity") - .field("entity_id", &self.any_entity.entity_id) - .field("entity_type", &type_name::()) - .finish() - } -} - -impl Hash for Entity { - #[inline] - fn hash(&self, state: &mut H) { - self.any_entity.hash(state); - } -} - -impl PartialEq for Entity { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.any_entity == other.any_entity - } -} - -impl Eq for Entity {} - -impl PartialEq> for Entity { - #[inline] - fn eq(&self, other: &WeakEntity) -> bool { - self.any_entity.entity_id() == other.entity_id() - } -} - -impl Ord for Entity { - #[inline] - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.entity_id().cmp(&other.entity_id()) - } -} - -impl PartialOrd for Entity { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -/// A type erased, weak reference to a entity. -#[derive(Clone)] -pub struct AnyWeakEntity { - pub(crate) entity_id: EntityId, - entity_type: TypeId, - entity_ref_counts: Weak>, -} - -impl AnyWeakEntity { - /// Get the entity ID associated with this weak reference. - #[inline] - pub fn entity_id(&self) -> EntityId { - self.entity_id - } - - /// Check if this weak handle can be upgraded, or if the entity has already been dropped - pub fn is_upgradable(&self) -> bool { - let ref_count = self - .entity_ref_counts - .upgrade() - .and_then(|ref_counts| Some(ref_counts.read().counts.get(self.entity_id)?.load(SeqCst))) - .unwrap_or(0); - ref_count > 0 - } - - /// Upgrade this weak entity reference to a strong reference. - pub fn upgrade(&self) -> Option { - let ref_counts = &self.entity_ref_counts.upgrade()?; - let ref_counts = ref_counts.read(); - let ref_count = ref_counts.counts.get(self.entity_id)?; - - if atomic_incr_if_not_zero(ref_count) == 0 { - // entity_id is in dropped_entity_ids - return None; - } - drop(ref_counts); - - Some(AnyEntity { - entity_id: self.entity_id, - entity_type: self.entity_type, - entity_map: self.entity_ref_counts.clone(), - #[cfg(any(test, feature = "leak-detection"))] - handle_id: self - .entity_ref_counts - .upgrade() - .unwrap() - .write() - .leak_detector - .handle_created(self.entity_id, None), - }) - } - - /// Asserts that the entity referenced by this weak handle has been fully released. - /// - /// # Example - /// - /// ```ignore - /// let entity = cx.new(|_| MyEntity::new()); - /// let weak = entity.downgrade(); - /// drop(entity); - /// - /// // Verify the entity was released - /// weak.assert_released(); - /// ``` - /// - /// # Debugging Leaks - /// - /// If this method panics due to leaked handles, set the `LEAK_BACKTRACE` environment - /// variable to see where the leaked handles were allocated: - /// - /// ```bash - /// LEAK_BACKTRACE=1 cargo test my_test - /// ``` - /// - /// # Panics - /// - /// - Panics if any strong handles to the entity are still alive. - /// - Panics if the entity was recently dropped but cleanup hasn't completed yet - /// (resources are retained until the end of the effect cycle). - #[cfg(any(test, feature = "leak-detection"))] - pub fn assert_released(&self) { - self.entity_ref_counts - .upgrade() - .unwrap() - .write() - .leak_detector - .assert_released(self.entity_id); - - if self - .entity_ref_counts - .upgrade() - .and_then(|ref_counts| Some(ref_counts.read().counts.get(self.entity_id)?.load(SeqCst))) - .is_some() - { - panic!( - "entity was recently dropped but resources are retained until the end of the effect cycle." - ) - } - } - - /// Creates a weak entity that can never be upgraded. - pub fn new_invalid() -> Self { - /// To hold the invariant that all ids are unique, and considering that slotmap - /// increases their IDs from `0`, we can decrease ours from `u64::MAX` so these - /// two will never conflict (u64 is way too large). - static UNIQUE_NON_CONFLICTING_ID_GENERATOR: AtomicU64 = AtomicU64::new(u64::MAX); - let entity_id = UNIQUE_NON_CONFLICTING_ID_GENERATOR.fetch_sub(1, SeqCst); - - Self { - // Safety: - // Docs say this is safe but can be unspecified if slotmap changes the representation - // after `1.0.7`, that said, providing a valid entity_id here is not necessary as long - // as we guarantee that `entity_id` is never used if `entity_ref_counts` equals - // to `Weak::new()` (that is, it's unable to upgrade), that is the invariant that - // actually needs to be hold true. - // - // And there is no sane reason to read an entity slot if `entity_ref_counts` can't be - // read in the first place, so we're good! - entity_id: entity_id.into(), - entity_type: TypeId::of::<()>(), - entity_ref_counts: Weak::new(), - } - } -} - -impl std::fmt::Debug for AnyWeakEntity { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct(type_name::()) - .field("entity_id", &self.entity_id) - .field("entity_type", &self.entity_type) - .finish() - } -} - -impl From> for AnyWeakEntity { - #[inline] - fn from(entity: WeakEntity) -> Self { - entity.any_entity - } -} - -impl Hash for AnyWeakEntity { - #[inline] - fn hash(&self, state: &mut H) { - self.entity_id.hash(state); - } -} - -impl PartialEq for AnyWeakEntity { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.entity_id == other.entity_id - } -} - -impl Eq for AnyWeakEntity {} - -impl Ord for AnyWeakEntity { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.entity_id.cmp(&other.entity_id) - } -} - -impl PartialOrd for AnyWeakEntity { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -/// A weak reference to a entity of the given type. -#[derive(Deref, DerefMut)] -pub struct WeakEntity { - #[deref] - #[deref_mut] - any_entity: AnyWeakEntity, - entity_type: PhantomData T>, -} - -impl std::fmt::Debug for WeakEntity { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct(type_name::()) - .field("entity_id", &self.any_entity.entity_id) - .field("entity_type", &type_name::()) - .finish() - } -} - -impl Clone for WeakEntity { - fn clone(&self) -> Self { - Self { - any_entity: self.any_entity.clone(), - entity_type: self.entity_type, - } - } -} - -impl WeakEntity { - /// Upgrade this weak entity reference into a strong entity reference - pub fn upgrade(&self) -> Option> { - Some(Entity { - any_entity: self.any_entity.upgrade()?, - entity_type: self.entity_type, - }) - } - - /// Updates the entity referenced by this handle with the given function if - /// the referenced entity still exists. Returns an error if the entity has - /// been released. - pub fn update( - &self, - cx: &mut C, - update: impl FnOnce(&mut T, &mut Context) -> R, - ) -> Result - where - C: AppContext, - { - let entity = self.upgrade().context("entity released")?; - Ok(cx.update_entity(&entity, update)) - } - - /// Updates the entity referenced by this handle with the given function if - /// the referenced entity still exists, within a visual context that has a window. - /// Returns an error if the entity has been released. - pub fn update_in( - &self, - cx: &mut C, - update: impl FnOnce(&mut T, &mut Window, &mut Context) -> R, - ) -> Result - where - C: AppContext, - { - let entity = self.upgrade().context("entity released")?; - cx.with_window(entity.entity_id(), |window, app| { - entity.update(app, |entity, cx| update(entity, window, cx)) - }) - .context("entity has no current window") - } - - /// Reads the entity referenced by this handle with the given function if - /// the referenced entity still exists. Returns an error if the entity has - /// been released. - pub fn read_with(&self, cx: &C, read: impl FnOnce(&T, &App) -> R) -> Result - where - C: AppContext, - { - let entity = self.upgrade().context("entity released")?; - Ok(cx.read_entity(&entity, read)) - } - - /// Create a new weak entity that can never be upgraded. - #[inline] - pub fn new_invalid() -> Self { - Self { - any_entity: AnyWeakEntity::new_invalid(), - entity_type: PhantomData, - } - } -} - -impl Hash for WeakEntity { - #[inline] - fn hash(&self, state: &mut H) { - self.any_entity.hash(state); - } -} - -impl PartialEq for WeakEntity { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.any_entity == other.any_entity - } -} - -impl Eq for WeakEntity {} - -impl PartialEq> for WeakEntity { - #[inline] - fn eq(&self, other: &Entity) -> bool { - self.entity_id() == other.any_entity.entity_id() - } -} - -impl Ord for WeakEntity { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.entity_id().cmp(&other.entity_id()) - } -} - -impl PartialOrd for WeakEntity { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -/// Controls whether backtraces are captured when entity handles are created. -/// -/// Set the `LEAK_BACKTRACE` environment variable to any non-empty value to enable -/// backtrace capture. This helps identify where leaked handles were allocated. -#[cfg(any(test, feature = "leak-detection"))] -static LEAK_BACKTRACE: std::sync::LazyLock = - std::sync::LazyLock::new(|| std::env::var("LEAK_BACKTRACE").is_ok_and(|b| !b.is_empty())); - -/// Unique identifier for a specific entity handle instance. -/// -/// This is distinct from `EntityId` - while multiple handles can point to the same -/// entity (same `EntityId`), each handle has its own unique `HandleId`. -#[cfg(any(test, feature = "leak-detection"))] -#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] -pub(crate) struct HandleId { - id: u64, -} - -/// Tracks entity handle allocations to detect leaks. -/// -/// The leak detector is enabled in tests and when the `leak-detection` feature is active. -/// It tracks every `Entity` and `AnyEntity` handle that is created and released, -/// allowing you to verify that all handles to an entity have been properly dropped. -/// -/// # How do leaks happen? -/// -/// Entities are reference-counted structures that can own other entities -/// allowing to form cycles. If such a strong-reference counted cycle is -/// created, all participating strong entities in this cycle will effectively -/// leak as they cannot be released anymore. -/// -/// Cycles can also happen if an entity owns a task or subscription that it -/// itself owns a strong reference to the entity again. -/// -/// # Usage -/// -/// You can use `WeakEntity::assert_released` or `AnyWeakEntity::assert_released` -/// to verify that an entity has been fully released: -/// -/// ```ignore -/// let entity = cx.new(|_| MyEntity::new()); -/// let weak = entity.downgrade(); -/// drop(entity); -/// -/// // This will panic if any handles to the entity are still alive -/// weak.assert_released(); -/// ``` -/// -/// # Debugging Leaks -/// -/// When a leak is detected, the detector will panic with information about the leaked -/// handles. To see where the leaked handles were allocated, set the `LEAK_BACKTRACE` -/// environment variable: -/// -/// ```bash -/// LEAK_BACKTRACE=1 cargo test my_test -/// ``` -/// -/// This will capture and display backtraces for each leaked handle, helping you -/// identify where leaked handles were created. -/// -/// # How It Works -/// -/// - When an entity handle is created (via `Entity::new`, `Entity::clone`, or -/// `WeakEntity::upgrade`), `handle_created` is called to register the handle. -/// - When a handle is dropped, `handle_released` removes it from tracking. -/// - `assert_released` verifies that no handles remain for a given entity. -#[cfg(any(test, feature = "leak-detection"))] -pub(crate) struct LeakDetector { - next_handle_id: u64, - entity_handles: HashMap, -} - -/// A snapshot of the set of alive entities at a point in time. -/// -/// Created by [`LeakDetector::snapshot`]. Can later be passed to -/// [`LeakDetector::assert_no_new_leaks`] to verify that no new entity -/// handles remain between the snapshot and the current state. -#[cfg(any(test, feature = "leak-detection"))] -pub struct LeakDetectorSnapshot { - entity_ids: collections::HashSet, -} - -#[cfg(any(test, feature = "leak-detection"))] -struct EntityLeakData { - handles: HashMap>, - type_name: &'static str, -} - -#[cfg(any(test, feature = "leak-detection"))] -impl LeakDetector { - /// Records that a new handle has been created for the given entity. - /// - /// Returns a unique `HandleId` that must be passed to `handle_released` when - /// the handle is dropped. If `LEAK_BACKTRACE` is set, captures a backtrace - /// at the allocation site. - #[track_caller] - pub fn handle_created( - &mut self, - entity_id: EntityId, - type_name: Option<&'static str>, - ) -> HandleId { - let id = gpui_util::post_inc(&mut self.next_handle_id); - let handle_id = HandleId { id }; - let handles = self - .entity_handles - .entry(entity_id) - .or_insert_with(|| EntityLeakData { - handles: HashMap::default(), - type_name: type_name.unwrap_or(""), - }); - handles.handles.insert( - handle_id, - LEAK_BACKTRACE.then(backtrace::Backtrace::new_unresolved), - ); - handle_id - } - - /// Records that a handle has been released (dropped). - /// - /// This removes the handle from tracking. The `handle_id` should be the same - /// one returned by `handle_created` when the handle was allocated. - pub fn handle_released(&mut self, entity_id: EntityId, handle_id: HandleId) { - if let std::collections::hash_map::Entry::Occupied(mut data) = - self.entity_handles.entry(entity_id) - { - data.get_mut().handles.remove(&handle_id); - if data.get().handles.is_empty() { - data.remove(); - } - } - } - - /// Asserts that all handles to the given entity have been released. - /// - /// # Panics - /// - /// Panics if any handles to the entity are still alive. The panic message - /// includes backtraces for each leaked handle if `LEAK_BACKTRACE` is set, - /// otherwise it suggests setting the environment variable to get more info. - pub fn assert_released(&mut self, entity_id: EntityId) { - use std::fmt::Write as _; - - if let Some(data) = self.entity_handles.remove(&entity_id) { - let mut out = String::new(); - for (_, backtrace) in data.handles { - if let Some(mut backtrace) = backtrace { - backtrace.resolve(); - let backtrace = BacktraceFormatter(backtrace); - writeln!(out, "Leaked handle:\n{:?}", backtrace).unwrap(); - } else { - writeln!( - out, - "Leaked handle: (export LEAK_BACKTRACE to find allocation site)" - ) - .unwrap(); - } - } - panic!("Handles for {} leaked:\n{out}", data.type_name); - } - } - - /// Captures a snapshot of all entity IDs that currently have alive handles. - /// - /// The returned [`LeakDetectorSnapshot`] can later be passed to - /// [`assert_no_new_leaks`](Self::assert_no_new_leaks) to verify that no - /// entities created after the snapshot are still alive. - pub fn snapshot(&self) -> LeakDetectorSnapshot { - LeakDetectorSnapshot { - entity_ids: self.entity_handles.keys().copied().collect(), - } - } - - /// Asserts that no entities created after `snapshot` still have alive handles. - /// - /// Entities that were already tracked at the time of the snapshot are ignored, - /// even if they still have handles. Only *new* entities (those whose - /// `EntityId` was not present in the snapshot) are considered leaks. - /// - /// # Panics - /// - /// Panics if any new entity handles exist. The panic message lists every - /// leaked entity with its type name, and includes allocation-site backtraces - /// when `LEAK_BACKTRACE` is set. - pub fn assert_no_new_leaks(&self, snapshot: &LeakDetectorSnapshot) { - use std::fmt::Write as _; - - let mut out = String::new(); - for (entity_id, data) in &self.entity_handles { - if snapshot.entity_ids.contains(entity_id) { - continue; - } - for (_, backtrace) in &data.handles { - if let Some(backtrace) = backtrace { - let mut backtrace = backtrace.clone(); - backtrace.resolve(); - let backtrace = BacktraceFormatter(backtrace); - writeln!( - out, - "Leaked handle for entity {} ({entity_id:?}):\n{:?}", - data.type_name, backtrace - ) - .unwrap(); - } else { - writeln!( - out, - "Leaked handle for entity {} ({entity_id:?}): (export LEAK_BACKTRACE to find allocation site)", - data.type_name - ) - .unwrap(); - } - } - } - - if !out.is_empty() { - panic!("New entity leaks detected since snapshot:\n{out}"); - } - } -} - -#[cfg(any(test, feature = "leak-detection"))] -impl Drop for LeakDetector { - fn drop(&mut self) { - use std::fmt::Write; - - if self.entity_handles.is_empty() || std::thread::panicking() { - return; - } - - let mut out = String::new(); - for (entity_id, data) in self.entity_handles.drain() { - for (_handle, backtrace) in data.handles { - if let Some(mut backtrace) = backtrace { - backtrace.resolve(); - let backtrace = BacktraceFormatter(backtrace); - writeln!( - out, - "Leaked handle for entity {} ({entity_id:?}):\n{:?}", - data.type_name, backtrace - ) - .unwrap(); - } else { - writeln!( - out, - "Leaked handle for entity {} ({entity_id:?}): (export LEAK_BACKTRACE to find allocation site)", - data.type_name - ) - .unwrap(); - } - } - } - panic!("Exited with leaked handles:\n{out}"); - } -} - -#[cfg(any(test, feature = "leak-detection"))] -struct BacktraceFormatter(backtrace::Backtrace); - -#[cfg(any(test, feature = "leak-detection"))] -impl fmt::Debug for BacktraceFormatter { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - use backtrace::{BacktraceFmt, BytesOrWideString, PrintFmt}; - - let style = if fmt.alternate() { - PrintFmt::Full - } else { - PrintFmt::Short - }; - - // When printing paths we try to strip the cwd if it exists, otherwise - // we just print the path as-is. Note that we also only do this for the - // short format, because if it's full we presumably want to print - // everything. - let cwd = std::env::current_dir(); - let mut print_path = move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| { - let path = path.into_path_buf(); - if style != PrintFmt::Full { - if let Ok(cwd) = &cwd { - if let Ok(suffix) = path.strip_prefix(cwd) { - return fmt::Display::fmt(&suffix.display(), fmt); - } - } - } - fmt::Display::fmt(&path.display(), fmt) - }; - - let mut f = BacktraceFmt::new(fmt, style, &mut print_path); - f.add_context()?; - let mut strip = true; - for frame in self.0.frames() { - if let [symbol, ..] = frame.symbols() - && let Some(name) = symbol.name() - && let Some(filename) = name.as_str() - { - match filename { - "test::run_test_in_process" - | "scheduler::executor::spawn_local_with_source_location::impl$1::poll > > > >,alloc::alloc::Global> > >" => { - strip = true - } - "gpui::app::entity_map::LeakDetector::handle_created" => { - strip = false; - continue; - } - "zed::main" => { - strip = true; - f.frame().backtrace_frame(frame)?; - } - _ => {} - } - } - if strip { - continue; - } - f.frame().backtrace_frame(frame)?; - } - f.finish()?; - Ok(()) - } -} - -#[cfg(test)] -mod test { - use crate::EntityMap; - - struct TestEntity { - pub i: i32, - } - - #[test] - fn test_entity_map_slot_assignment_before_cleanup() { - // Tests that slots are not re-used before take_dropped. - let mut entity_map = EntityMap::new(); - - let slot = entity_map.reserve::(); - entity_map.insert(slot, TestEntity { i: 1 }); - - let slot = entity_map.reserve::(); - entity_map.insert(slot, TestEntity { i: 2 }); - - let dropped = entity_map.take_dropped(); - assert_eq!(dropped.len(), 2); - - assert_eq!( - dropped - .into_iter() - .map(|(_, entity)| entity.downcast::().unwrap().i) - .collect::>(), - vec![1, 2], - ); - } - - #[test] - fn test_entity_map_weak_upgrade_before_cleanup() { - // Tests that weak handles are not upgraded before take_dropped - let mut entity_map = EntityMap::new(); - - let slot = entity_map.reserve::(); - let handle = entity_map.insert(slot, TestEntity { i: 1 }); - let weak = handle.downgrade(); - drop(handle); - - let strong = weak.upgrade(); - assert_eq!(strong, None); - - let dropped = entity_map.take_dropped(); - assert_eq!(dropped.len(), 1); - - assert_eq!( - dropped - .into_iter() - .map(|(_, entity)| entity.downcast::().unwrap().i) - .collect::>(), - vec![1], - ); - } - - #[test] - fn test_leak_detector_snapshot_no_leaks() { - let mut entity_map = EntityMap::new(); - - let slot = entity_map.reserve::(); - let pre_existing = entity_map.insert(slot, TestEntity { i: 1 }); - - let snapshot = entity_map.leak_detector_snapshot(); - - let slot = entity_map.reserve::(); - let temporary = entity_map.insert(slot, TestEntity { i: 2 }); - drop(temporary); - - entity_map.assert_no_new_leaks(&snapshot); - - drop(pre_existing); - } - - #[test] - #[should_panic(expected = "New entity leaks detected since snapshot")] - fn test_leak_detector_snapshot_detects_new_leak() { - let mut entity_map = EntityMap::new(); - - let slot = entity_map.reserve::(); - let pre_existing = entity_map.insert(slot, TestEntity { i: 1 }); - - let snapshot = entity_map.leak_detector_snapshot(); - - let slot = entity_map.reserve::(); - let leaked = entity_map.insert(slot, TestEntity { i: 2 }); - - // `leaked` is still alive, so this should panic. - entity_map.assert_no_new_leaks(&snapshot); - - drop(pre_existing); - drop(leaked); - } -} diff --git a/crates/gpui_pre/src/app/headless_app_context.rs b/crates/gpui_pre/src/app/headless_app_context.rs deleted file mode 100644 index b21e64fa..00000000 --- a/crates/gpui_pre/src/app/headless_app_context.rs +++ /dev/null @@ -1,284 +0,0 @@ -//! Cross-platform headless app context for tests that need real text shaping. -//! -//! This replaces the macOS-only `HeadlessMetalAppContext` with a platform-neutral -//! implementation backed by `TestPlatform`. Tests supply a real `PlatformTextSystem` -//! (e.g. `DirectWriteTextSystem` on Windows, `MacTextSystem` on macOS) to get -//! accurate glyph measurements while keeping everything else deterministic. -//! -//! Optionally, a renderer factory can be provided to enable real GPU rendering -//! and screenshot capture via [`HeadlessAppContext::capture_screenshot`]. - -use crate::{ - AnyView, AnyWindowHandle, App, AppCell, AppContext, AssetSource, BackgroundExecutor, Bounds, - Context, Entity, EntityId, ForegroundExecutor, Global, Pixels, PlatformHeadlessRenderer, - PlatformTextSystem, Render, Reservation, Size, Task, TestDispatcher, TestPlatform, TextSystem, - Window, WindowBounds, WindowHandle, WindowOptions, - app::{GpuiBorrow, GpuiMode}, -}; -use anyhow::Result; -use image::RgbaImage; -use std::{future::Future, rc::Rc, sync::Arc, time::Duration}; - -/// A cross-platform headless app context for tests that need real text shaping. -/// -/// Unlike the old `HeadlessMetalAppContext`, this works on any platform. It uses -/// `TestPlatform` for deterministic scheduling and accepts a pluggable -/// `PlatformTextSystem` so tests get real glyph measurements. -/// -/// # Usage -/// -/// ```ignore -/// let text_system = Arc::new(gpui_wgpu::CosmicTextSystem::new("fallback")); -/// let mut cx = HeadlessAppContext::with_platform( -/// text_system, -/// Arc::new(Assets), -/// || gpui_platform::current_headless_renderer(), -/// ); -/// ``` -pub struct HeadlessAppContext { - /// The underlying app cell. - pub app: Rc, - /// The background executor for running async tasks. - pub background_executor: BackgroundExecutor, - /// The foreground executor for running tasks on the main thread. - pub foreground_executor: ForegroundExecutor, - dispatcher: TestDispatcher, - text_system: Arc, -} - -impl HeadlessAppContext { - /// Creates a new headless app context with the given text system. - pub fn new(platform_text_system: Arc) -> Self { - Self::with_platform(platform_text_system, Arc::new(()), || None) - } - - /// Creates a new headless app context with a custom text system and asset source. - pub fn with_asset_source( - platform_text_system: Arc, - asset_source: Arc, - ) -> Self { - Self::with_platform(platform_text_system, asset_source, || None) - } - - /// Creates a new headless app context with the given text system, asset source, - /// and an optional renderer factory for screenshot support. - pub fn with_platform( - platform_text_system: Arc, - asset_source: Arc, - renderer_factory: impl Fn() -> Option> + 'static, - ) -> Self { - let seed = std::env::var("SEED") - .ok() - .and_then(|s| s.parse().ok()) - .unwrap_or(0); - - let dispatcher = TestDispatcher::new(seed); - let arc_dispatcher = Arc::new(dispatcher.clone()); - let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(arc_dispatcher); - - let renderer_factory: Box Option>> = - Box::new(renderer_factory); - let platform = TestPlatform::with_platform( - background_executor.clone(), - foreground_executor.clone(), - platform_text_system.clone(), - Some(renderer_factory), - ); - - let text_system = Arc::new(TextSystem::new(platform_text_system)); - let http_client = http_client::FakeHttpClient::with_404_response(); - let app = App::new_app(platform, asset_source, http_client); - app.borrow_mut().mode = GpuiMode::test(); - - Self { - app, - background_executor, - foreground_executor, - dispatcher, - text_system, - } - } - - /// Opens a window for headless rendering. - pub fn open_window( - &mut self, - size: Size, - build_root: impl FnOnce(&mut Window, &mut App) -> Entity, - ) -> Result> { - use crate::{point, px}; - - let bounds = Bounds { - origin: point(px(0.0), px(0.0)), - size, - }; - - let mut cx = self.app.borrow_mut(); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - focus: false, - show: false, - ..Default::default() - }, - build_root, - ) - } - - /// Runs all pending tasks until parked. - pub fn run_until_parked(&self) { - self.dispatcher.run_until_parked(); - } - - /// Advances the simulated clock. - pub fn advance_clock(&self, duration: Duration) { - self.dispatcher.advance_clock(duration); - } - - /// Enables parking mode, allowing blocking on real I/O (e.g., async asset loading). - pub fn allow_parking(&self) { - self.dispatcher.allow_parking(); - } - - /// Disables parking mode, returning to deterministic test execution. - pub fn forbid_parking(&self) { - self.dispatcher.forbid_parking(); - } - - /// Updates app state. - pub fn update(&mut self, f: impl FnOnce(&mut App) -> R) -> R { - let mut app = self.app.borrow_mut(); - f(&mut app) - } - - /// Updates a window and calls draw to render. - pub fn update_window( - &mut self, - window: AnyWindowHandle, - f: impl FnOnce(AnyView, &mut Window, &mut App) -> R, - ) -> Result { - let mut app = self.app.borrow_mut(); - app.update_window(window, f) - } - - /// Captures a screenshot from a window. - /// - /// Requires that the context was created with a renderer factory that - /// returns `Some` via [`HeadlessAppContext::with_platform`]. - pub fn capture_screenshot(&mut self, window: AnyWindowHandle) -> Result { - let mut app = self.app.borrow_mut(); - app.update_window(window, |_, window, _| window.render_to_image())? - } - - /// Returns the text system. - pub fn text_system(&self) -> &Arc { - &self.text_system - } - - /// Returns the background executor. - pub fn background_executor(&self) -> &BackgroundExecutor { - &self.background_executor - } - - /// Returns the foreground executor. - pub fn foreground_executor(&self) -> &ForegroundExecutor { - &self.foreground_executor - } -} - -impl Drop for HeadlessAppContext { - fn drop(&mut self) { - // Shut down the app so windows are closed and entity handles are - // released before the LeakDetector runs. - self.app.borrow_mut().shutdown(); - } -} - -impl AppContext for HeadlessAppContext { - fn new(&mut self, build_entity: impl FnOnce(&mut Context) -> T) -> Entity { - let mut app = self.app.borrow_mut(); - app.new(build_entity) - } - - fn reserve_entity(&mut self) -> Reservation { - let mut app = self.app.borrow_mut(); - app.reserve_entity() - } - - fn insert_entity( - &mut self, - reservation: Reservation, - build_entity: impl FnOnce(&mut Context) -> T, - ) -> Entity { - let mut app = self.app.borrow_mut(); - app.insert_entity(reservation, build_entity) - } - - fn update_entity( - &mut self, - handle: &Entity, - update: impl FnOnce(&mut T, &mut Context) -> R, - ) -> R { - let mut app = self.app.borrow_mut(); - app.update_entity(handle, update) - } - - fn as_mut<'a, T>(&'a mut self, _: &Entity) -> GpuiBorrow<'a, T> - where - T: 'static, - { - panic!("Cannot use as_mut with HeadlessAppContext. Call update() instead.") - } - - fn read_entity(&self, handle: &Entity, read: impl FnOnce(&T, &App) -> R) -> R - where - T: 'static, - { - let app = self.app.borrow(); - app.read_entity(handle, read) - } - - fn update_window(&mut self, window: AnyWindowHandle, f: F) -> Result - where - F: FnOnce(AnyView, &mut Window, &mut App) -> T, - { - let mut lock = self.app.borrow_mut(); - lock.update_window(window, f) - } - - fn with_window( - &mut self, - entity_id: EntityId, - f: impl FnOnce(&mut Window, &mut App) -> R, - ) -> Option { - let mut lock = self.app.borrow_mut(); - lock.with_window(entity_id, f) - } - - fn read_window( - &self, - window: &WindowHandle, - read: impl FnOnce(Entity, &App) -> R, - ) -> Result - where - T: 'static, - { - let app = self.app.borrow(); - app.read_window(window, read) - } - - fn background_spawn(&self, future: impl Future + Send + 'static) -> Task - where - R: Send + 'static, - { - self.background_executor.spawn(future) - } - - fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> R - where - G: Global, - { - let app = self.app.borrow(); - app.read_global(callback) - } -} diff --git a/crates/gpui_pre/src/app/test_app.rs b/crates/gpui_pre/src/app/test_app.rs deleted file mode 100644 index d2418638..00000000 --- a/crates/gpui_pre/src/app/test_app.rs +++ /dev/null @@ -1,607 +0,0 @@ -//! A clean testing API for GPUI applications. -//! -//! `TestApp` provides a simpler alternative to `TestAppContext` with: -//! - Automatic effect flushing after updates -//! - Clean window creation and inspection -//! - Input simulation helpers -//! -//! # Example -//! ```ignore -//! #[test] -//! fn test_my_view() { -//! let mut app = TestApp::new(); -//! -//! let mut window = app.open_window(|window, cx| { -//! MyView::new(window, cx) -//! }); -//! -//! window.update(|view, window, cx| { -//! view.do_something(cx); -//! }); -//! -//! // Check rendered state -//! assert_eq!(window.title(), Some("Expected Title")); -//! } -//! ``` - -use crate::{ - AnyWindowHandle, App, AppCell, AppContext, AsyncApp, BackgroundExecutor, BorrowAppContext, - Bounds, ClipboardItem, Context, Entity, ForegroundExecutor, Global, InputEvent, Keystroke, - MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Platform, - PlatformTextSystem, Point, Render, Size, Task, TestDispatcher, TestPlatform, TextSystem, - Window, WindowBounds, WindowHandle, WindowOptions, app::GpuiMode, -}; -use std::{future::Future, rc::Rc, sync::Arc, time::Duration}; - -/// A test application context with a clean API. -/// -/// Unlike `TestAppContext`, `TestApp` automatically flushes effects after -/// each update and provides simpler window management. -pub struct TestApp { - app: Rc, - platform: Rc, - background_executor: BackgroundExecutor, - foreground_executor: ForegroundExecutor, - #[allow(dead_code)] - dispatcher: TestDispatcher, - text_system: Arc, -} - -impl TestApp { - /// Create a new test application. - pub fn new() -> Self { - Self::with_seed(0) - } - - /// Create a new test application with a specific random seed. - pub fn with_seed(seed: u64) -> Self { - Self::build(seed, None, Arc::new(())) - } - - /// Create a new test application with a custom text system for real font shaping. - pub fn with_text_system(text_system: Arc) -> Self { - Self::build(0, Some(text_system), Arc::new(())) - } - - /// Create a new test application with a custom text system and asset source. - pub fn with_text_system_and_assets( - text_system: Arc, - asset_source: Arc, - ) -> Self { - Self::build(0, Some(text_system), asset_source) - } - - fn build( - seed: u64, - platform_text_system: Option>, - asset_source: Arc, - ) -> Self { - let dispatcher = TestDispatcher::new(seed); - let arc_dispatcher = Arc::new(dispatcher.clone()); - let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(arc_dispatcher); - let platform = match platform_text_system.clone() { - Some(ts) => TestPlatform::with_text_system( - background_executor.clone(), - foreground_executor.clone(), - ts, - ), - None => TestPlatform::new(background_executor.clone(), foreground_executor.clone()), - }; - let http_client = http_client::FakeHttpClient::with_404_response(); - let text_system = Arc::new(TextSystem::new( - platform_text_system.unwrap_or_else(|| platform.text_system.clone()), - )); - - let app = App::new_app(platform.clone(), asset_source, http_client); - app.borrow_mut().mode = GpuiMode::test(); - - Self { - app, - platform, - background_executor, - foreground_executor, - dispatcher, - text_system, - } - } - - /// Run a closure with mutable access to the App context. - /// Automatically runs until parked after the closure completes. - pub fn update(&mut self, f: impl FnOnce(&mut App) -> R) -> R { - let result = { - let mut app = self.app.borrow_mut(); - app.update(f) - }; - self.run_until_parked(); - result - } - - /// Run a closure with read-only access to the App context. - pub fn read(&self, f: impl FnOnce(&App) -> R) -> R { - let app = self.app.borrow(); - f(&app) - } - - /// Create a new entity in the app. - pub fn new_entity( - &mut self, - build: impl FnOnce(&mut Context) -> T, - ) -> Entity { - self.update(|cx| cx.new(build)) - } - - /// Update an entity. - pub fn update_entity( - &mut self, - entity: &Entity, - f: impl FnOnce(&mut T, &mut Context) -> R, - ) -> R { - self.update(|cx| entity.update(cx, f)) - } - - /// Read an entity. - pub fn read_entity( - &self, - entity: &Entity, - f: impl FnOnce(&T, &App) -> R, - ) -> R { - self.read(|cx| f(entity.read(cx), cx)) - } - - /// Open a test window with the given root view, using maximized bounds. - pub fn open_window( - &mut self, - build_view: impl FnOnce(&mut Window, &mut Context) -> V, - ) -> TestAppWindow { - let bounds = self.read(|cx| Bounds::maximized(None, cx)); - let handle = self.update(|cx| { - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |window, cx| cx.new(|cx| build_view(window, cx)), - ) - .unwrap() - }); - - TestAppWindow { - handle, - app: self.app.clone(), - platform: self.platform.clone(), - background_executor: self.background_executor.clone(), - } - } - - /// Open a test window with specific options. - pub fn open_window_with_options( - &mut self, - options: WindowOptions, - build_view: impl FnOnce(&mut Window, &mut Context) -> V, - ) -> TestAppWindow { - let handle = self.update(|cx| { - cx.open_window(options, |window, cx| cx.new(|cx| build_view(window, cx))) - .unwrap() - }); - - TestAppWindow { - handle, - app: self.app.clone(), - platform: self.platform.clone(), - background_executor: self.background_executor.clone(), - } - } - - /// Run pending tasks until there's nothing left to do. - pub fn run_until_parked(&self) { - self.background_executor.run_until_parked(); - } - - /// Advance the simulated clock by the given duration. - pub fn advance_clock(&self, duration: Duration) { - self.background_executor.advance_clock(duration); - } - - /// Spawn a future on the foreground executor. - pub fn spawn(&self, f: impl FnOnce(AsyncApp) -> Fut) -> Task - where - Fut: Future + 'static, - R: 'static, - { - self.foreground_executor.spawn(f(self.to_async())) - } - - /// Spawn a future on the background executor. - pub fn background_spawn(&self, future: impl Future + Send + 'static) -> Task - where - R: Send + 'static, - { - self.background_executor.spawn(future) - } - - /// Get an async handle to the app. - pub fn to_async(&self) -> AsyncApp { - AsyncApp { - app: Rc::downgrade(&self.app), - background_executor: self.background_executor.clone(), - foreground_executor: self.foreground_executor.clone(), - } - } - - /// Get the background executor. - pub fn background_executor(&self) -> &BackgroundExecutor { - &self.background_executor - } - - /// Get the foreground executor. - pub fn foreground_executor(&self) -> &ForegroundExecutor { - &self.foreground_executor - } - - /// Get the text system. - pub fn text_system(&self) -> &Arc { - &self.text_system - } - - /// Check if a global of the given type exists. - pub fn has_global(&self) -> bool { - self.read(|cx| cx.has_global::()) - } - - /// Set a global value. - pub fn set_global(&mut self, global: G) { - self.update(|cx| cx.set_global(global)); - } - - /// Read a global value. - pub fn read_global(&self, f: impl FnOnce(&G, &App) -> R) -> R { - self.read(|cx| f(cx.global(), cx)) - } - - /// Update a global value. - pub fn update_global(&mut self, f: impl FnOnce(&mut G, &mut App) -> R) -> R { - self.update(|cx| cx.update_global(f)) - } - - // Platform simulation methods - - /// Write text to the simulated clipboard. - pub fn write_to_clipboard(&self, item: ClipboardItem) { - self.platform.write_to_clipboard(item); - } - - /// Read from the simulated clipboard. - pub fn read_from_clipboard(&self) -> Option { - self.platform.read_from_clipboard() - } - - /// Get URLs that have been opened via `cx.open_url()`. - pub fn opened_url(&self) -> Option { - self.platform.opened_url.borrow().clone() - } - - /// Check if a file path prompt is pending. - pub fn did_prompt_for_new_path(&self) -> bool { - self.platform.did_prompt_for_new_path() - } - - /// Simulate answering a path selection dialog. - pub fn simulate_new_path_selection( - &self, - select: impl FnOnce(&std::path::Path) -> Option, - ) { - self.platform.simulate_new_path_selection(select); - } - - /// Check if a prompt dialog is pending. - pub fn has_pending_prompt(&self) -> bool { - self.platform.has_pending_prompt() - } - - /// Simulate answering a prompt dialog. - pub fn simulate_prompt_answer(&self, button: &str) { - self.platform.simulate_prompt_answer(button); - } - - /// Get all open windows. - pub fn windows(&self) -> Vec { - self.read(|cx| cx.windows()) - } -} - -impl Default for TestApp { - fn default() -> Self { - Self::new() - } -} - -/// A test window with inspection and simulation capabilities. -pub struct TestAppWindow { - handle: WindowHandle, - app: Rc, - platform: Rc, - background_executor: BackgroundExecutor, -} - -impl TestAppWindow { - /// Get the window handle. - pub fn handle(&self) -> WindowHandle { - self.handle - } - - /// Get the root view entity. - pub fn root(&self) -> Entity { - let mut app = self.app.borrow_mut(); - let any_handle: AnyWindowHandle = self.handle.into(); - app.update_window(any_handle, |root_view, _, _| { - root_view.downcast::().expect("root view type mismatch") - }) - .expect("window not found") - } - - /// Update the root view. - pub fn update(&mut self, f: impl FnOnce(&mut V, &mut Window, &mut Context) -> R) -> R { - let result = { - let mut app = self.app.borrow_mut(); - let any_handle: AnyWindowHandle = self.handle.into(); - app.update_window(any_handle, |root_view, window, cx| { - let view = root_view.downcast::().expect("root view type mismatch"); - view.update(cx, |view, cx| f(view, window, cx)) - }) - .expect("window not found") - }; - self.background_executor.run_until_parked(); - result - } - - /// Read the root view. - pub fn read(&self, f: impl FnOnce(&V, &App) -> R) -> R { - let app = self.app.borrow(); - let view = self - .app - .borrow() - .windows - .get(self.handle.window_id()) - .and_then(|w| w.as_ref()) - .and_then(|w| w.root.clone()) - .and_then(|r| r.downcast::().ok()) - .expect("window or root view not found"); - f(view.read(&app), &app) - } - - /// Get the window title. - pub fn title(&self) -> Option { - let app = self.app.borrow(); - app.read_window(&self.handle, |_, _cx| { - // TODO: expose title through Window API - None - }) - .unwrap() - } - - /// Simulate a keystroke. - pub fn simulate_keystroke(&mut self, keystroke: &str) { - let keystroke = Keystroke::parse(keystroke).unwrap(); - { - let mut app = self.app.borrow_mut(); - let any_handle: AnyWindowHandle = self.handle.into(); - app.update_window(any_handle, |_, window, cx| { - window.dispatch_keystroke(keystroke, cx); - }) - .unwrap(); - } - self.background_executor.run_until_parked(); - } - - /// Simulate multiple keystrokes (space-separated). - pub fn simulate_keystrokes(&mut self, keystrokes: &str) { - for keystroke in keystrokes.split(' ') { - self.simulate_keystroke(keystroke); - } - } - - /// Simulate typing text. - pub fn simulate_input(&mut self, input: &str) { - for char in input.chars() { - self.simulate_keystroke(&char.to_string()); - } - } - - /// Simulate a mouse move. - pub fn simulate_mouse_move(&mut self, position: Point) { - self.simulate_event(MouseMoveEvent { - position, - modifiers: Default::default(), - pressed_button: None, - }); - } - - /// Simulate a mouse down event. - pub fn simulate_mouse_down(&mut self, position: Point, button: MouseButton) { - self.simulate_event(MouseDownEvent { - position, - button, - modifiers: Default::default(), - click_count: 1, - first_mouse: false, - }); - } - - /// Simulate a mouse up event. - pub fn simulate_mouse_up(&mut self, position: Point, button: MouseButton) { - self.simulate_event(MouseUpEvent { - position, - button, - modifiers: Default::default(), - click_count: 1, - }); - } - - /// Simulate a click at the given position. - pub fn simulate_click(&mut self, position: Point, button: MouseButton) { - self.simulate_mouse_down(position, button); - self.simulate_mouse_up(position, button); - } - - /// Simulate a scroll event. - pub fn simulate_scroll(&mut self, position: Point, delta: Point) { - self.simulate_event(crate::ScrollWheelEvent { - position, - delta: crate::ScrollDelta::Pixels(delta), - modifiers: Default::default(), - touch_phase: crate::TouchPhase::Moved, - }); - } - - /// Simulate an input event. - pub fn simulate_event(&mut self, event: E) { - let platform_input = event.to_platform_input(); - { - let mut app = self.app.borrow_mut(); - let any_handle: AnyWindowHandle = self.handle.into(); - app.update_window(any_handle, |_, window, cx| { - window.dispatch_event(platform_input, cx); - }) - .unwrap(); - } - self.background_executor.run_until_parked(); - } - - /// Simulate resizing the window. - pub fn simulate_resize(&mut self, size: Size) { - let window_id = self.handle.window_id(); - let mut app = self.app.borrow_mut(); - if let Some(Some(window)) = app.windows.get_mut(window_id) { - if let Some(test_window) = window.platform_window.as_test() { - test_window.simulate_resize(size); - } - } - drop(app); - self.background_executor.run_until_parked(); - } - - /// Force a redraw of the window. - pub fn draw(&mut self) { - let mut app = self.app.borrow_mut(); - let any_handle: AnyWindowHandle = self.handle.into(); - app.update_window(any_handle, |_, window, cx| { - window.draw(cx).clear(cx); - }) - .unwrap(); - } -} - -impl Clone for TestAppWindow { - fn clone(&self) -> Self { - Self { - handle: self.handle, - app: self.app.clone(), - platform: self.platform.clone(), - background_executor: self.background_executor.clone(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{FocusHandle, Focusable, div, prelude::*}; - - struct Counter { - count: usize, - focus_handle: FocusHandle, - } - - impl Counter { - fn new(_window: &mut Window, cx: &mut Context) -> Self { - let focus_handle = cx.focus_handle(); - Self { - count: 0, - focus_handle, - } - } - - fn increment(&mut self, _cx: &mut Context) { - self.count += 1; - } - } - - impl Focusable for Counter { - fn focus_handle(&self, _cx: &App) -> FocusHandle { - self.focus_handle.clone() - } - } - - impl Render for Counter { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div().child(format!("Count: {}", self.count)) - } - } - - #[test] - fn test_basic_usage() { - let mut app = TestApp::new(); - - let mut window = app.open_window(Counter::new); - - window.update(|counter, _window, cx| { - counter.increment(cx); - }); - - window.read(|counter, _| { - assert_eq!(counter.count, 1); - }); - - drop(window); - app.update(|cx| cx.shutdown()); - } - - #[test] - fn test_entity_creation() { - let mut app = TestApp::new(); - - let entity = app.new_entity(|cx| Counter { - count: 42, - focus_handle: cx.focus_handle(), - }); - - app.read_entity(&entity, |counter, _| { - assert_eq!(counter.count, 42); - }); - - app.update_entity(&entity, |counter, _cx| { - counter.count += 1; - }); - - app.read_entity(&entity, |counter, _| { - assert_eq!(counter.count, 43); - }); - } - - #[test] - fn test_globals() { - let mut app = TestApp::new(); - - struct MyGlobal(String); - impl Global for MyGlobal {} - - assert!(!app.has_global::()); - - app.set_global(MyGlobal("hello".into())); - - assert!(app.has_global::()); - - app.read_global::(|global, _| { - assert_eq!(global.0, "hello"); - }); - - app.update_global::(|global, _| { - global.0 = "world".into(); - }); - - app.read_global::(|global, _| { - assert_eq!(global.0, "world"); - }); - } -} diff --git a/crates/gpui_pre/src/app/test_context.rs b/crates/gpui_pre/src/app/test_context.rs deleted file mode 100644 index 1dceee09..00000000 --- a/crates/gpui_pre/src/app/test_context.rs +++ /dev/null @@ -1,1337 +0,0 @@ -use crate::{ - Action, AnyView, AnyWindowHandle, App, AppCell, AppContext, AsyncApp, AvailableSpace, - BackgroundExecutor, BorrowAppContext, Bounds, Capslock, ClipboardItem, DrawPhase, Drawable, - Element, Empty, EntityId, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, - Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, - Pixels, Platform, Point, Render, Result, SharedString, Size, SystemNotification, - SystemNotificationResponse, Task, TestDispatcher, TestPlatform, TestScreenCaptureSource, - TestWindow, TextSystem, VisualContext, Window, WindowBounds, WindowHandle, WindowOptions, - app::GpuiMode, window::ElementArenaScope, -}; -use anyhow::{anyhow, bail}; -use futures::{Stream, StreamExt, channel::oneshot}; - -use std::{ - cell::RefCell, future::Future, ops::Deref, path::PathBuf, rc::Rc, sync::Arc, time::Duration, -}; - -/// A TestAppContext is provided to tests created with `#[gpui::test]`, it provides -/// an implementation of `Context` with additional methods that are useful in tests. -#[derive(Clone)] -pub struct TestAppContext { - #[doc(hidden)] - pub background_executor: BackgroundExecutor, - #[doc(hidden)] - pub foreground_executor: ForegroundExecutor, - #[doc(hidden)] - pub dispatcher: TestDispatcher, - test_platform: Rc, - text_system: Arc, - fn_name: Option<&'static str>, - on_quit: Rc>>>, - #[doc(hidden)] - pub app: Rc, -} - -impl AppContext for TestAppContext { - fn new(&mut self, build_entity: impl FnOnce(&mut Context) -> T) -> Entity { - let mut app = self.app.borrow_mut(); - app.new(build_entity) - } - - fn reserve_entity(&mut self) -> crate::Reservation { - let mut app = self.app.borrow_mut(); - app.reserve_entity() - } - - fn insert_entity( - &mut self, - reservation: crate::Reservation, - build_entity: impl FnOnce(&mut Context) -> T, - ) -> Entity { - let mut app = self.app.borrow_mut(); - app.insert_entity(reservation, build_entity) - } - - fn update_entity( - &mut self, - handle: &Entity, - update: impl FnOnce(&mut T, &mut Context) -> R, - ) -> R { - let mut app = self.app.borrow_mut(); - app.update_entity(handle, update) - } - - fn as_mut<'a, T>(&'a mut self, _: &Entity) -> super::GpuiBorrow<'a, T> - where - T: 'static, - { - panic!("Cannot use as_mut with a test app context. Try calling update() first") - } - - fn read_entity(&self, handle: &Entity, read: impl FnOnce(&T, &App) -> R) -> R - where - T: 'static, - { - let app = self.app.borrow(); - app.read_entity(handle, read) - } - - fn update_window(&mut self, window: AnyWindowHandle, f: F) -> Result - where - F: FnOnce(AnyView, &mut Window, &mut App) -> T, - { - let mut lock = self.app.borrow_mut(); - lock.update_window(window, f) - } - - fn with_window( - &mut self, - entity_id: EntityId, - f: impl FnOnce(&mut Window, &mut App) -> R, - ) -> Option { - let mut lock = self.app.borrow_mut(); - lock.with_window(entity_id, f) - } - - fn read_window( - &self, - window: &WindowHandle, - read: impl FnOnce(Entity, &App) -> R, - ) -> Result - where - T: 'static, - { - let app = self.app.borrow(); - app.read_window(window, read) - } - - fn background_spawn(&self, future: impl Future + Send + 'static) -> Task - where - R: Send + 'static, - { - self.background_executor.spawn(future) - } - - fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> R - where - G: Global, - { - let app = self.app.borrow(); - app.read_global(callback) - } -} - -impl TestAppContext { - /// Creates a new `TestAppContext`. Usually you can rely on `#[gpui::test]` to do this for you. - pub fn build(dispatcher: TestDispatcher, fn_name: Option<&'static str>) -> Self { - let arc_dispatcher = Arc::new(dispatcher.clone()); - let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(arc_dispatcher); - let platform = TestPlatform::new(background_executor.clone(), foreground_executor.clone()); - let asset_source = Arc::new(()); - let http_client = http_client::FakeHttpClient::with_404_response(); - let text_system = Arc::new(TextSystem::new(platform.text_system())); - - let app = App::new_app(platform.clone(), asset_source, http_client); - app.borrow_mut().mode = GpuiMode::test(); - - Self { - app, - background_executor, - foreground_executor, - dispatcher, - test_platform: platform, - text_system, - fn_name, - on_quit: Rc::new(RefCell::new(Vec::default())), - } - } - - /// Skip all drawing operations for the duration of this test. - pub fn skip_drawing(&mut self) { - self.app.borrow_mut().mode = GpuiMode::Test { skip_drawing: true }; - } - - /// Create a single TestAppContext, for non-multi-client tests - pub fn single() -> Self { - let dispatcher = TestDispatcher::new(0); - Self::build(dispatcher, None) - } - - /// The name of the test function that created this `TestAppContext` - pub fn test_function_name(&self) -> Option<&'static str> { - self.fn_name - } - - /// Checks whether there have been any new path prompts received by the platform. - pub fn did_prompt_for_new_path(&self) -> bool { - self.test_platform.did_prompt_for_new_path() - } - - /// returns a new `TestAppContext` re-using the same executors to interleave tasks. - pub fn new_app(&self) -> TestAppContext { - Self::build(self.dispatcher.clone(), self.fn_name) - } - - /// Called by the test helper to end the test. - /// public so the macro can call it. - pub fn quit(&self) { - self.on_quit.borrow_mut().drain(..).for_each(|f| f()); - self.app.borrow_mut().shutdown(); - } - - /// Register cleanup to run when the test ends. - pub fn on_quit(&mut self, f: impl FnOnce() + 'static) { - self.on_quit.borrow_mut().push(Box::new(f)); - } - - /// Schedules all windows to be redrawn on the next effect cycle. - pub fn refresh(&mut self) -> Result<()> { - let mut app = self.app.borrow_mut(); - app.refresh_windows(); - Ok(()) - } - - /// Returns an executor (for running tasks in the background) - pub fn executor(&self) -> BackgroundExecutor { - self.background_executor.clone() - } - - /// Returns an executor (for running tasks on the main thread) - pub fn foreground_executor(&self) -> &ForegroundExecutor { - &self.foreground_executor - } - - /// Gives you an `&mut App` for the duration of the closure - pub fn update(&self, f: impl FnOnce(&mut App) -> R) -> R { - let mut cx = self.app.borrow_mut(); - cx.update(f) - } - - /// Gives you an `&App` for the duration of the closure - pub fn read(&self, f: impl FnOnce(&App) -> R) -> R { - let cx = self.app.borrow(); - f(&cx) - } - - /// Adds a new window. The Window will always be backed by a `TestWindow` which - /// can be retrieved with `self.test_window(handle)` - pub fn add_window(&mut self, build_window: F) -> WindowHandle - where - F: FnOnce(&mut Window, &mut Context) -> V, - V: 'static + Render, - { - let mut cx = self.app.borrow_mut(); - - // Some tests rely on the window size matching the bounds of the test display - let bounds = Bounds::maximized(None, &cx); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |window, cx| cx.new(|cx| build_window(window, cx)), - ) - .unwrap() - } - - /// Opens a new window with a specific size. - /// - /// Unlike `add_window` which uses maximized bounds, this allows controlling - /// the window dimensions, which is important for layout-sensitive tests. - pub fn open_window( - &mut self, - window_size: Size, - build_window: F, - ) -> WindowHandle - where - F: FnOnce(&mut Window, &mut Context) -> V, - V: 'static + Render, - { - let mut cx = self.app.borrow_mut(); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(Bounds { - origin: Point::default(), - size: window_size, - })), - ..Default::default() - }, - |window, cx| cx.new(|cx| build_window(window, cx)), - ) - .unwrap() - } - - /// Adds a new window with no content. - pub fn add_empty_window(&mut self) -> &mut VisualTestContext { - let mut cx = self.app.borrow_mut(); - let bounds = Bounds::maximized(None, &cx); - let window = cx - .open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |_, cx| cx.new(|_| Empty), - ) - .unwrap(); - drop(cx); - let cx = VisualTestContext::from_window(*window.deref(), self).into_mut(); - cx.run_until_parked(); - cx - } - - /// Adds a new window, and returns its root view and a `VisualTestContext` which can be used - /// as a `Window` and `App` for the rest of the test. Typically you would shadow this context with - /// the returned one. `let (view, cx) = cx.add_window_view(...);` - pub fn add_window_view( - &mut self, - build_root_view: F, - ) -> (Entity, &mut VisualTestContext) - where - F: FnOnce(&mut Window, &mut Context) -> V, - V: 'static + Render, - { - let mut cx = self.app.borrow_mut(); - let bounds = Bounds::maximized(None, &cx); - let window = cx - .open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - ..Default::default() - }, - |window, cx| cx.new(|cx| build_root_view(window, cx)), - ) - .unwrap(); - drop(cx); - let view = window.root(self).unwrap(); - let cx = VisualTestContext::from_window(*window.deref(), self).into_mut(); - cx.run_until_parked(); - - // it might be nice to try and cleanup these at the end of each test. - (view, cx) - } - - /// returns the TextSystem - pub fn text_system(&self) -> &Arc { - &self.text_system - } - - /// Simulates writing to the platform clipboard - pub fn write_to_clipboard(&self, item: ClipboardItem) { - self.test_platform.write_to_clipboard(item) - } - - /// Simulates reading from the platform clipboard. - /// This will return the most recent value from `write_to_clipboard`. - pub fn read_from_clipboard(&self) -> Option { - self.test_platform.read_from_clipboard() - } - - /// Simulates choosing a File in the platform's "Open" dialog. - pub fn simulate_new_path_selection( - &self, - select_path: impl FnOnce(&std::path::Path) -> Option, - ) { - self.test_platform.simulate_new_path_selection(select_path); - } - - /// Simulates responding to a `prompt_for_paths` ("Open") dialog. - pub fn simulate_path_prompt_response( - &self, - select_paths: impl FnOnce(&crate::PathPromptOptions) -> Option>, - ) { - self.test_platform - .simulate_path_prompt_response(select_paths); - } - - /// Returns true if there's a path selection dialog pending. - pub fn did_prompt_for_paths(&self) -> bool { - self.test_platform.did_prompt_for_paths() - } - - /// Simulates clicking a button in an platform-level alert dialog. - #[track_caller] - pub fn simulate_prompt_answer(&self, button: &str) { - self.test_platform.simulate_prompt_answer(button); - } - - /// Returns true if there's an alert dialog open. - pub fn has_pending_prompt(&self) -> bool { - self.test_platform.has_pending_prompt() - } - - /// Returns true if there's an alert dialog open. - pub fn pending_prompt(&self) -> Option<(String, String)> { - self.test_platform.pending_prompt() - } - - /// All the urls that have been opened with cx.open_url() during this test. - pub fn opened_url(&self) -> Option { - self.test_platform.opened_url.borrow().clone() - } - - /// Returns the application identity configured during this test. - pub fn app_identity(&self) -> Option<(SharedString, SharedString)> { - self.test_platform.app_identity() - } - - /// Returns all system notifications shown during this test, in order. - pub fn shown_system_notifications(&self) -> Vec { - self.test_platform.shown_system_notifications() - } - - /// Returns the system notifications currently delivered by the test platform. - pub fn delivered_system_notifications(&self) -> Vec { - self.test_platform.delivered_system_notifications() - } - - /// Returns the tags of all system notifications dismissed during this test, in order. - pub fn dismissed_system_notifications(&self) -> Vec { - self.test_platform.dismissed_system_notifications() - } - - /// Simulates the user activating a system notification. - pub fn simulate_system_notification_response(&self, response: SystemNotificationResponse) { - self.test_platform - .simulate_system_notification_response(response); - } - - /// Simulates the user resizing the window to the new size. - pub fn simulate_window_resize(&self, window_handle: AnyWindowHandle, size: Size) { - self.test_window(window_handle).simulate_resize(size); - } - - /// Returns true if there's an alert dialog open. - pub fn expect_restart(&self) -> oneshot::Receiver<(Option, Vec)> { - let (tx, rx) = futures::channel::oneshot::channel(); - self.test_platform.expect_restart.borrow_mut().replace(tx); - rx - } - - /// Causes the given sources to be returned if the application queries for screen - /// capture sources. - pub fn set_screen_capture_sources(&self, sources: Vec) { - self.test_platform.set_screen_capture_sources(sources); - } - - /// Returns all windows open in the test. - pub fn windows(&self) -> Vec { - self.app.borrow().windows() - } - - /// Run the given task on the main thread. - #[track_caller] - pub fn spawn(&self, f: impl FnOnce(AsyncApp) -> Fut) -> Task - where - Fut: Future + 'static, - R: 'static, - { - self.foreground_executor.spawn(f(self.to_async())) - } - - /// true if the given global is defined - pub fn has_global(&self) -> bool { - let app = self.app.borrow(); - app.has_global::() - } - - /// runs the given closure with a reference to the global - /// panics if `has_global` would return false. - pub fn read_global(&self, read: impl FnOnce(&G, &App) -> R) -> R { - let app = self.app.borrow(); - read(app.global(), &app) - } - - /// runs the given closure with a reference to the global (if set) - pub fn try_read_global(&self, read: impl FnOnce(&G, &App) -> R) -> Option { - let lock = self.app.borrow(); - Some(read(lock.try_global()?, &lock)) - } - - /// sets the global in this context. - pub fn set_global(&mut self, global: G) { - let mut lock = self.app.borrow_mut(); - lock.update(|cx| cx.set_global(global)) - } - - /// updates the global in this context. (panics if `has_global` would return false) - pub fn update_global(&mut self, update: impl FnOnce(&mut G, &mut App) -> R) -> R { - let mut lock = self.app.borrow_mut(); - lock.update(|cx| cx.update_global(update)) - } - - /// Returns an `AsyncApp` which can be used to run tasks that expect to be on a background - /// thread on the current thread in tests. - pub fn to_async(&self) -> AsyncApp { - AsyncApp { - app: Rc::downgrade(&self.app), - background_executor: self.background_executor.clone(), - foreground_executor: self.foreground_executor.clone(), - } - } - - /// Wait until there are no more pending tasks. - pub fn run_until_parked(&self) { - self.dispatcher.run_until_parked(); - } - - /// Simulate dispatching an action to the currently focused node in the window. - pub fn dispatch_action(&mut self, window: AnyWindowHandle, action: A) - where - A: Action, - { - window - .update(self, |_, window, cx| { - window.dispatch_action(action.boxed_clone(), cx) - }) - .unwrap(); - - self.background_executor.run_until_parked() - } - - /// simulate_keystrokes takes a space-separated list of keys to type. - /// cx.simulate_keystrokes("cmd-shift-p b k s p enter") - /// in Zed, this will run backspace on the current editor through the command palette. - /// This will also run the background executor until it's parked. - pub fn simulate_keystrokes(&mut self, window: AnyWindowHandle, keystrokes: &str) { - for keystroke in keystrokes - .split(' ') - .map(Keystroke::parse) - .map(Result::unwrap) - { - self.dispatch_keystroke(window, keystroke); - } - - self.background_executor.run_until_parked() - } - - /// simulate_input takes a string of text to type. - /// cx.simulate_input("abc") - /// will type abc into your current editor - /// This will also run the background executor until it's parked. - pub fn simulate_input(&mut self, window: AnyWindowHandle, input: &str) { - for keystroke in input.split("").map(Keystroke::parse).map(Result::unwrap) { - self.dispatch_keystroke(window, keystroke); - } - - self.background_executor.run_until_parked() - } - - /// dispatches a single Keystroke (see also `simulate_keystrokes` and `simulate_input`) - pub fn dispatch_keystroke(&mut self, window: AnyWindowHandle, keystroke: Keystroke) { - self.update_window(window, |_, window, cx| { - window.dispatch_keystroke(keystroke, cx) - }) - .unwrap(); - } - - /// Returns the `TestWindow` backing the given handle. - pub(crate) fn test_window(&self, window: AnyWindowHandle) -> TestWindow { - self.app - .borrow_mut() - .windows - .get_mut(window.id) - .unwrap() - .as_deref_mut() - .unwrap() - .platform_window - .as_test() - .unwrap() - .clone() - } - - /// Returns a stream of notifications whenever the Entity is updated. - pub fn notifications( - &mut self, - entity: &Entity, - ) -> impl Stream + use { - let (tx, rx) = futures::channel::mpsc::unbounded(); - self.update(|cx| { - cx.observe(entity, { - let tx = tx.clone(); - move |_, _| { - let _ = tx.unbounded_send(()); - } - }) - .detach(); - cx.observe_release(entity, move |_, _| tx.close_channel()) - .detach() - }); - rx - } - - /// Returns a stream of events emitted by the given Entity. - pub fn events>( - &mut self, - entity: &Entity, - ) -> futures::channel::mpsc::UnboundedReceiver - where - Evt: 'static + Clone, - { - let (tx, rx) = futures::channel::mpsc::unbounded(); - entity - .update(self, |_, cx: &mut Context| { - cx.subscribe(entity, move |_entity, _handle, event, _cx| { - let _ = tx.unbounded_send(event.clone()); - }) - }) - .detach(); - rx - } - - /// Runs until the given condition becomes true. (Prefer `run_until_parked` if you - /// don't need to jump in at a specific time). - pub async fn condition( - &mut self, - entity: &Entity, - mut predicate: impl FnMut(&mut T, &mut Context) -> bool, - ) { - let timer = self.executor().timer(Duration::from_secs(3)); - let mut notifications = self.notifications(entity); - - use futures::FutureExt as _; - use futures_concurrency::future::Race as _; - - ( - async { - loop { - if entity.update(self, &mut predicate) { - return Ok(()); - } - - if notifications.next().await.is_none() { - bail!("entity dropped") - } - } - }, - timer.map(|_| Err(anyhow!("condition timed out"))), - ) - .race() - .await - .unwrap(); - } - - /// Set a name for this App. - #[cfg(any(test, feature = "test-support"))] - pub fn set_name(&mut self, name: &'static str) { - self.update(|cx| cx.name = Some(name)) - } -} - -impl Entity { - /// Block until the next event is emitted by the entity, then return it. - pub fn next_event(&self, cx: &mut TestAppContext) -> impl Future - where - Event: Send + Clone + 'static, - T: EventEmitter, - { - let (tx, mut rx) = oneshot::channel(); - let mut tx = Some(tx); - let subscription = self.update(cx, |_, cx| { - cx.subscribe(self, move |_, _, event, _| { - if let Some(tx) = tx.take() { - _ = tx.send(event.clone()); - } - }) - }); - - async move { - let event = rx.await.expect("no event emitted"); - drop(subscription); - event - } - } -} - -impl Entity { - /// Returns a future that resolves when the view is next updated. - pub fn next_notification( - &self, - advance_clock_by: Duration, - cx: &TestAppContext, - ) -> impl Future { - use postage::prelude::{Sink as _, Stream as _}; - - let (mut tx, mut rx) = postage::mpsc::channel(1); - let subscription = cx.app.borrow_mut().observe(self, move |_, _| { - tx.try_send(()).ok(); - }); - - cx.executor().advance_clock(advance_clock_by); - - async move { - rx.recv() - .await - .expect("entity dropped while test was waiting for its next notification"); - drop(subscription); - } - } -} - -impl Entity { - /// Returns a future that resolves when the condition becomes true. - pub fn condition( - &self, - cx: &TestAppContext, - mut predicate: impl FnMut(&V, &App) -> bool, - ) -> impl Future - where - Evt: 'static, - V: EventEmitter, - { - use postage::prelude::{Sink as _, Stream as _}; - - let (tx, mut rx) = postage::mpsc::channel(1024); - - let mut cx = cx.app.borrow_mut(); - let subscriptions = ( - cx.observe(self, { - let mut tx = tx.clone(); - move |_, _| { - tx.blocking_send(()).ok(); - } - }), - cx.subscribe(self, { - let mut tx = tx; - move |_, _: &Evt, _| { - tx.blocking_send(()).ok(); - } - }), - ); - - let cx = cx.this.upgrade().unwrap(); - let handle = self.downgrade(); - - async move { - loop { - { - let cx = cx.borrow(); - let cx = &*cx; - if predicate( - handle - .upgrade() - .expect("view dropped with pending condition") - .read(cx), - cx, - ) { - break; - } - } - - rx.recv() - .await - .expect("view dropped with pending condition"); - } - drop(subscriptions); - } - } -} - -use derive_more::{Deref, DerefMut}; - -use super::{Context, Entity}; -#[derive(Deref, DerefMut, Clone)] -/// A VisualTestContext is the test-equivalent of a `Window` and `App`. It allows you to -/// run window-specific test code. It can be dereferenced to a `TextAppContext`. -pub struct VisualTestContext { - #[deref] - #[deref_mut] - /// cx is the original TestAppContext (you can more easily access this using Deref) - pub cx: TestAppContext, - window: AnyWindowHandle, -} - -impl VisualTestContext { - /// Provides a `Window` and `App` for the duration of the closure. - pub fn update(&mut self, f: impl FnOnce(&mut Window, &mut App) -> R) -> R { - self.cx - .update_window(self.window, |_, window, cx| f(window, cx)) - .unwrap() - } - - /// Creates a new VisualTestContext. You would typically shadow the passed in - /// TestAppContext with this, as this is typically more useful. - /// `let cx = VisualTestContext::from_window(window, cx);` - pub fn from_window(window: AnyWindowHandle, cx: &TestAppContext) -> Self { - Self { - cx: cx.clone(), - window, - } - } - - /// Wait until there are no more pending tasks. - pub fn run_until_parked(&self) { - self.cx.background_executor.run_until_parked(); - } - - /// Dispatch the action to the currently focused node. - pub fn dispatch_action(&mut self, action: A) - where - A: Action, - { - self.cx.dispatch_action(self.window, action) - } - - /// Read the title off the window (set by `Window#set_window_title`) - pub fn window_title(&mut self) -> Option { - self.cx.test_window(self.window).0.lock().title.clone() - } - - /// Read the document path off the window (set by `Window#set_document_path`) - pub fn document_path(&mut self) -> Option { - self.cx - .test_window(self.window) - .0 - .lock() - .document_path - .clone() - } - - /// Simulate a sequence of keystrokes `cx.simulate_keystrokes("cmd-p escape")` - /// Automatically runs until parked. - pub fn simulate_keystrokes(&mut self, keystrokes: &str) { - self.cx.simulate_keystrokes(self.window, keystrokes) - } - - /// Simulate typing text `cx.simulate_input("hello")` - /// Automatically runs until parked. - pub fn simulate_input(&mut self, input: &str) { - self.cx.simulate_input(self.window, input) - } - - /// Simulate a mouse move event to the given point - pub fn simulate_mouse_move( - &mut self, - position: Point, - button: impl Into>, - modifiers: Modifiers, - ) { - self.simulate_event(MouseMoveEvent { - position, - modifiers, - pressed_button: button.into(), - }) - } - - /// Simulate a mouse down event to the given point - pub fn simulate_mouse_down( - &mut self, - position: Point, - button: MouseButton, - modifiers: Modifiers, - ) { - self.simulate_event(MouseDownEvent { - position, - modifiers, - button, - click_count: 1, - first_mouse: false, - }) - } - - /// Simulate a mouse up event to the given point - pub fn simulate_mouse_up( - &mut self, - position: Point, - button: MouseButton, - modifiers: Modifiers, - ) { - self.simulate_event(MouseUpEvent { - position, - modifiers, - button, - click_count: 1, - }) - } - - /// Simulate a primary mouse click at the given point - pub fn simulate_click(&mut self, position: Point, modifiers: Modifiers) { - self.simulate_event(MouseDownEvent { - position, - modifiers, - button: MouseButton::Left, - click_count: 1, - first_mouse: false, - }); - self.simulate_event(MouseUpEvent { - position, - modifiers, - button: MouseButton::Left, - click_count: 1, - }); - } - - /// Simulate a modifiers changed event - pub fn simulate_modifiers_change(&mut self, modifiers: Modifiers) { - self.simulate_event(ModifiersChangedEvent { - modifiers, - capslock: Capslock { on: false }, - }) - } - - /// Simulate a capslock changed event - pub fn simulate_capslock_change(&mut self, on: bool) { - self.simulate_event(ModifiersChangedEvent { - modifiers: Modifiers::none(), - capslock: Capslock { on }, - }) - } - - /// Simulates the user resizing the window to the new size. - pub fn simulate_resize(&self, size: Size) { - self.simulate_window_resize(self.window, size) - } - - /// debug_bounds returns the bounds of the element with the given selector. - pub fn debug_bounds(&mut self, selector: &'static str) -> Option> { - self.update(|window, _| window.rendered_frame.debug_bounds.get(selector).copied()) - } - - /// Draw an element to the window. Useful for simulating events or actions - pub fn draw( - &mut self, - origin: Point, - space: impl Into>, - f: impl FnOnce(&mut Window, &mut App) -> E, - ) -> (E::RequestLayoutState, E::PrepaintState) - where - E: Element, - { - self.update(|window, cx| { - let arena_scope = ElementArenaScope::enter(&cx.element_arena); - - window.invalidator.set_phase(DrawPhase::Prepaint); - let mut element = Drawable::new(f(window, cx)); - element.layout_as_root(space.into(), window, cx); - window.with_absolute_element_offset(origin, |window| element.prepaint(window, cx)); - - window.invalidator.set_phase(DrawPhase::Paint); - let (request_layout_state, prepaint_state) = element.paint(window, cx); - - window.invalidator.set_phase(DrawPhase::None); - window.refresh(); - - drop(element); - arena_scope.exit(&cx.element_arena).clear(cx); - - (request_layout_state, prepaint_state) - }) - } - - /// Simulate an event from the platform, e.g. a ScrollWheelEvent - /// Make sure you've called [VisualTestContext::draw] first! - pub fn simulate_event(&mut self, event: E) { - self.test_window(self.window) - .simulate_input(event.to_platform_input()); - self.background_executor.run_until_parked(); - } - - /// Simulates the user blurring the window. - pub fn deactivate_window(&mut self) { - if Some(self.window) == self.test_platform.active_window() { - self.test_platform.set_active_window(None) - } - self.background_executor.run_until_parked(); - } - - /// Simulates the user closing the window. - /// Returns true if the window was closed. - pub fn simulate_close(&mut self) -> bool { - let handler = self - .cx - .update_window(self.window, |_, window, _| { - window - .platform_window - .as_test() - .unwrap() - .0 - .lock() - .should_close_handler - .take() - }) - .unwrap(); - if let Some(mut handler) = handler { - let should_close = handler(); - self.cx - .update_window(self.window, |_, window, _| { - window.platform_window.on_should_close(handler); - }) - .unwrap(); - should_close - } else { - false - } - } - - /// Get an &mut VisualTestContext (which is mostly what you need to pass to other methods). - /// This method internally retains the VisualTestContext until the end of the test. - pub fn into_mut(self) -> &'static mut Self { - let ptr = Box::into_raw(Box::new(self)); - // safety: on_quit will be called after the test has finished. - // the executor will ensure that all tasks related to the test have stopped. - // so there is no way for cx to be accessed after on_quit is called. - // todo: This is unsound under stacked borrows (also tree borrows probably?) - // the mutable reference invalidates `ptr` which is later used in the closure - let cx = unsafe { &mut *ptr }; - cx.on_quit(move || unsafe { - drop(Box::from_raw(ptr)); - }); - cx - } -} - -impl AppContext for VisualTestContext { - fn new(&mut self, build_entity: impl FnOnce(&mut Context) -> T) -> Entity { - self.window - .update(&mut self.cx, |_, _, cx| cx.new(build_entity)) - .expect("window was unexpectedly closed") - } - - fn reserve_entity(&mut self) -> crate::Reservation { - self.cx.reserve_entity() - } - - fn insert_entity( - &mut self, - reservation: crate::Reservation, - build_entity: impl FnOnce(&mut Context) -> T, - ) -> Entity { - self.window - .update(&mut self.cx, |_, _, cx| { - cx.insert_entity(reservation, build_entity) - }) - .expect("window was unexpectedly closed") - } - - fn update_entity( - &mut self, - handle: &Entity, - update: impl FnOnce(&mut T, &mut Context) -> R, - ) -> R - where - T: 'static, - { - self.cx.update_entity(handle, update) - } - - fn as_mut<'a, T>(&'a mut self, handle: &Entity) -> super::GpuiBorrow<'a, T> - where - T: 'static, - { - self.cx.as_mut(handle) - } - - fn read_entity(&self, handle: &Entity, read: impl FnOnce(&T, &App) -> R) -> R - where - T: 'static, - { - self.cx.read_entity(handle, read) - } - - fn update_window(&mut self, window: AnyWindowHandle, f: F) -> Result - where - F: FnOnce(AnyView, &mut Window, &mut App) -> T, - { - self.cx.update_window(window, f) - } - - fn with_window( - &mut self, - entity_id: EntityId, - f: impl FnOnce(&mut Window, &mut App) -> R, - ) -> Option { - self.cx.with_window(entity_id, f) - } - - fn read_window( - &self, - window: &WindowHandle, - read: impl FnOnce(Entity, &App) -> R, - ) -> Result - where - T: 'static, - { - self.cx.read_window(window, read) - } - - fn background_spawn(&self, future: impl Future + Send + 'static) -> Task - where - R: Send + 'static, - { - self.cx.background_spawn(future) - } - - fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> R - where - G: Global, - { - self.cx.read_global(callback) - } -} - -impl VisualContext for VisualTestContext { - type Result = T; - - /// Get the underlying window handle underlying this context. - fn window_handle(&self) -> AnyWindowHandle { - self.window - } - - fn new_window_entity( - &mut self, - build_entity: impl FnOnce(&mut Window, &mut Context) -> T, - ) -> Entity { - self.window - .update(&mut self.cx, |_, window, cx| { - cx.new(|cx| build_entity(window, cx)) - }) - .expect("window was unexpectedly closed") - } - - fn update_window_entity( - &mut self, - view: &Entity, - update: impl FnOnce(&mut V, &mut Window, &mut Context) -> R, - ) -> R { - let view = view.clone(); - self.cx - .app - .borrow_mut() - .with_window(view.entity_id(), |window, app| { - view.update(app, |v, cx| update(v, window, cx)) - }) - .expect("entity has no current window; use `update` instead of `update_in`") - } - - fn replace_root_view( - &mut self, - build_view: impl FnOnce(&mut Window, &mut Context) -> V, - ) -> Entity - where - V: 'static + Render, - { - self.window - .update(&mut self.cx, |_, window, cx| { - window.replace_root(cx, build_view) - }) - .expect("window was unexpectedly closed") - } - - fn focus(&mut self, view: &Entity) { - self.window - .update(&mut self.cx, |_, window, cx| { - view.read(cx).focus_handle(cx).focus(window, cx) - }) - .expect("window was unexpectedly closed") - } -} - -impl AnyWindowHandle { - /// Creates the given view in this window. - pub fn build_entity( - &self, - cx: &mut TestAppContext, - build_view: impl FnOnce(&mut Window, &mut Context) -> V, - ) -> Entity { - self.update(cx, |_, window, cx| cx.new(|cx| build_view(window, cx))) - .unwrap() - } -} - -#[cfg(test)] -mod tests { - use crate::{ - PathPromptOptions, SystemNotification, SystemNotificationAction, - SystemNotificationResponse, TestAppContext, - }; - use std::cell::RefCell; - use std::path::PathBuf; - use std::rc::Rc; - - #[gpui::test] - async fn test_system_notifications_require_identity_and_replace_matching_tags( - cx: &mut TestAppContext, - ) { - cx.update(|cx| { - cx.show_system_notification(SystemNotification { - tag: "thread-1".into(), - title: "Task started".into(), - body: "Running tests".into(), - actions: Vec::new(), - }); - }); - assert!(cx.shown_system_notifications().is_empty()); - assert!(cx.delivered_system_notifications().is_empty()); - - cx.update(|cx| { - cx.set_app_identity("com.example.tasks", "Tasks"); - cx.show_system_notification(SystemNotification { - tag: "thread-1".into(), - title: "Task started".into(), - body: "Running tests".into(), - actions: Vec::new(), - }); - cx.show_system_notification(SystemNotification { - tag: "thread-1".into(), - title: "Task finished".into(), - body: "All tests passed".into(), - actions: vec![SystemNotificationAction { - id: "open".into(), - label: "Open".into(), - }], - }); - }); - - assert_eq!( - cx.app_identity(), - Some(("com.example.tasks".into(), "Tasks".into())) - ); - assert_eq!(cx.shown_system_notifications().len(), 2); - assert_eq!( - cx.delivered_system_notifications(), - [SystemNotification { - tag: "thread-1".into(), - title: "Task finished".into(), - body: "All tests passed".into(), - actions: vec![SystemNotificationAction { - id: "open".into(), - label: "Open".into(), - }], - }] - ); - - cx.update(|cx| cx.dismiss_system_notification("thread-1")); - assert!(cx.delivered_system_notifications().is_empty()); - assert_eq!(cx.dismissed_system_notifications(), ["thread-1"]); - } - - #[gpui::test] - async fn test_system_notification_body_and_action_responses(cx: &mut TestAppContext) { - let responses = Rc::new(RefCell::new(Vec::new())); - cx.update(|cx| { - cx.on_system_notification_response({ - let responses = responses.clone(); - move |response, _cx| responses.borrow_mut().push(response) - }); - }); - - cx.simulate_system_notification_response(SystemNotificationResponse { - tag: "thread-1".into(), - action_id: None, - }); - cx.simulate_system_notification_response(SystemNotificationResponse { - tag: "thread-1".into(), - action_id: Some("default".into()), - }); - - assert_eq!( - responses.borrow().as_slice(), - &[ - SystemNotificationResponse { - tag: "thread-1".into(), - action_id: None, - }, - SystemNotificationResponse { - tag: "thread-1".into(), - action_id: Some("default".into()), - }, - ] - ); - } - - #[gpui::test] - async fn test_system_notification_response_handler_can_be_replaced(cx: &mut TestAppContext) { - let first_responses = Rc::new(RefCell::new(Vec::new())); - let second_responses = Rc::new(RefCell::new(Vec::new())); - cx.update(|cx| { - cx.on_system_notification_response({ - let first_responses = first_responses.clone(); - move |response, _cx| first_responses.borrow_mut().push(response) - }); - cx.on_system_notification_response({ - let second_responses = second_responses.clone(); - move |response, _cx| second_responses.borrow_mut().push(response) - }); - }); - - let response = SystemNotificationResponse { - tag: "thread-1".into(), - action_id: None, - }; - cx.simulate_system_notification_response(response.clone()); - - assert!(first_responses.borrow().is_empty()); - assert_eq!(second_responses.borrow().as_slice(), &[response]); - } - - #[gpui::test] - async fn test_system_notification_response_handler_can_reenter_app(cx: &mut TestAppContext) { - cx.update(|cx| { - cx.set_app_identity("com.example.tasks", "Tasks"); - cx.show_system_notification(SystemNotification { - tag: "thread-1".into(), - title: "Task finished".into(), - body: "All tests passed".into(), - actions: Vec::new(), - }); - cx.on_system_notification_response(|response, cx| { - cx.dismiss_system_notification(&response.tag); - }); - }); - - cx.simulate_system_notification_response(SystemNotificationResponse { - tag: "thread-1".into(), - action_id: None, - }); - - assert!(cx.delivered_system_notifications().is_empty()); - assert_eq!(cx.dismissed_system_notifications(), ["thread-1"]); - } - - #[gpui::test] - async fn test_simulate_path_prompt_response(cx: &mut TestAppContext) { - assert!(!cx.did_prompt_for_paths()); - - let receiver = cx.update(|cx| { - cx.prompt_for_paths(PathPromptOptions { - files: false, - directories: true, - multiple: true, - prompt: None, - }) - }); - assert!(cx.did_prompt_for_paths()); - - let selected = vec![PathBuf::from("/a"), PathBuf::from("/b")]; - cx.simulate_path_prompt_response({ - let selected = selected.clone(); - move |options| { - assert!(options.multiple); - Some(selected) - } - }); - assert!(!cx.did_prompt_for_paths()); - - let response = receiver.await.unwrap().unwrap(); - assert_eq!(response, Some(selected)); - } - - #[gpui::test] - async fn test_simulate_path_prompt_cancellation(cx: &mut TestAppContext) { - let receiver = cx.update(|cx| { - cx.prompt_for_paths(PathPromptOptions { - files: true, - directories: false, - multiple: false, - prompt: None, - }) - }); - - cx.simulate_path_prompt_response(|_options| None); - - let response = receiver.await.unwrap().unwrap(); - assert_eq!(response, None); - } -} diff --git a/crates/gpui_pre/src/app/visual_test_context.rs b/crates/gpui_pre/src/app/visual_test_context.rs deleted file mode 100644 index b54802a2..00000000 --- a/crates/gpui_pre/src/app/visual_test_context.rs +++ /dev/null @@ -1,484 +0,0 @@ -use crate::{ - Action, AnyView, AnyWindowHandle, App, AppCell, AppContext, AssetSource, BackgroundExecutor, - Bounds, ClipboardItem, Context, Entity, EntityId, ForegroundExecutor, Global, InputEvent, - Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, - Platform, Point, Render, Result, Size, Task, TestDispatcher, TextSystem, VisualTestPlatform, - Window, WindowBounds, WindowHandle, WindowOptions, app::GpuiMode, -}; -use anyhow::anyhow; -use image::RgbaImage; -use std::{future::Future, rc::Rc, sync::Arc, time::Duration}; - -/// A test context that uses real macOS rendering instead of mocked rendering. -/// This is used for visual tests that need to capture actual screenshots. -/// -/// Unlike `TestAppContext` which uses `TestPlatform` with mocked rendering, -/// `VisualTestAppContext` uses the real `MacPlatform` to produce actual rendered output. -/// -/// Windows created through this context are positioned off-screen (at coordinates like -10000, -10000) -/// so they are invisible to the user but still fully rendered by the compositor. -#[derive(Clone)] -pub struct VisualTestAppContext { - /// The underlying app cell - pub app: Rc, - /// The background executor for running async tasks - pub background_executor: BackgroundExecutor, - /// The foreground executor for running tasks on the main thread - pub foreground_executor: ForegroundExecutor, - /// The test dispatcher for deterministic task scheduling - dispatcher: TestDispatcher, - platform: Rc, - text_system: Arc, -} - -impl VisualTestAppContext { - /// Creates a new `VisualTestAppContext` with real macOS platform rendering - /// but deterministic task scheduling via TestDispatcher. - /// - /// This provides: - /// - Real Metal/compositor rendering for accurate screenshots - /// - Deterministic task scheduling via TestDispatcher - /// - Controllable time via `advance_clock` - /// - /// Note: This uses a no-op asset source, so SVG icons won't render. - /// Use `with_asset_source` to provide real assets for icon rendering. - pub fn new(platform: Rc) -> Self { - Self::with_asset_source(platform, Arc::new(())) - } - - /// Creates a new `VisualTestAppContext` with a custom asset source. - /// - /// Use this when you need SVG icons to render properly in visual tests. - /// Pass the real `Assets` struct to enable icon rendering. - pub fn with_asset_source( - platform: Rc, - asset_source: Arc, - ) -> Self { - // Use a seeded RNG for deterministic behavior - let seed = std::env::var("SEED") - .ok() - .and_then(|s| s.parse().ok()) - .unwrap_or(0); - - // Create a visual test platform that combines real Mac rendering - // with controllable TestDispatcher for deterministic task scheduling - let platform = Rc::new(VisualTestPlatform::new(platform, seed)); - - // Get the dispatcher and executors from the platform - let dispatcher = platform.dispatcher().clone(); - let background_executor = platform.background_executor(); - let foreground_executor = platform.foreground_executor(); - - let text_system = Arc::new(TextSystem::new(platform.text_system())); - - let http_client = http_client::FakeHttpClient::with_404_response(); - - let mut app = App::new_app(platform.clone(), asset_source, http_client); - app.borrow_mut().mode = GpuiMode::test(); - - Self { - app, - background_executor, - foreground_executor, - dispatcher, - platform, - text_system, - } - } - - /// Opens a window positioned off-screen for invisible rendering. - /// - /// The window is positioned at (-10000, -10000) so it's not visible on any display, - /// but it's still fully rendered by the compositor and can be captured via ScreenCaptureKit. - /// - /// # Arguments - /// * `size` - The size of the window to create - /// * `build_root` - A closure that builds the root view for the window - pub fn open_offscreen_window( - &mut self, - size: Size, - build_root: impl FnOnce(&mut Window, &mut App) -> Entity, - ) -> Result> { - use crate::{point, px}; - - let bounds = Bounds { - origin: point(px(-10000.0), px(-10000.0)), - size, - }; - - let mut cx = self.app.borrow_mut(); - cx.open_window( - WindowOptions { - window_bounds: Some(WindowBounds::Windowed(bounds)), - focus: false, - show: true, - ..Default::default() - }, - build_root, - ) - } - - /// Opens an off-screen window with default size (1280x800). - pub fn open_offscreen_window_default( - &mut self, - build_root: impl FnOnce(&mut Window, &mut App) -> Entity, - ) -> Result> { - use crate::{px, size}; - self.open_offscreen_window(size(px(1280.0), px(800.0)), build_root) - } - - /// Returns whether screen capture is supported on this platform. - pub fn is_screen_capture_supported(&self) -> bool { - self.platform.is_screen_capture_supported() - } - - /// Returns the text system used by this context. - pub fn text_system(&self) -> &Arc { - &self.text_system - } - - /// Returns the background executor. - pub fn executor(&self) -> BackgroundExecutor { - self.background_executor.clone() - } - - /// Returns the foreground executor. - pub fn foreground_executor(&self) -> ForegroundExecutor { - self.foreground_executor.clone() - } - - /// Runs all pending foreground and background tasks until there's nothing left to do. - /// This is essential for processing async operations like tooltip timers. - pub fn run_until_parked(&self) { - self.dispatcher.run_until_parked(); - } - - /// Advances the simulated clock by the given duration and processes any tasks - /// that become ready. This is essential for testing time-based behaviors like - /// tooltip delays. - pub fn advance_clock(&self, duration: Duration) { - self.dispatcher.advance_clock(duration); - } - - /// Updates the app state. - pub fn update(&mut self, f: impl FnOnce(&mut App) -> R) -> R { - let mut app = self.app.borrow_mut(); - f(&mut app) - } - - /// Reads from the app state. - pub fn read(&self, f: impl FnOnce(&App) -> R) -> R { - let app = self.app.borrow(); - f(&app) - } - - /// Updates a window. - pub fn update_window(&mut self, window: AnyWindowHandle, f: F) -> Result - where - F: FnOnce(AnyView, &mut Window, &mut App) -> T, - { - let mut lock = self.app.borrow_mut(); - lock.update_window(window, f) - } - - /// Spawns a task on the foreground executor. - pub fn spawn(&self, f: F) -> Task - where - F: Future + 'static, - R: 'static, - { - self.foreground_executor.spawn(f) - } - - /// Checks if a global of type G exists. - pub fn has_global(&self) -> bool { - let app = self.app.borrow(); - app.has_global::() - } - - /// Reads a global value. - pub fn read_global(&self, f: impl FnOnce(&G, &App) -> R) -> R { - let app = self.app.borrow(); - f(app.global::(), &app) - } - - /// Sets a global value. - pub fn set_global(&mut self, global: G) { - let mut app = self.app.borrow_mut(); - app.set_global(global); - } - - /// Updates a global value. - pub fn update_global(&mut self, f: impl FnOnce(&mut G, &mut App) -> R) -> R { - let mut lock = self.app.borrow_mut(); - lock.update(|cx| { - let mut global = cx.lease_global::(); - let result = f(&mut global, cx); - cx.end_global_lease(global); - result - }) - } - - /// Simulates a sequence of keystrokes on the given window. - /// - /// Keystrokes are specified as a space-separated string, e.g., "cmd-p escape". - pub fn simulate_keystrokes(&mut self, window: AnyWindowHandle, keystrokes: &str) { - for keystroke_text in keystrokes.split_whitespace() { - let keystroke = Keystroke::parse(keystroke_text) - .unwrap_or_else(|_| panic!("Invalid keystroke: {}", keystroke_text)); - self.dispatch_keystroke(window, keystroke); - } - self.run_until_parked(); - } - - /// Dispatches a single keystroke to a window. - pub fn dispatch_keystroke(&mut self, window: AnyWindowHandle, keystroke: Keystroke) { - self.update_window(window, |_, window, cx| { - window.dispatch_keystroke(keystroke, cx); - }) - .ok(); - } - - /// Simulates typing text input on the given window. - pub fn simulate_input(&mut self, window: AnyWindowHandle, input: &str) { - for char in input.chars() { - let key = char.to_string(); - let keystroke = Keystroke { - modifiers: Modifiers::default(), - key: key.clone(), - key_char: Some(key), - }; - self.dispatch_keystroke(window, keystroke); - } - self.run_until_parked(); - } - - /// Simulates a mouse move event. - pub fn simulate_mouse_move( - &mut self, - window: AnyWindowHandle, - position: Point, - button: impl Into>, - modifiers: Modifiers, - ) { - self.simulate_event( - window, - MouseMoveEvent { - position, - modifiers, - pressed_button: button.into(), - }, - ); - } - - /// Simulates a mouse down event. - pub fn simulate_mouse_down( - &mut self, - window: AnyWindowHandle, - position: Point, - button: MouseButton, - modifiers: Modifiers, - ) { - self.simulate_event( - window, - MouseDownEvent { - position, - modifiers, - button, - click_count: 1, - first_mouse: false, - }, - ); - } - - /// Simulates a mouse up event. - pub fn simulate_mouse_up( - &mut self, - window: AnyWindowHandle, - position: Point, - button: MouseButton, - modifiers: Modifiers, - ) { - self.simulate_event( - window, - MouseUpEvent { - position, - modifiers, - button, - click_count: 1, - }, - ); - } - - /// Simulates a click (mouse down followed by mouse up). - pub fn simulate_click( - &mut self, - window: AnyWindowHandle, - position: Point, - modifiers: Modifiers, - ) { - self.simulate_mouse_down(window, position, MouseButton::Left, modifiers); - self.simulate_mouse_up(window, position, MouseButton::Left, modifiers); - } - - /// Simulates an input event on the given window. - pub fn simulate_event(&mut self, window: AnyWindowHandle, event: E) { - self.update_window(window, |_, window, cx| { - window.dispatch_event(event.to_platform_input(), cx); - }) - .ok(); - self.run_until_parked(); - } - - /// Dispatches an action to the given window. - pub fn dispatch_action(&mut self, window: AnyWindowHandle, action: impl Action) { - self.update_window(window, |_, window, cx| { - window.dispatch_action(action.boxed_clone(), cx); - }) - .ok(); - self.run_until_parked(); - } - - /// Writes to the clipboard. - pub fn write_to_clipboard(&self, item: ClipboardItem) { - self.platform.write_to_clipboard(item); - } - - /// Reads from the clipboard. - pub fn read_from_clipboard(&self) -> Option { - self.platform.read_from_clipboard() - } - - /// Waits for a condition to become true, with a timeout. - pub async fn wait_for( - &mut self, - entity: &Entity, - predicate: impl Fn(&T) -> bool, - timeout: Duration, - ) -> Result<()> { - let start = web_time::Instant::now(); - loop { - { - let app = self.app.borrow(); - if predicate(entity.read(&app)) { - return Ok(()); - } - } - - if start.elapsed() > timeout { - return Err(anyhow!("Timed out waiting for condition")); - } - - self.run_until_parked(); - self.background_executor - .timer(Duration::from_millis(10)) - .await; - } - } - - /// Captures a screenshot of the specified window using direct texture capture. - /// - /// This renders the scene to a Metal texture and reads the pixels directly, - /// which does not require the window to be visible on screen. - #[cfg(any(test, feature = "test-support"))] - pub fn capture_screenshot(&mut self, window: AnyWindowHandle) -> Result { - self.update_window(window, |_, window, _cx| window.render_to_image())? - } - - /// Waits for animations to complete by waiting a couple of frames. - pub async fn wait_for_animations(&self) { - self.background_executor - .timer(Duration::from_millis(32)) - .await; - self.run_until_parked(); - } -} - -impl AppContext for VisualTestAppContext { - fn new(&mut self, build_entity: impl FnOnce(&mut Context) -> T) -> Entity { - let mut app = self.app.borrow_mut(); - app.new(build_entity) - } - - fn reserve_entity(&mut self) -> crate::Reservation { - let mut app = self.app.borrow_mut(); - app.reserve_entity() - } - - fn insert_entity( - &mut self, - reservation: crate::Reservation, - build_entity: impl FnOnce(&mut Context) -> T, - ) -> Entity { - let mut app = self.app.borrow_mut(); - app.insert_entity(reservation, build_entity) - } - - fn update_entity( - &mut self, - handle: &Entity, - update: impl FnOnce(&mut T, &mut Context) -> R, - ) -> R { - let mut app = self.app.borrow_mut(); - app.update_entity(handle, update) - } - - fn as_mut<'a, T>(&'a mut self, _: &Entity) -> crate::GpuiBorrow<'a, T> - where - T: 'static, - { - panic!("Cannot use as_mut with a visual test app context. Try calling update() first") - } - - fn read_entity(&self, handle: &Entity, read: impl FnOnce(&T, &App) -> R) -> R - where - T: 'static, - { - let app = self.app.borrow(); - app.read_entity(handle, read) - } - - fn update_window(&mut self, window: AnyWindowHandle, f: F) -> Result - where - F: FnOnce(AnyView, &mut Window, &mut App) -> T, - { - let mut lock = self.app.borrow_mut(); - lock.update_window(window, f) - } - - fn with_window( - &mut self, - entity_id: EntityId, - f: impl FnOnce(&mut Window, &mut App) -> R, - ) -> Option { - let mut lock = self.app.borrow_mut(); - lock.with_window(entity_id, f) - } - - fn read_window( - &self, - window: &WindowHandle, - read: impl FnOnce(Entity, &App) -> R, - ) -> Result - where - T: 'static, - { - let app = self.app.borrow(); - app.read_window(window, read) - } - - fn background_spawn(&self, future: impl Future + Send + 'static) -> Task - where - R: Send + 'static, - { - self.background_executor.spawn(future) - } - - fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> R - where - G: Global, - { - let app = self.app.borrow(); - callback(app.global::(), &app) - } -} diff --git a/crates/gpui_pre/src/arena.rs b/crates/gpui_pre/src/arena.rs deleted file mode 100644 index 2ba7a2f6..00000000 --- a/crates/gpui_pre/src/arena.rs +++ /dev/null @@ -1,396 +0,0 @@ -use std::{ - alloc::{self, handle_alloc_error}, - cell::Cell, - num::NonZeroUsize, - ops::{Deref, DerefMut}, - ptr::{self, NonNull}, - rc::Rc, -}; - -struct ArenaElement { - value: *mut u8, - drop: unsafe fn(*mut u8), -} - -impl Drop for ArenaElement { - #[inline(always)] - fn drop(&mut self) { - unsafe { (self.drop)(self.value) }; - } -} - -struct Chunk { - start: *mut u8, - end: *mut u8, - offset: *mut u8, -} - -impl Drop for Chunk { - fn drop(&mut self) { - unsafe { - let chunk_size = self.end.offset_from_unsigned(self.start); - // SAFETY: This succeeded during allocation. - let layout = alloc::Layout::from_size_align_unchecked(chunk_size, 1); - alloc::dealloc(self.start, layout); - } - } -} - -impl Chunk { - fn new(chunk_size: NonZeroUsize) -> Self { - // this only fails if chunk_size is unreasonably huge - let layout = alloc::Layout::from_size_align(chunk_size.get(), 1).unwrap(); - let start = unsafe { alloc::alloc(layout) }; - if start.is_null() { - handle_alloc_error(layout); - } - let end = unsafe { start.add(chunk_size.get()) }; - Self { - start, - end, - offset: start, - } - } - - fn allocate(&mut self, layout: alloc::Layout) -> Option> { - // Compute the allocation bounds in integer address space so that the - // bounds check happens before any pointer offsetting. Offsetting a - // pointer past the end of its allocation is undefined behavior even if - // the result is never dereferenced (as happens on the chunk-spill - // path), so `ptr::add` cannot be used until we know the result stays - // in bounds. `checked_add` also handles the documented case where - // `align_offset` returns `usize::MAX`. - let base = self.offset.addr(); - let aligned_addr = base.checked_add(self.offset.align_offset(layout.align()))?; - let next_addr = aligned_addr.checked_add(layout.size())?; - - if next_addr <= self.end.addr() { - let aligned = self.offset.with_addr(aligned_addr); - self.offset = self.offset.with_addr(next_addr); - NonNull::new(aligned) - } else { - None - } - } - - fn reset(&mut self) { - self.offset = self.start; - } -} - -pub struct Arena { - chunks: Vec, - elements: Vec, - valid: Rc>, - current_chunk_index: usize, - chunk_size: NonZeroUsize, - scope_depth: usize, -} - -impl Drop for Arena { - fn drop(&mut self) { - self.force_clear(); - } -} - -impl Arena { - pub fn new(chunk_size: usize) -> Self { - let chunk_size = NonZeroUsize::try_from(chunk_size).unwrap(); - Self { - chunks: vec![Chunk::new(chunk_size)], - elements: Vec::new(), - valid: Rc::new(Cell::new(true)), - current_chunk_index: 0, - chunk_size, - scope_depth: 0, - } - } - - pub fn capacity(&self) -> usize { - self.chunks.len() * self.chunk_size.get() - } - - /// Marks the start of a scope (e.g. a window draw) whose allocations must stay - /// live until the scope ends, even if `clear` is called by a nested scope in - /// the meantime. - pub fn begin_scope(&mut self) { - self.scope_depth += 1; - } - - /// Ends the innermost scope started with `begin_scope`. - /// - /// Panics if no scope is active: an unbalanced `end_scope` would let `clear` - /// run while an enclosing scope still references arena memory, which is - /// exactly the use-after-free this bookkeeping exists to prevent, so failing - /// loudly here is preferable. - pub fn end_scope(&mut self) { - self.scope_depth = self - .scope_depth - .checked_sub(1) - .expect("Arena::end_scope called without a matching begin_scope"); - } - - /// Drops all allocations and resets the arena, unless a scope is still active. - /// - /// When a draw triggers a nested draw (e.g. re-entrant window procedure - /// invocations on Windows, or opening a window from within a draw), the nested - /// draw's clear must not free memory the outer draw still references, so it is - /// deferred: the outer draw's own clear will drop both draws' allocations. - pub fn clear(&mut self) { - if self.scope_depth == 0 { - self.force_clear(); - } else { - log::debug!( - "deferring arena clear; {} enclosing scope(s) still active", - self.scope_depth - ); - } - } - - fn force_clear(&mut self) { - self.valid.set(false); - self.valid = Rc::new(Cell::new(true)); - self.elements.clear(); - for chunk_index in 0..=self.current_chunk_index { - self.chunks[chunk_index].reset(); - } - self.current_chunk_index = 0; - } - - #[inline(always)] - pub fn alloc(&mut self, f: impl FnOnce() -> T) -> ArenaBox { - #[inline(always)] - unsafe fn inner_writer(ptr: *mut T, f: F) - where - F: FnOnce() -> T, - { - unsafe { ptr::write(ptr, f()) }; - } - - unsafe fn drop(ptr: *mut u8) { - unsafe { std::ptr::drop_in_place(ptr.cast::()) }; - } - - let layout = alloc::Layout::new::(); - let mut current_chunk = &mut self.chunks[self.current_chunk_index]; - let ptr = if let Some(ptr) = current_chunk.allocate(layout) { - ptr.as_ptr() - } else { - self.current_chunk_index += 1; - if self.current_chunk_index >= self.chunks.len() { - self.chunks.push(Chunk::new(self.chunk_size)); - assert_eq!(self.current_chunk_index, self.chunks.len() - 1); - log::trace!( - "increased element arena capacity to {}kb", - self.capacity() / 1024, - ); - } - current_chunk = &mut self.chunks[self.current_chunk_index]; - if let Some(ptr) = current_chunk.allocate(layout) { - ptr.as_ptr() - } else { - panic!( - "Arena chunk_size of {} is too small to allocate {} bytes", - self.chunk_size, - layout.size() - ); - } - }; - - unsafe { inner_writer(ptr.cast(), f) }; - self.elements.push(ArenaElement { - value: ptr, - drop: drop::, - }); - - ArenaBox { - ptr: ptr.cast(), - valid: self.valid.clone(), - } - } -} - -pub struct ArenaBox { - ptr: *mut T, - valid: Rc>, -} - -impl ArenaBox { - #[inline(always)] - pub fn map(mut self, f: impl FnOnce(&mut T) -> &mut U) -> ArenaBox { - ArenaBox { - ptr: f(&mut self), - valid: self.valid, - } - } - - #[track_caller] - fn validate(&self) { - assert!( - self.valid.get(), - "attempted to dereference an ArenaRef after its Arena was cleared" - ); - } -} - -impl Deref for ArenaBox { - type Target = T; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - self.validate(); - unsafe { &*self.ptr } - } -} - -impl DerefMut for ArenaBox { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - self.validate(); - unsafe { &mut *self.ptr } - } -} - -#[cfg(test)] -mod tests { - use std::{cell::Cell, rc::Rc}; - - use super::*; - - #[test] - fn test_arena() { - let mut arena = Arena::new(1024); - let a = arena.alloc(|| 1u64); - let b = arena.alloc(|| 2u32); - let c = arena.alloc(|| 3u16); - let d = arena.alloc(|| 4u8); - assert_eq!(*a, 1); - assert_eq!(*b, 2); - assert_eq!(*c, 3); - assert_eq!(*d, 4); - - arena.clear(); - let a = arena.alloc(|| 5u64); - let b = arena.alloc(|| 6u32); - let c = arena.alloc(|| 7u16); - let d = arena.alloc(|| 8u8); - assert_eq!(*a, 5); - assert_eq!(*b, 6); - assert_eq!(*c, 7); - assert_eq!(*d, 8); - - // Ensure drop gets called. - let dropped = Rc::new(Cell::new(false)); - struct DropGuard(Rc>); - impl Drop for DropGuard { - fn drop(&mut self) { - self.0.set(true); - } - } - arena.alloc(|| DropGuard(dropped.clone())); - arena.clear(); - assert!(dropped.get()); - } - - #[test] - fn test_arena_grow() { - let mut arena = Arena::new(8); - arena.alloc(|| 1u64); - arena.alloc(|| 2u64); - - assert_eq!(arena.capacity(), 16); - - arena.alloc(|| 3u32); - arena.alloc(|| 4u32); - - assert_eq!(arena.capacity(), 24); - } - - #[test] - fn test_arena_alignment() { - let mut arena = Arena::new(256); - let x1 = arena.alloc(|| 1u8); - let x2 = arena.alloc(|| 2u16); - let x3 = arena.alloc(|| 3u32); - let x4 = arena.alloc(|| 4u64); - let x5 = arena.alloc(|| 5u64); - - assert_eq!(*x1, 1); - assert_eq!(*x2, 2); - assert_eq!(*x3, 3); - assert_eq!(*x4, 4); - assert_eq!(*x5, 5); - - assert_eq!(x1.ptr.align_offset(std::mem::align_of_val(&*x1)), 0); - assert_eq!(x2.ptr.align_offset(std::mem::align_of_val(&*x2)), 0); - } - - #[test] - #[should_panic(expected = "attempted to dereference an ArenaRef after its Arena was cleared")] - fn test_arena_use_after_clear() { - let mut arena = Arena::new(16); - let value = arena.alloc(|| 1u64); - - arena.clear(); - let _read_value = *value; - } - - #[test] - fn test_clear_deferred_while_scope_active() { - struct DropCounter(Rc>); - impl Drop for DropCounter { - fn drop(&mut self) { - self.0.set(self.0.get() + 1); - } - } - - let drops = Rc::new(Cell::new(0)); - let mut arena = Arena::new(1024); - - // Outer draw starts and allocates. - arena.begin_scope(); - let outer = arena.alloc(|| 42u64); - arena.alloc({ - let drops = drops.clone(); - || DropCounter(drops) - }); - - // Nested draw runs to completion and requests a clear. - arena.begin_scope(); - let inner = arena.alloc(|| 7u64); - arena.alloc({ - let drops = drops.clone(); - || DropCounter(drops) - }); - arena.end_scope(); - arena.clear(); - - // The clear must be deferred: the outer draw's allocations are still live. - assert_eq!(*outer, 42); - assert_eq!(*inner, 7); - assert_eq!(drops.get(), 0); - - // Once the outer draw finishes, its clear drops both draws' allocations. - arena.end_scope(); - arena.clear(); - assert_eq!(drops.get(), 2); - } - - #[test] - fn test_clear_without_scope_is_immediate() { - let mut arena = Arena::new(1024); - let value = arena.alloc(|| 1u64); - assert_eq!(*value, 1); - arena.clear(); - assert!(!value.valid.get()); - } - - #[test] - #[should_panic(expected = "Arena::end_scope called without a matching begin_scope")] - fn test_unbalanced_end_scope_panics() { - let mut arena = Arena::new(1024); - arena.begin_scope(); - arena.end_scope(); - arena.end_scope(); - } -} diff --git a/crates/gpui_pre/src/asset_cache.rs b/crates/gpui_pre/src/asset_cache.rs deleted file mode 100644 index bd127154..00000000 --- a/crates/gpui_pre/src/asset_cache.rs +++ /dev/null @@ -1,82 +0,0 @@ -use crate::{App, SharedString, SharedUri}; -use futures::{Future, TryFutureExt}; - -use std::fmt::Debug; -use std::hash::{BuildHasher, Hash}; -use std::marker::PhantomData; -use std::path::{Path, PathBuf}; -use std::sync::Arc; - -/// An enum representing -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub enum Resource { - /// This resource is at a given URI - Uri(SharedUri), - /// This resource is at a given path in the file system - Path(Arc), - /// This resource is embedded in the application binary - Embedded(SharedString), -} - -impl From for Resource { - fn from(value: SharedUri) -> Self { - Self::Uri(value) - } -} - -impl From for Resource { - fn from(value: PathBuf) -> Self { - Self::Path(value.into()) - } -} - -impl From> for Resource { - fn from(value: Arc) -> Self { - Self::Path(value) - } -} - -/// A trait for asynchronous asset loading. -pub trait Asset: 'static { - /// The source of the asset. - type Source: Clone + Hash + Send; - - /// The loaded asset - type Output: Clone + Send; - - /// Load the asset asynchronously - fn load( - source: Self::Source, - cx: &mut App, - ) -> impl Future + Send + 'static; -} - -/// An asset Loader which logs the [`Err`] variant of a [`Result`] during loading -pub enum AssetLogger { - #[doc(hidden)] - _Phantom(PhantomData, &'static dyn crate::seal::Sealed), -} - -impl Asset for AssetLogger -where - T: Asset>, - R: Clone + Send, - E: Clone + Send + Debug, -{ - type Source = T::Source; - - type Output = T::Output; - - fn load( - source: Self::Source, - cx: &mut App, - ) -> impl Future + Send + 'static { - let load = T::load(source, cx); - load.inspect_err(|e| log::error!("Failed to load asset: {:?}", e)) - } -} - -/// Use a quick, non-cryptographically secure hash function to get an identifier from data -pub fn hash(data: &T) -> u64 { - collections::FxBuildHasher.hash_one(data) -} diff --git a/crates/gpui_pre/src/assets.rs b/crates/gpui_pre/src/assets.rs deleted file mode 100644 index cb8b47ed..00000000 --- a/crates/gpui_pre/src/assets.rs +++ /dev/null @@ -1,133 +0,0 @@ -use crate::{DevicePixels, Pixels, Result, SharedString, Size, size}; -use smallvec::SmallVec; - -use image::{Delay, Frame}; -use std::{ - borrow::Cow, - fmt, - hash::Hash, - sync::atomic::{AtomicUsize, Ordering::SeqCst}, -}; - -/// A source of assets for this app to use. -pub trait AssetSource: 'static + Send + Sync { - /// Load the given asset from the source path. - fn load(&self, path: &str) -> Result>>; - - /// List the assets at the given path. - fn list(&self, path: &str) -> Result>; -} - -impl AssetSource for () { - fn load(&self, _path: &str) -> Result>> { - Ok(None) - } - - fn list(&self, _path: &str) -> Result> { - Ok(vec![]) - } -} - -/// A unique identifier for the image cache -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct ImageId(pub usize); - -#[derive(PartialEq, Eq, Hash, Clone)] -#[expect(missing_docs)] -pub struct RenderImageParams { - pub image_id: ImageId, - pub frame_index: usize, -} - -/// A cached and processed image, in BGRA format -pub struct RenderImage { - /// The ID associated with this image - pub id: ImageId, - /// The scale factor of this image on render. - pub(crate) scale_factor: f32, - data: SmallVec<[Frame; 1]>, -} - -impl PartialEq for RenderImage { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl Eq for RenderImage {} - -impl RenderImage { - /// Create a new image from the given data. - pub fn new(data: impl Into>) -> Self { - static NEXT_ID: AtomicUsize = AtomicUsize::new(0); - - Self { - id: ImageId(NEXT_ID.fetch_add(1, SeqCst)), - scale_factor: 1.0, - data: data.into(), - } - } - - /// Convert this image into a byte slice. - pub fn as_bytes(&self, frame_index: usize) -> Option<&[u8]> { - self.data - .get(frame_index) - .map(|frame| frame.buffer().as_raw().as_slice()) - } - - /// Get the size of this image, in pixels. - pub fn size(&self, frame_index: usize) -> Size { - self.data - .get(frame_index) - .map(|frame| { - let (width, height) = frame.buffer().dimensions(); - size(width.into(), height.into()) - }) - .unwrap_or_default() - } - - /// Get the size of this image, in pixels for display, adjusted for the scale factor. - pub(crate) fn render_size(&self, frame_index: usize) -> Size { - self.size(frame_index) - .map(|v| (v.0 as f32 / self.scale_factor).into()) - } - - /// Get the delay of this frame from the previous - pub fn delay(&self, frame_index: usize) -> Delay { - self.data - .get(frame_index) - .map(|frame| frame.delay()) - .unwrap_or(Delay::from_numer_denom_ms(100, 1)) - } - - /// Get the number of frames for this image. - pub fn frame_count(&self) -> usize { - self.data.len() - } -} - -impl fmt::Debug for RenderImage { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ImageData") - .field("id", &self.id) - .field("size", &self.data.first().map(|f| f.buffer().dimensions())) - .finish() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use smallvec::SmallVec; - - #[test] - fn empty_render_image_does_not_panic() { - let image = RenderImage::new(SmallVec::new()); - assert_eq!(image.frame_count(), 0); - assert_eq!(image.size(0), Size::default()); - assert_eq!(image.as_bytes(0), None); - assert_eq!(image.render_size(0), Size::default()); - assert_eq!(image.delay(0), Delay::from_numer_denom_ms(100, 1)); - let _ = format!("{image:?}"); - } -} diff --git a/crates/gpui_pre/src/bounds_tree.rs b/crates/gpui_pre/src/bounds_tree.rs deleted file mode 100644 index e95e3247..00000000 --- a/crates/gpui_pre/src/bounds_tree.rs +++ /dev/null @@ -1,472 +0,0 @@ -use crate::{Bounds, Half}; -use std::{ - cmp, - fmt::Debug, - ops::{Add, Sub}, - ptr::NonNull, -}; - -/// Maximum children per internal node (R-tree style branching factor). -/// Higher values = shorter tree = fewer cache misses, but more work per node. -const MAX_CHILDREN: usize = 12; - -/// A spatial tree optimized for finding maximum ordering among intersecting bounds. -/// -/// This is an R-tree variant specifically designed for the use case of assigning -/// z-order to overlapping UI elements. Key optimizations: -/// - Tracks the leaf with global max ordering for O(1) fast-path queries -/// - Uses higher branching factor (4) for lower tree height -/// - Aggressive pruning during search based on max_order metadata -#[derive(Debug)] -pub(crate) struct BoundsTree -where - U: Clone + Debug + Default + PartialEq, -{ - /// All nodes stored contiguously for cache efficiency. - nodes: Vec>, - /// Index of the root node, if any. - root: Option, - /// Index of the leaf with the highest ordering (for fast-path lookups). - max_leaf: Option, - /// Reusable stack for tree traversal during insertion. - insert_path: Vec, - /// Reusable stack for search operations. - search_stack: Vec>>, -} - -/// A node in the bounds tree. -#[derive(Debug, Clone)] -struct Node -where - U: Clone + Debug + Default + PartialEq, -{ - /// Bounding box containing this node and all descendants. - bounds: Bounds, - /// Maximum ordering value in this subtree. - max_order: u32, - /// Node-specific data. - kind: NodeKind, -} - -#[derive(Debug, Clone)] -enum NodeKind { - /// Leaf node containing actual bounds data. - Leaf { - /// The ordering assigned to this bounds. - order: u32, - }, - /// Internal node with children. - Internal { - /// Indices of child nodes (2 to MAX_CHILDREN). - children: NodeChildren, - }, -} - -/// Fixed-size array for child indices, avoiding heap allocation. -#[derive(Debug, Clone)] -struct NodeChildren { - // Keeps an invariant where the max order child is always at the end - indices: [usize; MAX_CHILDREN], - len: u8, -} - -impl NodeChildren { - fn new() -> Self { - Self { - indices: [0; MAX_CHILDREN], - len: 0, - } - } - - fn push(&mut self, index: usize) { - debug_assert!((self.len as usize) < MAX_CHILDREN); - self.indices[self.len as usize] = index; - self.len += 1; - } - - fn len(&self) -> usize { - self.len as usize - } - - fn as_slice(&self) -> &[usize] { - &self.indices[..self.len as usize] - } -} - -impl BoundsTree -where - U: Clone - + Debug - + PartialEq - + PartialOrd - + Add - + Sub - + Half - + Default, -{ - /// Clears all nodes from the tree. - pub fn clear(&mut self) { - self.nodes.clear(); - self.root = None; - self.max_leaf = None; - self.insert_path.clear(); - self.search_stack.clear(); - } - - /// Inserts bounds into the tree and returns its assigned ordering. - /// - /// The ordering is one greater than the maximum ordering of any - /// existing bounds that intersect with the new bounds. - pub fn insert(&mut self, new_bounds: Bounds) -> u32 { - // Find maximum ordering among intersecting bounds - let max_intersecting = self.find_max_ordering(&new_bounds); - let ordering = max_intersecting + 1; - - // Insert the new leaf - let new_leaf_idx = self.insert_leaf(new_bounds, ordering); - - // Update max_leaf tracking - self.max_leaf = match self.max_leaf { - None => Some(new_leaf_idx), - Some(old_idx) if self.nodes[old_idx].max_order < ordering => Some(new_leaf_idx), - some => some, - }; - - ordering - } - - /// Finds the maximum ordering among all bounds that intersect with the query. - fn find_max_ordering(&mut self, query: &Bounds) -> u32 { - let Some(root_idx) = self.root else { - return 0; - }; - - // Fast path: check if the max-ordering leaf intersects - if let Some(max_idx) = self.max_leaf { - let max_node = &self.nodes[max_idx]; - if query.intersects(&max_node.bounds) { - return max_node.max_order; - } - } - - // Slow path: search the tree - self.search_stack.clear(); - self.search_stack.push(NonNull::from(&self.nodes[root_idx])); - - let mut max_found = 0u32; - - while let Some(node) = self.search_stack.pop() { - // SAFETY: `node` is guaranteed to be valid as the `nodes` stack is unmodified in this function - // and the `search_stack` only contains pointers from this function call. - let node = unsafe { node.as_ref() }; - - // Pruning: skip if this subtree can't improve our result - if node.max_order <= max_found { - continue; - } - - // Spatial pruning: skip if bounds don't intersect - if !query.intersects(&node.bounds) { - continue; - } - - match &node.kind { - NodeKind::Leaf { order } => { - max_found = cmp::max(max_found, *order); - } - NodeKind::Internal { children } => { - // Children are maintained with highest max_order at the end. - // Push in forward order to highest (last) is popped first. - self.search_stack.extend( - children - .as_slice() - .iter() - .map(|&child_idx| &self.nodes[child_idx]) - .filter(|node| node.max_order > max_found) - .map(NonNull::from), - ); - } - } - } - - max_found - } - - /// Inserts a leaf node with the given bounds and ordering. - /// Returns the index of the new leaf. - fn insert_leaf(&mut self, bounds: Bounds, order: u32) -> usize { - let new_leaf_idx = self.nodes.len(); - self.nodes.push(Node { - bounds: bounds.clone(), - max_order: order, - kind: NodeKind::Leaf { order }, - }); - - let Some(root_idx) = self.root else { - // Tree is empty, new leaf becomes root - self.root = Some(new_leaf_idx); - return new_leaf_idx; - }; - - // If root is a leaf, create internal node with both - if matches!(self.nodes[root_idx].kind, NodeKind::Leaf { .. }) { - let root_bounds = self.nodes[root_idx].bounds.clone(); - let root_order = self.nodes[root_idx].max_order; - - let mut children = NodeChildren::new(); - // Max end invariant - if order > root_order { - children.push(root_idx); - children.push(new_leaf_idx); - } else { - children.push(new_leaf_idx); - children.push(root_idx); - } - - let new_root_idx = self.nodes.len(); - self.nodes.push(Node { - bounds: root_bounds.union(&bounds), - max_order: cmp::max(root_order, order), - kind: NodeKind::Internal { children }, - }); - self.root = Some(new_root_idx); - return new_leaf_idx; - } - - // Descend to find the best internal node to insert into - self.insert_path.clear(); - let mut current_idx = root_idx; - - loop { - let current = &self.nodes[current_idx]; - let NodeKind::Internal { children } = ¤t.kind else { - unreachable!("Should only traverse internal nodes"); - }; - - self.insert_path.push(current_idx); - - // Find the best child to descend into - let mut best_child_idx = children.as_slice()[0]; - let mut best_child_pos = 0; - let mut best_cost = bounds - .union(&self.nodes[best_child_idx].bounds) - .half_perimeter(); - - for (pos, &child_idx) in children.as_slice().iter().enumerate().skip(1) { - let cost = bounds.union(&self.nodes[child_idx].bounds).half_perimeter(); - if cost < best_cost { - best_cost = cost; - best_child_idx = child_idx; - best_child_pos = pos; - } - } - - // Check if best child is a leaf or internal - if matches!(self.nodes[best_child_idx].kind, NodeKind::Leaf { .. }) { - // Best child is a leaf. Check if current node has room for another child. - if children.len() < MAX_CHILDREN { - // Add new leaf directly to this node - let node = &mut self.nodes[current_idx]; - - if let NodeKind::Internal { children } = &mut node.kind { - children.push(new_leaf_idx); - // Swap new leaf only if it has the highest max_order - if order <= node.max_order { - let last = children.len() - 1; - children.indices.swap(last - 1, last); - } - } - - node.bounds = node.bounds.union(&bounds); - node.max_order = cmp::max(node.max_order, order); - break; - } else { - // Node is full, create new internal with [best_leaf, new_leaf] - let sibling_bounds = self.nodes[best_child_idx].bounds.clone(); - let sibling_order = self.nodes[best_child_idx].max_order; - - let mut new_children = NodeChildren::new(); - // Max end invariant - if order > sibling_order { - new_children.push(best_child_idx); - new_children.push(new_leaf_idx); - } else { - new_children.push(new_leaf_idx); - new_children.push(best_child_idx); - } - - let new_internal_idx = self.nodes.len(); - let new_internal_max = cmp::max(sibling_order, order); - self.nodes.push(Node { - bounds: sibling_bounds.union(&bounds), - max_order: new_internal_max, - kind: NodeKind::Internal { - children: new_children, - }, - }); - - // Replace the leaf with the new internal in parent - let parent = &mut self.nodes[current_idx]; - if let NodeKind::Internal { children } = &mut parent.kind { - let children_len = children.len(); - - children.indices[best_child_pos] = new_internal_idx; - - // If new internal has highest max_order, swap it to the end - // to maintain sorting invariant - if new_internal_max > parent.max_order { - children.indices.swap(best_child_pos, children_len - 1); - } - } - break; - } - } else { - // Best child is internal, continue descent - current_idx = best_child_idx; - } - } - - // Propagate bounds and max_order updates up the tree - let mut updated_child_idx = None; - for &node_idx in self.insert_path.iter().rev() { - let node = &mut self.nodes[node_idx]; - node.bounds = node.bounds.union(&bounds); - - if node.max_order < order { - node.max_order = order; - - // Swap updated child to end (skip first iteration since the invariant is already handled by previous cases) - if let Some(child_idx) = updated_child_idx { - if let NodeKind::Internal { children } = &mut node.kind { - if let Some(pos) = children.as_slice().iter().position(|&c| c == child_idx) - { - let last = children.len() - 1; - if pos != last { - children.indices.swap(pos, last); - } - } - } - } - } - - updated_child_idx = Some(node_idx); - } - - new_leaf_idx - } -} - -impl Default for BoundsTree -where - U: Clone + Debug + Default + PartialEq, -{ - fn default() -> Self { - BoundsTree { - nodes: Vec::new(), - root: None, - max_leaf: None, - insert_path: Vec::new(), - search_stack: Vec::new(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{Bounds, Point, Size}; - use rand::{Rng, SeedableRng}; - - #[test] - fn test_insert() { - let mut tree = BoundsTree::::default(); - let bounds1 = Bounds { - origin: Point { x: 0.0, y: 0.0 }, - size: Size { - width: 10.0, - height: 10.0, - }, - }; - let bounds2 = Bounds { - origin: Point { x: 5.0, y: 5.0 }, - size: Size { - width: 10.0, - height: 10.0, - }, - }; - let bounds3 = Bounds { - origin: Point { x: 10.0, y: 10.0 }, - size: Size { - width: 10.0, - height: 10.0, - }, - }; - - // Insert the bounds into the tree and verify the order is correct - assert_eq!(tree.insert(bounds1), 1); - assert_eq!(tree.insert(bounds2), 2); - assert_eq!(tree.insert(bounds3), 3); - - // Insert non-overlapping bounds and verify they can reuse orders - let bounds4 = Bounds { - origin: Point { x: 20.0, y: 20.0 }, - size: Size { - width: 10.0, - height: 10.0, - }, - }; - let bounds5 = Bounds { - origin: Point { x: 40.0, y: 40.0 }, - size: Size { - width: 10.0, - height: 10.0, - }, - }; - let bounds6 = Bounds { - origin: Point { x: 25.0, y: 25.0 }, - size: Size { - width: 10.0, - height: 10.0, - }, - }; - assert_eq!(tree.insert(bounds4), 1); // bounds4 does not overlap with bounds1, bounds2, or bounds3 - assert_eq!(tree.insert(bounds5), 1); // bounds5 does not overlap with any other bounds - assert_eq!(tree.insert(bounds6), 2); // bounds6 overlaps with bounds4, so it should have a different order - } - - #[test] - fn test_random_iterations() { - let max_bounds = 100; - for seed in 1..=1000 { - // let seed = 44; - let mut tree = BoundsTree::default(); - let mut rng = rand::rngs::StdRng::seed_from_u64(seed as u64); - let mut expected_quads: Vec<(Bounds, u32)> = Vec::new(); - - // Insert a random number of random AABBs into the tree. - let num_bounds = rng.random_range(1..=max_bounds); - for _ in 0..num_bounds { - let min_x: f32 = rng.random_range(-100.0..100.0); - let min_y: f32 = rng.random_range(-100.0..100.0); - let width: f32 = rng.random_range(0.0..50.0); - let height: f32 = rng.random_range(0.0..50.0); - let bounds = Bounds { - origin: Point { x: min_x, y: min_y }, - size: Size { width, height }, - }; - - let expected_ordering = expected_quads - .iter() - .filter_map(|quad| quad.0.intersects(&bounds).then_some(quad.1)) - .max() - .unwrap_or(0) - + 1; - expected_quads.push((bounds, expected_ordering)); - - // Insert the AABB into the tree and collect intersections. - let actual_ordering = tree.insert(bounds); - assert_eq!(actual_ordering, expected_ordering); - } - } - } -} diff --git a/crates/gpui_pre/src/clip.rs b/crates/gpui_pre/src/clip.rs deleted file mode 100644 index 4bb79d9d..00000000 --- a/crates/gpui_pre/src/clip.rs +++ /dev/null @@ -1,188 +0,0 @@ -//! Rounded clipping keeps each ancestor's geometry separate from culling bounds. - -use crate::{px, Bounds, ContentMask, Corners, Pixels, Point, ScaledPixels}; -use smallvec::SmallVec; -use std::fmt::Debug; - -/// One immutable rounded rectangle in a scene's clip chain. -/// Separate horizontal and vertical radii preserve corners inset by unequal borders. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct RoundedClip { - /// Original geometry, never replaced by an intersection's bounding box. - pub bounds: Bounds

, - /// Horizontal corner radii. - pub radii_x: Corners

, - /// Vertical corner radii. - pub radii_y: Corners

, - /// One-based index of the next clip in the scene; zero ends the chain. - pub parent: u32, - /// Explicit GPU record padding. - pub padding: u32, -} - -impl RoundedClip { - /// Convert logical clip geometry to device coordinates. - pub fn scale(&self, scale: f32) -> RoundedClip { - RoundedClip { - bounds: self.bounds.scale(scale), - radii_x: self.radii_x.scale(scale), - radii_y: self.radii_y.scale(scale), - parent: 0, - padding: 0, - } - } - - /// Exact point membership in the rounded rectangle. - pub fn contains(&self, point: Point) -> bool { - if self.bounds.is_empty() || !self.bounds.contains(&point) { - return false; - } - let x = f32::from(point.x - self.bounds.left()); - let y = f32::from(point.y - self.bounds.top()); - let right = f32::from(self.bounds.right() - point.x); - let bottom = f32::from(self.bounds.bottom() - point.y); - [ - (x, y, self.radii_x.top_left, self.radii_y.top_left), - (right, y, self.radii_x.top_right, self.radii_y.top_right), - ( - right, - bottom, - self.radii_x.bottom_right, - self.radii_y.bottom_right, - ), - ( - x, - bottom, - self.radii_x.bottom_left, - self.radii_y.bottom_left, - ), - ] - .into_iter() - .all(|(x, y, rx, ry)| { - let (rx, ry) = (f32::from(rx), f32::from(ry)); - if rx <= 0.0 || ry <= 0.0 || x >= rx || y >= ry { - return true; - } - let dx = (x - rx) / rx; - let dy = (y - ry) / ry; - dx * dx + dy * dy <= 1.0 - }) - } -} - -/// The exact intersection of rectangular and rounded ancestor clips. -/// -/// `bounds` is only a culling rectangle. Rounded curves keep the bounds and -/// radii of their owning element, even when a narrow descendant intersects them. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct ClipRegion { - /// Rectangular intersection used for culling and layout visibility. - pub bounds: Bounds, - /// Rounded constraints still relevant within `bounds`. - pub rounded_clips: SmallVec<[RoundedClip; 1]>, -} - -impl From> for ClipRegion { - fn from(mask: ContentMask) -> Self { - let radii = mask - .corner_radii - .clamp_radii_for_quad_size(mask.bounds.size); - Self::rounded(mask.bounds, radii, radii) - } -} - -impl ClipRegion { - /// Construct a clip with elliptical corners. The caller normalizes radii - /// against the original element before deriving any border inset. - pub fn rounded( - bounds: Bounds, - radii_x: Corners, - radii_y: Corners, - ) -> Self { - let mut region = Self { - bounds, - rounded_clips: SmallVec::new(), - }; - if radii_x.max() > px(0.) && radii_y.max() > px(0.) && !bounds.is_empty() { - region.rounded_clips.push(RoundedClip { - bounds, - radii_x, - radii_y, - parent: 0, - padding: 0, - }); - } - region - } - - /// Intersect without approximating or relocating either region's curves. - pub fn intersect(&self, other: &Self) -> Self { - let bounds = self.bounds.intersect(&other.bounds); - let mut result = Self { - bounds, - rounded_clips: SmallVec::new(), - }; - if bounds.is_empty() { - return result; - } - for clip in self.rounded_clips.iter().chain(&other.rounded_clips) { - // Logical containment cannot prune a curve: the GPU's conservative - // culling rectangle can expand across its antialiased edge. - if result.rounded_clips.contains(clip) { - continue; - } - result.rounded_clips.push(*clip); - } - result - } - - /// Exact logical point membership, independent of rasterization scale. - pub fn contains(&self, point: Point) -> bool { - !self.bounds.is_empty() - && self.bounds.contains(&point) - && self.rounded_clips.iter().all(|clip| clip.contains(point)) - } -} - -/// Concrete clip record exported to the Metal shader bindings. -#[allow(non_camel_case_types)] -pub type RoundedClip_ScaledPixels = RoundedClip; - -#[cfg(test)] -mod tests { - use super::*; - use crate::point; - - #[test] - fn nested_clips_preserve_both_original_shapes() { - let outer: ClipRegion = ContentMask { - bounds: Bounds::from_corners(point(px(0.), px(0.)), point(px(100.), px(100.))), - corner_radii: Corners::all(px(16.)), - ..Default::default() - } - .into(); - for (left, top, right, bottom) in - [(4., 4., 96., 96.), (0., 0., 10., 100.), (7., 1., 94., 80.)] - { - let inner: ClipRegion = ContentMask { - bounds: Bounds::from_corners( - point(px(left), px(top)), - point(px(right), px(bottom)), - ), - ..Default::default() - } - .into(); - let intersection = outer.intersect(&inner); - for x in 0..100 { - for y in 0..100 { - let point = point(px(x as f32 + 0.5), px(y as f32 + 0.5)); - assert_eq!( - intersection.contains(point), - outer.contains(point) && inner.contains(point) - ); - } - } - } - } -} diff --git a/crates/gpui_pre/src/color.rs b/crates/gpui_pre/src/color.rs deleted file mode 100644 index 3bd893f9..00000000 --- a/crates/gpui_pre/src/color.rs +++ /dev/null @@ -1,1070 +0,0 @@ -use anyhow::{Context as _, bail}; -use schemars::{JsonSchema, json_schema}; -use serde::{ - Deserialize, Deserializer, Serialize, Serializer, - de::{self, Visitor}, -}; -use std::borrow::Cow; -use std::{ - fmt::{self, Display, Formatter}, - hash::{Hash, Hasher}, -}; - -/// Convert an RGB hex color code number to a color type -pub fn rgb(hex: u32) -> Rgba { - let [_, r, g, b] = hex.to_be_bytes().map(|b| (b as f32) / 255.0); - Rgba { r, g, b, a: 1.0 } -} - -/// Convert an RGBA hex color code number to [`Rgba`] -pub fn rgba(hex: u32) -> Rgba { - let [r, g, b, a] = hex.to_be_bytes().map(|b| (b as f32) / 255.0); - Rgba { r, g, b, a } -} - -/// Swap from RGBA with premultiplied alpha to BGRA -pub fn swap_rgba_pa_to_bgra(color: &mut [u8]) { - color.swap(0, 2); - if color[3] > 0 { - let a = color[3] as f32 / 255.; - color[0] = (color[0] as f32 / a) as u8; - color[1] = (color[1] as f32 / a) as u8; - color[2] = (color[2] as f32 / a) as u8; - } -} - -/// An RGBA color -#[derive(PartialEq, Clone, Copy, Default)] -#[repr(C)] -pub struct Rgba { - /// The red component of the color, in the range 0.0 to 1.0 - pub r: f32, - /// The green component of the color, in the range 0.0 to 1.0 - pub g: f32, - /// The blue component of the color, in the range 0.0 to 1.0 - pub b: f32, - /// The alpha component of the color, in the range 0.0 to 1.0 - 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 { - /// Create a new [`Rgba`] color by blending this and another color together - pub fn blend(&self, other: Rgba) -> Self { - if other.a >= 1.0 { - other - } else if other.a <= 0.0 { - *self - } else { - 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, - } - } - } - - /// Returns a new RGBA color with the same red, green and blue channels, but - /// with a new alpha value. - /// - /// Example: - /// ``` - /// use gpui::rgba; - /// let color = rgba(0xFF0000FF); - /// let faded = color.alpha(0.25); - /// assert_eq!(faded.a, 0.25); - /// ``` - /// - /// This will return a red color with 25% opacity. - /// - /// Example: - /// ``` - /// use gpui::rgba; - /// let color = rgba(0x3399FFCC); - /// let transparent = color.alpha(0.0); - /// assert_eq!(transparent.a, 0.0); - /// ``` - /// - /// This will return the same blue color, fully transparent. - pub fn alpha(&self, a: f32) -> Self { - Rgba { - r: self.r, - g: self.g, - b: self.b, - a: a.clamp(0., 1.), - } - } - - /// Returns a new RGBA color with the same red, green, and blue channels, - /// but with the alpha channel multiplied by the given factor. - /// - /// Example: - /// ``` - /// use gpui::rgba; - /// let color = rgba(0xFF0000FF); // Fully opaque red - /// let faded = color.opacity(0.5); - /// assert_eq!(faded.a, 0.5); - /// ``` - /// - /// This will return a red color with 50% opacity. - /// - /// Example: - /// ``` - /// use gpui::rgba; - /// let color = rgba(0x3399FFCC); // A light blue with 80% opacity - /// let faded = color.opacity(0.5); - /// assert!((faded.a - 0.4).abs() < 1e-6); - /// ``` - /// - /// This will return the same blue color scaled down to 40% opacity. - pub fn opacity(&self, factor: f32) -> Self { - Rgba { - r: self.r, - g: self.g, - b: self.b, - a: self.a * factor.clamp(0., 1.), - } - } -} - -impl From 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 Visitor<'_> 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(self, value: &str) -> Result { - Rgba::try_from(value).map_err(E::custom) - } -} - -impl JsonSchema for Rgba { - fn schema_name() -> Cow<'static, str> { - "Rgba".into() - } - - fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema { - json_schema!({ - "type": "string", - "pattern": "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$" - }) - } -} - -impl<'de> Deserialize<'de> for Rgba { - fn deserialize>(deserializer: D) -> Result { - deserializer.deserialize_str(RgbaVisitor) - } -} - -impl Serialize for Rgba { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let r = (self.r * 255.0).round() as u8; - let g = (self.g * 255.0).round() as u8; - let b = (self.b * 255.0).round() as u8; - let a = (self.a * 255.0).round() as u8; - - let s = format!("#{r:02x}{g:02x}{b:02x}{a:02x}"); - serializer.serialize_str(&s) - } -} - -impl From 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: r.clamp(0., 1.), - g: g.clamp(0., 1.), - b: b.clamp(0., 1.), - a: color.a, - } - } -} - -impl TryFrom<&'_ str> for Rgba { - type Error = anyhow::Error; - - fn try_from(value: &'_ str) -> Result { - 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"; - const INVALID_UNICODE: &str = "invalid unicode characters in color"; - - 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.get(0..1).with_context(|| { - format!("{INVALID_UNICODE}: r component of #rgb/#rgba for value: '{value}'") - })?, - 16, - )?; - let g = u8::from_str_radix( - hex.get(1..2).with_context(|| { - format!("{INVALID_UNICODE}: g component of #rgb/#rgba for value: '{value}'") - })?, - 16, - )?; - let b = u8::from_str_radix( - hex.get(2..3).with_context(|| { - format!("{INVALID_UNICODE}: b component of #rgb/#rgba for value: '{value}'") - })?, - 16, - )?; - let a = if hex.len() == RGBA { - u8::from_str_radix( - hex.get(3..4).with_context(|| { - format!("{INVALID_UNICODE}: a component of #rgba for value: '{value}'") - })?, - 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.get(0..2).with_context(|| { - format!( - "{}: r component of #rrggbb/#rrggbbaa for value: '{}'", - INVALID_UNICODE, value - ) - })?, - 16, - )?; - let g = u8::from_str_radix( - hex.get(2..4).with_context(|| { - format!( - "{INVALID_UNICODE}: g component of #rrggbb/#rrggbbaa for value: '{value}'" - ) - })?, - 16, - )?; - let b = u8::from_str_radix( - hex.get(4..6).with_context(|| { - format!( - "{INVALID_UNICODE}: b component of #rrggbb/#rrggbbaa for value: '{value}'" - ) - })?, - 16, - )?; - let a = if hex.len() == RRGGBBAA { - u8::from_str_radix( - hex.get(6..8).with_context(|| { - format!( - "{INVALID_UNICODE}: a component of #rrggbbaa for value: '{value}'" - ) - })?, - 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., - }) - } -} - -/// An HSLA color -#[derive(Default, Copy, Clone, Debug)] -#[repr(C)] -pub struct Hsla { - /// Hue, in a range from 0 to 1 - pub h: f32, - - /// Saturation, in a range from 0 to 1 - pub s: f32, - - /// Lightness, in a range from 0 to 1 - pub l: f32, - - /// Alpha, in a range from 0 to 1 - pub a: f32, -} - -#[cfg(feature = "proptest")] -mod property { - use super::Hsla; - use proptest::prelude::*; - - impl Hsla { - /// Proptest [`Strategy`] that produces opaque colors (i.e. alpha = 1). - /// - /// For truly arbitrary colors, use the [`Arbitrary`] implementation. - pub fn opaque_strategy() -> impl Strategy { - (0.0f32..=1.0, 0.0f32..=1.0, 0.0f32..=1.0).prop_map(|(h, s, l)| Hsla { h, s, l, a: 1. }) - } - } - - impl Arbitrary for Hsla { - type Strategy = BoxedStrategy; - type Parameters = (); - - fn arbitrary_with((): Self::Parameters) -> Self::Strategy { - (0.0f32..=1.0, 0.0f32..=1.0, 0.0f32..=1.0, 0.0f32..=1.0) - .prop_map(|(h, s, l, a)| Hsla { h, s, l, a }) - .boxed() - } - } -} - -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 { - Some(self.cmp(other)) - } -} - -impl Ord for Hsla { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - 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 Eq for Hsla {} - -impl Hash for Hsla { - fn hash(&self, state: &mut H) { - state.write_u32(u32::from_be_bytes(self.h.to_be_bytes())); - state.write_u32(u32::from_be_bytes(self.s.to_be_bytes())); - state.write_u32(u32::from_be_bytes(self.l.to_be_bytes())); - state.write_u32(u32::from_be_bytes(self.a.to_be_bytes())); - } -} - -impl Display for Hsla { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "hsla({:.2}, {:.2}%, {:.2}%, {:.2})", - self.h * 360., - self.s * 100., - self.l * 100., - self.a - ) - } -} - -/// Construct an [`Hsla`] object from plain values -pub const 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.), - } -} - -/// Pure black in [`Hsla`] -pub const fn black() -> Hsla { - Hsla { - h: 0., - s: 0., - l: 0., - a: 1., - } -} - -/// Transparent black in [`Hsla`] -pub const fn transparent_black() -> Hsla { - Hsla { - h: 0., - s: 0., - l: 0., - a: 0., - } -} - -/// Transparent white in [`Hsla`] -pub const fn transparent_white() -> Hsla { - Hsla { - h: 0., - s: 0., - l: 1., - a: 0., - } -} - -/// Opaque grey in [`Hsla`], values will be clamped to the range [0, 1] -pub const fn opaque_grey(lightness: f32, opacity: f32) -> Hsla { - Hsla { - h: 0., - s: 0., - l: lightness.clamp(0., 1.), - a: opacity.clamp(0., 1.), - } -} - -/// Pure white in [`Hsla`] -pub const fn white() -> Hsla { - Hsla { - h: 0., - s: 0., - l: 1., - a: 1., - } -} - -/// The color red in [`Hsla`] -pub const fn red() -> Hsla { - Hsla { - h: 0., - s: 1., - l: 0.5, - a: 1., - } -} - -/// The color blue in [`Hsla`] -pub const fn blue() -> Hsla { - Hsla { - h: 0.6666666667, - s: 1., - l: 0.5, - a: 1., - } -} - -/// The color green in [`Hsla`] -pub const fn green() -> Hsla { - Hsla { - h: 0.3333333333, - s: 1., - l: 0.25, - a: 1., - } -} - -/// The color yellow in [`Hsla`] -pub const fn yellow() -> Hsla { - Hsla { - h: 0.1666666667, - s: 1., - l: 0.5, - a: 1., - } -} - -impl Hsla { - /// Converts this HSLA color to an RGBA color. - pub fn to_rgb(self) -> Rgba { - self.into() - } - - /// The color red - pub const fn red() -> Self { - red() - } - - /// The color green - pub const fn green() -> Self { - green() - } - - /// The color blue - pub const fn blue() -> Self { - blue() - } - - /// The color black - pub const fn black() -> Self { - black() - } - - /// The color white - pub const fn white() -> Self { - white() - } - - /// The color transparent black - pub const fn transparent_black() -> Self { - transparent_black() - } - - /// Returns true if the HSLA color is fully transparent, false otherwise. - pub fn is_transparent(&self) -> bool { - self.a == 0.0 - } - - /// Returns true if the HSLA color is fully opaque, false otherwise. - pub fn is_opaque(&self) -> bool { - self.a == 1.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 its 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 { - self - } else { - let converted_self = Rgba::from(self); - let converted_other = Rgba::from(other); - let blended_rgb = converted_self.blend(converted_other); - Hsla::from(blended_rgb) - } - } - - /// Returns a new HSLA color with the same hue, and lightness, but with no saturation. - pub fn grayscale(&self) -> Self { - Hsla { - h: self.h, - s: 0., - l: self.l, - a: self.a, - } - } - - /// 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.); - } - - /// Multiplies the alpha value of the color by a given factor - /// and returns a new HSLA color. - /// - /// Useful for transforming colors with dynamic opacity, - /// like a color from an external source. - /// - /// Example: - /// ``` - /// let color = gpui::red(); - /// let faded_color = color.opacity(0.5); - /// assert_eq!(faded_color.a, 0.5); - /// ``` - /// - /// This will return a red color with half the opacity. - /// - /// Example: - /// ``` - /// use gpui::hsla; - /// let color = hsla(0.7, 1.0, 0.5, 0.7); // A saturated blue - /// let faded_color = color.opacity(0.16); - /// assert!((faded_color.a - 0.112).abs() < 1e-6); - /// ``` - /// - /// This will return a blue color with around ~10% opacity, - /// suitable for an element's hover or selected state. - /// - pub fn opacity(&self, factor: f32) -> Self { - Hsla { - h: self.h, - s: self.s, - l: self.l, - a: self.a * factor.clamp(0., 1.), - } - } - - /// Returns a new HSLA color with the same hue, saturation, - /// and lightness, but with a new alpha value. - /// - /// Example: - /// ``` - /// let color = gpui::red(); - /// let red_color = color.alpha(0.25); - /// assert_eq!(red_color.a, 0.25); - /// ``` - /// - /// This will return a red color with 25% opacity. - /// - /// Example: - /// ``` - /// use gpui::hsla; - /// let color = hsla(0.7, 1.0, 0.5, 0.7); // A saturated blue - /// let faded_color = color.alpha(0.25); - /// assert_eq!(faded_color.a, 0.25); - /// ``` - /// - /// This will return a blue color with 25% opacity. - pub fn alpha(&self, a: f32) -> Self { - Hsla { - h: self.h, - s: self.s, - l: self.l, - a: a.clamp(0., 1.), - } - } -} - -impl From 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 JsonSchema for Hsla { - fn schema_name() -> Cow<'static, str> { - Rgba::schema_name() - } - - fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema { - Rgba::json_schema(generator) - } -} - -impl Serialize for Hsla { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - Rgba::from(*self).serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for Hsla { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Ok(Rgba::deserialize(deserializer)?.into()) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub(crate) enum BackgroundTag { - Solid = 0, - LinearGradient = 1, - PatternSlash = 2, - Checkerboard = 3, -} - -/// A color space for color interpolation. -/// -/// References: -/// - -/// - -#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub enum ColorSpace { - #[default] - /// The sRGB color space. - Srgb = 0, - /// The Oklab color space. - Oklab = 1, -} - -impl Display for ColorSpace { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - ColorSpace::Srgb => write!(f, "sRGB"), - ColorSpace::Oklab => write!(f, "Oklab"), - } - } -} - -/// A background color, which can be either a solid color or a linear gradient. -#[derive(Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub struct Background { - pub(crate) tag: BackgroundTag, - pub(crate) color_space: ColorSpace, - pub(crate) solid: Hsla, - pub(crate) gradient_angle_or_pattern_height: f32, - pub(crate) colors: [LinearColorStop; 2], - /// Padding for alignment for repr(C) layout. - pad: u32, -} - -impl std::fmt::Debug for Background { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self.tag { - BackgroundTag::Solid => write!(f, "Solid({:?})", self.solid), - BackgroundTag::LinearGradient => write!( - f, - "LinearGradient({}, {:?}, {:?})", - self.gradient_angle_or_pattern_height, self.colors[0], self.colors[1] - ), - BackgroundTag::PatternSlash => write!( - f, - "PatternSlash({:?}, {})", - self.solid, self.gradient_angle_or_pattern_height - ), - BackgroundTag::Checkerboard => write!( - f, - "Checkerboard({:?}, {})", - self.solid, self.gradient_angle_or_pattern_height - ), - } - } -} - -impl Eq for Background {} -impl Default for Background { - fn default() -> Self { - Self { - tag: BackgroundTag::Solid, - solid: Hsla::default(), - color_space: ColorSpace::default(), - gradient_angle_or_pattern_height: 0.0, - colors: [LinearColorStop::default(), LinearColorStop::default()], - pad: 0, - } - } -} - -/// Creates a hash pattern background -pub fn pattern_slash(color: impl Into, width: f32, interval: f32) -> Background { - let width_scaled = (width * 255.0) as u32; - let interval_scaled = (interval * 255.0) as u32; - let height = ((width_scaled * 0xFFFF) + interval_scaled) as f32; - - Background { - tag: BackgroundTag::PatternSlash, - solid: color.into(), - gradient_angle_or_pattern_height: height, - ..Default::default() - } -} - -/// Creates a checkerboard pattern background -pub fn checkerboard(color: impl Into, size: f32) -> Background { - Background { - tag: BackgroundTag::Checkerboard, - solid: color.into(), - gradient_angle_or_pattern_height: size, - ..Default::default() - } -} - -/// Creates a solid background color. -pub fn solid_background(color: impl Into) -> Background { - Background { - solid: color.into(), - ..Default::default() - } -} - -/// Creates a LinearGradient background color. -/// -/// The gradient line's angle of direction. A value of `0.` is equivalent to top; increasing values rotate clockwise from there. -/// -/// The `angle` is in degrees value in the range 0.0 to 360.0. -/// -/// -pub fn linear_gradient( - angle: f32, - from: impl Into, - to: impl Into, -) -> Background { - Background { - tag: BackgroundTag::LinearGradient, - gradient_angle_or_pattern_height: angle, - colors: [from.into(), to.into()], - ..Default::default() - } -} - -/// A color stop in a linear gradient. -/// -/// -#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub struct LinearColorStop { - /// The color of the color stop. - pub color: Hsla, - /// The percentage of the gradient, in the range 0.0 to 1.0. - pub percentage: f32, -} - -/// Creates a new linear color stop. -/// -/// The percentage of the gradient, in the range 0.0 to 1.0. -pub fn linear_color_stop(color: impl Into, percentage: f32) -> LinearColorStop { - LinearColorStop { - color: color.into(), - percentage, - } -} - -impl LinearColorStop { - /// Returns a new color stop with the same color, but with a modified alpha value. - pub fn opacity(&self, factor: f32) -> Self { - Self { - percentage: self.percentage, - color: self.color.opacity(factor), - } - } -} - -impl Background { - /// Returns the solid color if this is a solid background, None otherwise. - pub fn as_solid(&self) -> Option { - if self.tag == BackgroundTag::Solid { - Some(self.solid) - } else { - None - } - } - - /// Use specified color space for color interpolation. - /// - /// - pub fn color_space(mut self, color_space: ColorSpace) -> Self { - self.color_space = color_space; - self - } - - /// Returns a new background color with the same hue, saturation, and lightness, but with a modified alpha value. - pub fn opacity(&self, factor: f32) -> Self { - let mut background = *self; - background.solid = background.solid.opacity(factor); - background.colors = [ - self.colors[0].opacity(factor), - self.colors[1].opacity(factor), - ]; - background - } - - /// Returns whether the background color is transparent. - pub fn is_transparent(&self) -> bool { - match self.tag { - BackgroundTag::Solid => self.solid.is_transparent(), - BackgroundTag::LinearGradient => self.colors.iter().all(|c| c.color.is_transparent()), - BackgroundTag::PatternSlash => self.solid.is_transparent(), - BackgroundTag::Checkerboard => self.solid.is_transparent(), - } - } -} - -impl From for Background { - fn from(value: Hsla) -> Self { - Background { - tag: BackgroundTag::Solid, - solid: value, - ..Default::default() - } - } -} - -impl From for Background { - fn from(value: Rgba) -> Self { - Background { - tag: BackgroundTag::Solid, - solid: Hsla::from(value), - ..Default::default() - } - } -} - -#[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)) - } - - #[test] - fn test_background_solid() { - let color = Hsla::from(rgba(0xff0099ff)); - let mut background = Background::from(color); - assert_eq!(background.tag, BackgroundTag::Solid); - assert_eq!(background.solid, color); - - assert_eq!(background.opacity(0.5).solid, color.opacity(0.5)); - assert!(!background.is_transparent()); - background.solid = hsla(0.0, 0.0, 0.0, 0.0); - assert!(background.is_transparent()); - } - - #[test] - fn test_background_linear_gradient() { - let from = linear_color_stop(rgba(0xff0099ff), 0.0); - let to = linear_color_stop(rgba(0x00ff99ff), 1.0); - let background = linear_gradient(90.0, from, to); - assert_eq!(background.tag, BackgroundTag::LinearGradient); - assert_eq!(background.colors[0], from); - assert_eq!(background.colors[1], to); - - assert_eq!(background.opacity(0.5).colors[0], from.opacity(0.5)); - assert_eq!(background.opacity(0.5).colors[1], to.opacity(0.5)); - assert!(!background.is_transparent()); - assert!(background.opacity(0.0).is_transparent()); - } - - #[test] - fn test_rgba_alpha() { - let color = Rgba { - r: 0.2, - g: 0.6, - b: 1.0, - a: 0.8, - }; - - assert_eq!(color.alpha(0.25).a, 0.25); - assert_eq!(color.alpha(1.5).a, 1.0); - } - - #[test] - fn test_rgba_opacity() { - let color = Rgba { - r: 0.2, - g: 0.6, - b: 1.0, - a: 0.8, - }; - assert!((color.opacity(0.5).a - 0.4).abs() < 1e-6); - assert_eq!(color.opacity(2.0).a, 0.8); - } -} diff --git a/crates/gpui_pre/src/colors.rs b/crates/gpui_pre/src/colors.rs deleted file mode 100644 index ef11ef57..00000000 --- a/crates/gpui_pre/src/colors.rs +++ /dev/null @@ -1,122 +0,0 @@ -use crate::{App, Global, Rgba, Window, WindowAppearance, rgb}; -use std::ops::Deref; -use std::sync::Arc; - -/// The default set of colors for gpui. -/// -/// These are used for styling base components, examples and more. -#[derive(Clone, Debug)] -pub struct Colors { - /// Text color - pub text: Rgba, - /// Selected text color - pub selected_text: Rgba, - /// Background color - pub background: Rgba, - /// Disabled color - pub disabled: Rgba, - /// Selected color - pub selected: Rgba, - /// Border color - pub border: Rgba, - /// Separator color - pub separator: Rgba, - /// Container color - pub container: Rgba, -} - -impl Default for Colors { - fn default() -> Self { - Self::light() - } -} - -impl Colors { - /// Returns the default colors for the given window appearance. - pub fn for_appearance(window: &Window) -> Self { - match window.appearance() { - WindowAppearance::Light | WindowAppearance::VibrantLight => Self::light(), - WindowAppearance::Dark | WindowAppearance::VibrantDark => Self::dark(), - } - } - - /// Returns the default dark colors. - pub fn dark() -> Self { - Self { - text: rgb(0xffffff), - selected_text: rgb(0xffffff), - disabled: rgb(0x565656), - selected: rgb(0x2457ca), - background: rgb(0x222222), - border: rgb(0x000000), - separator: rgb(0xd9d9d9), - container: rgb(0x262626), - } - } - - /// Returns the default light colors. - pub fn light() -> Self { - Self { - text: rgb(0x252525), - selected_text: rgb(0xffffff), - background: rgb(0xffffff), - disabled: rgb(0xb0b0b0), - selected: rgb(0x2a63d9), - border: rgb(0xd9d9d9), - separator: rgb(0xe6e6e6), - container: rgb(0xf4f5f5), - } - } - - /// Get [Colors] from the global state - pub fn get_global(cx: &App) -> &Arc { - &cx.global::().0 - } -} - -/// Get [Colors] from the global state -#[derive(Clone, Debug)] -pub struct GlobalColors(pub Arc); - -impl Deref for GlobalColors { - type Target = Arc; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Global for GlobalColors {} - -/// Implement this trait to allow global [Colors] access via `cx.default_colors()`. -pub trait DefaultColors { - /// Returns the default [`Colors`] - fn default_colors(&self) -> &Arc; -} - -impl DefaultColors for App { - fn default_colors(&self) -> &Arc { - &self.global::().0 - } -} - -/// The appearance of the base GPUI colors, used to style GPUI elements -/// -/// Varies based on the system's current [`WindowAppearance`]. -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -pub enum DefaultAppearance { - /// Use the set of colors for light appearances. - #[default] - Light, - /// Use the set of colors for dark appearances. - Dark, -} - -impl From for DefaultAppearance { - fn from(appearance: WindowAppearance) -> Self { - match appearance { - WindowAppearance::Light | WindowAppearance::VibrantLight => Self::Light, - WindowAppearance::Dark | WindowAppearance::VibrantDark => Self::Dark, - } - } -} diff --git a/crates/gpui_pre/src/debug_overlay.rs b/crates/gpui_pre/src/debug_overlay.rs deleted file mode 100644 index 9157bcf0..00000000 --- a/crates/gpui_pre/src/debug_overlay.rs +++ /dev/null @@ -1,429 +0,0 @@ -//! A developer overlay that paints frame-time statistics directly into the -//! scene, bypassing layout, text, and view invalidation entirely (to avoid -//! infinitely triggering new frames). - -use crate::{ - BorderStyle, Bounds, ContentMask, Corners, Edges, Hsla, Pixels, Quad, ScaledPixels, Scene, - Size, point, rgba, size, transparent_black, -}; -use std::{collections::VecDeque, time::Duration}; - -#[allow(missing_docs)] -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub enum DebugFrameOverlayMode { - #[default] - Hidden, - Minimal, - Full, -} - -impl DebugFrameOverlayMode { - /// Returns the next mode in the Hidden → FrameTime → Detailed cycle. - pub fn next(self) -> Self { - match self { - Self::Hidden => Self::Minimal, - Self::Minimal => Self::Full, - Self::Full => Self::Hidden, - } - } -} - -/// The number of most recent draw durations retained for percentile statistics. -const MAX_SAMPLES: usize = 1000; - -const GLYPH_WIDTH: usize = 5; -const GLYPH_HEIGHT: usize = 7; -/// Glyph advance and line advance, in font cells. -const CHAR_ADVANCE: f32 = (GLYPH_WIDTH + 1) as f32; -const LINE_ADVANCE: f32 = (GLYPH_HEIGHT + 2) as f32; -/// Padding between the panel edge and the text, in font cells. -const PANEL_PADDING: f32 = 2.0; -/// Margin between the panel and the window corner, in font cells. -const PANEL_MARGIN: f32 = 4.0; -/// Side of one square font cell, in logical pixels. -const CELL_SIZE: f32 = 2.0; - -fn text_color() -> Hsla { - rgba(0x33ff33ff).into() -} - -fn panel_color() -> Hsla { - rgba(0x000000aa).into() -} - -pub(crate) struct DebugFrameOverlay { - mode: DebugFrameOverlayMode, - draw_durations: VecDeque, - total_frame_count: u64, -} - -impl DebugFrameOverlay { - pub(crate) fn new() -> Self { - Self { - mode: DebugFrameOverlayMode::default(), - draw_durations: VecDeque::new(), - total_frame_count: 0, - } - } - - pub(crate) fn mode(&self) -> DebugFrameOverlayMode { - self.mode - } - - pub(crate) fn set_mode(&mut self, mode: DebugFrameOverlayMode) { - self.mode = mode; - } - - /// Clears the draw-duration samples, restarting the percentile window. - /// The total frame count is left untouched. - pub(crate) fn reset_stats(&mut self) { - self.draw_durations.clear(); - } - - pub(crate) fn is_enabled(&self) -> bool { - self.mode != DebugFrameOverlayMode::Hidden - } - - pub(crate) fn record_frame(&mut self, draw_duration: Duration) { - self.total_frame_count += 1; - if self.draw_durations.len() >= MAX_SAMPLES { - self.draw_durations.pop_front(); - } - self.draw_durations.push_back(draw_duration); - } - - pub(crate) fn paint(&self, scene: &mut Scene, viewport_size: Size, scale_factor: f32) { - if !self.is_enabled() { - return; - } - - let lines = self.lines(); - let max_line_chars = lines.iter().map(|line| line.len()).max().unwrap_or(0); - // Ensure at least one physical pixel per cell so the text stays legible - // at fractional downscale factors. - let cell = (CELL_SIZE * scale_factor).max(1.0); - - let panel_width = cell * (max_line_chars as f32 * CHAR_ADVANCE + 2.0 * PANEL_PADDING); - let panel_height = cell * (lines.len() as f32 * LINE_ADVANCE + 2.0 * PANEL_PADDING); - let viewport = viewport_size.scale(scale_factor); - let panel_left = viewport.width.0 - panel_width - cell * PANEL_MARGIN; - let panel_top = cell * PANEL_MARGIN; - - let content_mask = ContentMask { - bounds: Bounds { - origin: point(ScaledPixels(0.), ScaledPixels(0.)), - size: viewport, - }, - ..Default::default() - }; - - scene.insert_primitive(solid_quad( - scaled_bounds(panel_left, panel_top, panel_width, panel_height), - &content_mask, - panel_color(), - )); - - let text_color = text_color(); - for (line_index, line) in lines.iter().enumerate() { - let line_top = panel_top + cell * (PANEL_PADDING + line_index as f32 * LINE_ADVANCE); - for (char_index, character) in line.chars().enumerate() { - let Some(rows) = glyph(character) else { - continue; - }; - let glyph_left = - panel_left + cell * (PANEL_PADDING + char_index as f32 * CHAR_ADVANCE); - for (row_index, row) in rows.iter().enumerate() { - let row_top = line_top + cell * row_index as f32; - // Merge horizontal runs of lit cells into single quads. - let mut column = 0; - while column < GLYPH_WIDTH { - if row & (1 << (GLYPH_WIDTH - 1 - column)) == 0 { - column += 1; - continue; - } - let run_start = column; - while column < GLYPH_WIDTH && row & (1 << (GLYPH_WIDTH - 1 - column)) != 0 { - column += 1; - } - scene.insert_primitive(solid_quad( - scaled_bounds( - glyph_left + cell * run_start as f32, - row_top, - cell * (column - run_start) as f32, - cell, - ), - &content_mask, - text_color, - )); - } - } - } - } - } - - fn lines(&self) -> Vec { - let current = self.draw_durations.back().copied(); - match self.mode { - DebugFrameOverlayMode::Hidden => Vec::new(), - DebugFrameOverlayMode::Minimal => vec![format_ms(current)], - DebugFrameOverlayMode::Full => { - let mut sorted: Vec = self.draw_durations.iter().copied().collect(); - sorted.sort_unstable(); - let percentile = |numerator: usize| { - (!sorted.is_empty()).then(|| sorted[(sorted.len() - 1) * numerator / 100]) - }; - // Past five digits the count would break the column - // alignment, so it saturates instead. - let frame_count = if self.total_frame_count > 99_999 { - "LOTS".to_string() - } else { - self.total_frame_count.to_string() - }; - // Labels are padded to a uniform width so the fixed-width - // durations start in the same column on every line. - vec![ - format!("CUR {}", format_ms(current)), - format!("1% {}", format_ms(percentile(99))), - format!("10% {}", format_ms(percentile(90))), - format!("MAX {}", format_ms(sorted.last().copied())), - format!("FRAMES {frame_count:>5}"), - ] - } - } - } -} - -/// Formats as `abc.d MS`, right-aligned in room for three integer digits and -/// one decimal (padded with spaces, not zeroes), so stacked readouts align. -fn format_ms(duration: Option) -> String { - match duration { - Some(duration) => { - let ms = duration.as_secs_f32() * 1000.0; - format!("{ms:>5.1} MS") - } - None => " -- MS".into(), - } -} - -fn scaled_bounds(left: f32, top: f32, width: f32, height: f32) -> Bounds { - Bounds { - origin: point(ScaledPixels(left), ScaledPixels(top)), - size: size(ScaledPixels(width), ScaledPixels(height)), - } -} - -fn solid_quad( - bounds: Bounds, - content_mask: &ContentMask, - color: Hsla, -) -> Quad { - Quad { - order: 0, - border_style: BorderStyle::Solid, - bounds, - content_mask: *content_mask, - background: color.into(), - border_color: transparent_black(), - corner_radii: Corners::default(), - border_widths: Edges::default(), - } -} - -/// Returns the 5x7 bitmap for the given character, one `u8` of column bits per -/// row with the most significant of the 5 bits leftmost. Only the characters -/// used by the overlay's readouts are defined. -fn glyph(character: char) -> Option<[u8; GLYPH_HEIGHT]> { - Some(match character { - '0' => [ - 0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110, - ], - '1' => [ - 0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110, - ], - '2' => [ - 0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b01000, 0b11111, - ], - '3' => [ - 0b11111, 0b00010, 0b00100, 0b00010, 0b00001, 0b10001, 0b01110, - ], - '4' => [ - 0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010, - ], - '5' => [ - 0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110, - ], - '6' => [ - 0b00110, 0b01000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110, - ], - '7' => [ - 0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000, - ], - '8' => [ - 0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110, - ], - '9' => [ - 0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00010, 0b01100, - ], - '.' => [ - 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b01100, 0b01100, - ], - '-' => [ - 0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000, - ], - '%' => [ - 0b11001, 0b11001, 0b00010, 0b00100, 0b01000, 0b10011, 0b10011, - ], - 'A' => [ - 0b01110, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001, - ], - 'C' => [ - 0b01110, 0b10001, 0b10000, 0b10000, 0b10000, 0b10001, 0b01110, - ], - 'E' => [ - 0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111, - ], - 'F' => [ - 0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000, - ], - 'L' => [ - 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111, - ], - 'M' => [ - 0b10001, 0b11011, 0b10101, 0b10101, 0b10001, 0b10001, 0b10001, - ], - 'N' => [ - 0b10001, 0b11001, 0b10101, 0b10011, 0b10001, 0b10001, 0b10001, - ], - 'O' => [ - 0b01110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110, - ], - 'R' => [ - 0b11110, 0b10001, 0b10001, 0b11110, 0b10100, 0b10010, 0b10001, - ], - 'S' => [ - 0b01111, 0b10000, 0b10000, 0b01110, 0b00001, 0b00001, 0b11110, - ], - 'T' => [ - 0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, - ], - 'U' => [ - 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110, - ], - 'X' => [ - 0b10001, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001, 0b10001, - ], - _ => return None, - }) -} - -#[cfg(test)] -mod tests { - use super::*; - - /// Every character the readouts can produce must have a glyph, or it - /// would silently render as blank space. - #[test] - fn all_rendered_characters_have_glyphs() { - let mut overlay = DebugFrameOverlay::new(); - overlay.set_mode(DebugFrameOverlayMode::Full); - - let mut lines = Vec::new(); - for duration in [ - Duration::ZERO, - Duration::from_micros(1), - Duration::from_micros(8_333), - Duration::from_millis(123), - Duration::from_secs(2), - ] { - overlay.record_frame(duration); - lines.extend(overlay.lines()); - } - // Counts beyond five digits render as "LOTS". - overlay.total_frame_count = 100_000; - lines.extend(overlay.lines()); - - // An enabled overlay with no samples yet renders placeholders. - let mut empty = DebugFrameOverlay::new(); - empty.set_mode(DebugFrameOverlayMode::Full); - lines.extend(empty.lines()); - - for line in lines { - for character in line.chars() { - assert!( - character == ' ' || glyph(character).is_some(), - "no glyph for {character:?} in line {line:?}" - ); - } - } - } - - #[test] - fn percentile_lows_are_reported_as_times() { - let mut overlay = DebugFrameOverlay::new(); - overlay.set_mode(DebugFrameOverlayMode::Full); - for milliseconds in 1..=100 { - overlay.record_frame(Duration::from_millis(milliseconds)); - } - let lines = overlay.lines(); - assert_eq!(lines[0], "CUR 100.0 MS"); - assert_eq!(lines[1], "1% 99.0 MS"); - assert_eq!(lines[2], "10% 90.0 MS"); - assert_eq!(lines[3], "MAX 100.0 MS"); - assert_eq!(lines[4], "FRAMES 100"); - } - - #[test] - fn reset_clears_durations_but_keeps_frame_count() { - let mut overlay = DebugFrameOverlay::new(); - overlay.set_mode(DebugFrameOverlayMode::Full); - for _ in 0..10 { - overlay.record_frame(Duration::from_millis(10)); - } - overlay.reset_stats(); - let lines = overlay.lines(); - assert_eq!(lines[0], "CUR -- MS"); - assert_eq!(lines[3], "MAX -- MS"); - assert_eq!(lines[4], "FRAMES 10"); - overlay.record_frame(Duration::from_millis(20)); - let lines = overlay.lines(); - assert_eq!(lines[0], "CUR 20.0 MS"); - assert_eq!(lines[4], "FRAMES 11"); - } - - #[test] - fn frame_count_accumulates_across_mode_changes() { - let mut overlay = DebugFrameOverlay::new(); - for _ in 0..3 { - overlay.record_frame(Duration::from_millis(10)); - } - overlay.set_mode(DebugFrameOverlayMode::Minimal); - assert_eq!(overlay.lines(), vec![" 10.0 MS".to_string()]); - overlay.set_mode(DebugFrameOverlayMode::Full); - overlay.record_frame(Duration::from_millis(10)); - assert_eq!(overlay.lines()[4], "FRAMES 4"); - overlay.set_mode(DebugFrameOverlayMode::Hidden); - overlay.record_frame(Duration::from_millis(10)); - overlay.set_mode(DebugFrameOverlayMode::Full); - assert_eq!(overlay.lines()[4], "FRAMES 5"); - } - - #[test] - fn frame_count_is_right_aligned_and_saturates() { - let mut overlay = DebugFrameOverlay::new(); - overlay.set_mode(DebugFrameOverlayMode::Full); - overlay.record_frame(Duration::from_millis(10)); - assert_eq!(overlay.lines()[4], "FRAMES 1"); - overlay.total_frame_count = 99_999; - assert_eq!(overlay.lines()[4], "FRAMES 99999"); - overlay.total_frame_count = 100_000; - assert_eq!(overlay.lines()[4], "FRAMES LOTS"); - } - - #[test] - fn toggling_on_shows_previous_frame_immediately() { - let mut overlay = DebugFrameOverlay::new(); - overlay.record_frame(Duration::from_millis(10)); - overlay.set_mode(DebugFrameOverlayMode::Minimal); - assert_eq!(overlay.lines(), vec![" 10.0 MS".to_string()]); - } -} diff --git a/crates/gpui_pre/src/element.rs b/crates/gpui_pre/src/element.rs deleted file mode 100644 index c817492b..00000000 --- a/crates/gpui_pre/src/element.rs +++ /dev/null @@ -1,792 +0,0 @@ -//! Elements are the workhorses of GPUI. They are responsible for laying out and painting all of -//! the contents of a window. Elements form a tree and are laid out according to the web layout -//! standards as implemented by [taffy](https://github.com/DioxusLabs/taffy). Most of the time, -//! you won't need to interact with this module or these APIs directly. Elements provide their -//! own APIs and GPUI, or other element implementation, uses the APIs in this module to convert -//! that element tree into the pixels you see on the screen. -//! -//! # Element Basics -//! -//! Elements are constructed by calling [`Render::render()`] on the root view of the window, -//! which recursively constructs the element tree from the current state of the application,. -//! These elements are then laid out by Taffy, and painted to the screen according to their own -//! implementation of [`Element::paint()`]. Before the start of the next frame, the entire element -//! tree and any callbacks they have registered with GPUI are dropped and the process repeats. -//! -//! But some state is too simple and voluminous to store in every view that needs it, e.g. -//! whether a hover has been started or not. For this, GPUI provides the [`Element::PrepaintState`], associated type. -//! -//! # Implementing your own elements -//! -//! Elements are intended to be the low level, imperative API to GPUI. They are responsible for upholding, -//! or breaking, GPUI's features as they deem necessary. As an example, most GPUI elements are expected -//! to stay in the bounds that their parent element gives them. But with [`Window::with_content_mask`], -//! you can ignore this restriction and paint anywhere inside of the window's bounds. This is useful for overlays -//! and popups and anything else that shows up 'on top' of other elements. -//! With great power, comes great responsibility. -//! -//! However, most of the time, you won't need to implement your own elements. GPUI provides a number of -//! elements that should cover most common use cases out of the box and it's recommended that you use those -//! to construct `components`, using the [`RenderOnce`] trait and the `#[derive(IntoElement)]` macro. Only implement -//! elements when you need to take manual control of the layout and painting process, such as when using -//! your own custom layout algorithm or rendering a code editor. - -use crate::{ - A11ySubtreeBuilder, App, ArenaBox, AvailableSpace, Bounds, Context, DispatchNodeId, ElementId, - FocusHandle, InspectorElementId, LayoutId, Pixels, Point, Size, Style, Window, - util::FluentBuilder, window::with_element_arena, -}; -use derive_more::{Deref, DerefMut}; -use std::{ - any::Any, - fmt::{self, Debug, Display}, - mem, panic, - sync::Arc, -}; - -/// Implemented by types that participate in laying out and painting the contents of a window. -/// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy. -/// You can create custom elements by implementing this trait, see the module-level documentation -/// for more details. -pub trait Element: 'static + IntoElement { - /// The type of state returned from [`Element::request_layout`]. A mutable reference to this state is subsequently - /// provided to [`Element::prepaint`] and [`Element::paint`]. - type RequestLayoutState: 'static; - - /// The type of state returned from [`Element::prepaint`]. A mutable reference to this state is subsequently - /// provided to [`Element::paint`]. - type PrepaintState: 'static; - - /// If this element has a unique identifier, return it here. This is used to track elements across frames, and - /// will cause a GlobalElementId to be passed to the request_layout, prepaint, and paint methods. - /// - /// The global id can in turn be used to access state that's connected to an element with the same id across - /// frames. This id must be unique among children of the first containing element with an id. - fn id(&self) -> Option; - - /// Source location where this element was constructed, used to disambiguate elements in the - /// inspector and navigate to their source code. - fn source_location(&self) -> Option<&'static panic::Location<'static>>; - - /// Before an element can be painted, we need to know where it's going to be and how big it is. - /// Use this method to request a layout from Taffy and initialize the element's state. - fn request_layout( - &mut self, - id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState); - - /// After laying out an element, we need to commit its bounds to the current frame for hitbox - /// purposes. The state argument is the same state that was returned from [`Element::request_layout()`]. - fn prepaint( - &mut self, - id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - request_layout: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Self::PrepaintState; - - /// Once layout has been completed, this method will be called to paint the element to the screen. - /// The state argument is the same state that was returned from [`Element::request_layout()`]. - fn paint( - &mut self, - id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - request_layout: &mut Self::RequestLayoutState, - prepaint: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ); - - /// Returns the accessible role for this element, if any. - /// Elements that return `None` are not included in the accessibility tree. - /// - /// Note: inclusion in accessibility tree requires non-`None` [`id`][Element::id]. - /// - /// See the [accessibility guide](crate::_accessibility) for an overview. - fn a11y_role(&self) -> Option { - None - } - - /// Write accessibility properties to the given node. - /// Called only when `a11y_role()` returns `Some`. - /// - /// See the [accessibility guide](crate::_accessibility) for an overview. - fn write_a11y_info(&self, _node: &mut accesskit::Node) {} - - /// Add synthetic child nodes to an [`Element`] that has an - /// [`.id()`][Element::id] and a [`.role()`][Element::a11y_role]. - /// - /// Some elements may want to inject accessibility nodes that do not - /// correspond to any GPUI element. For example, a custom text field element - /// may want to inject synthetic child nodes for the text content. - /// - /// See [Synthetic children](crate::_accessibility#synthetic-children) in - /// the accessibility guide for more detail. - fn a11y_synthetic_children( - &mut self, - _prepaint: &mut Self::PrepaintState, - _builder: &mut A11ySubtreeBuilder, - ) { - } - - /// Convert this element into a dynamically-typed [`AnyElement`]. - fn into_any(self) -> AnyElement { - AnyElement::new(self) - } -} - -/// Implemented by any type that can be converted into an element. -pub trait IntoElement: Sized { - /// The specific type of element into which the implementing type is converted. - /// Useful for converting other types into elements automatically, like Strings - type Element: Element; - - /// Convert self into a type that implements [`Element`]. - fn into_element(self) -> Self::Element; - - /// Convert self into a dynamically-typed [`AnyElement`]. - fn into_any_element(self) -> AnyElement { - self.into_element().into_any() - } -} - -impl FluentBuilder for T {} - -/// An object that can be drawn to the screen. This is the trait that distinguishes "views" from -/// other entities. Views are `Entity`'s which `impl Render` and drawn to the screen. -pub trait Render: 'static + Sized { - /// Render this view into an element tree. - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement; -} - -impl Render for Empty { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - Empty - } -} - -/// You can derive [`IntoElement`] on any type that implements this trait. -/// It is used to construct reusable `components` out of plain data. Think of -/// components as a recipe for a certain pattern of elements. RenderOnce allows -/// you to invoke this pattern, without breaking the fluent builder pattern of -/// the element APIs. -pub trait RenderOnce: 'static { - /// Render this component into an element tree. Note that this method - /// takes ownership of self, as compared to [`Render::render()`] method - /// which takes a mutable reference. - fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement; -} - -/// This is a helper trait to provide a uniform interface for constructing elements that -/// can accept any number of any kind of child elements -pub trait ParentElement { - /// Extend this element's children with the given child elements. - fn extend(&mut self, elements: impl IntoIterator); - - /// Add a single child element to this element. - fn child(mut self, child: impl IntoElement) -> Self - where - Self: Sized, - { - self.extend(std::iter::once(child.into_element().into_any())); - self - } - - /// Add multiple child elements to this element. - fn children(mut self, children: impl IntoIterator) -> Self - where - Self: Sized, - { - self.extend(children.into_iter().map(|child| child.into_any_element())); - self - } -} - -/// A globally unique identifier for an element, used to track state across frames. -#[derive(Deref, DerefMut, Clone, Default, Debug, Eq, PartialEq, Hash)] -pub struct GlobalElementId(pub(crate) Arc<[ElementId]>); - -impl Display for GlobalElementId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for (i, element_id) in self.0.iter().enumerate() { - if i > 0 { - write!(f, ".")?; - } - write!(f, "{}", element_id)?; - } - Ok(()) - } -} - -impl GlobalElementId { - pub(crate) fn accesskit_node_id(&self) -> accesskit::NodeId { - use std::hash::{Hash, Hasher}; - let mut hasher = std::hash::DefaultHasher::default(); - self.hash(&mut hasher); - accesskit::NodeId(hasher.finish()) - } -} - -trait ElementObject { - fn inner_element(&mut self) -> &mut dyn Any; - - fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId; - - fn prepaint(&mut self, window: &mut Window, cx: &mut App); - - fn paint(&mut self, window: &mut Window, cx: &mut App); - - fn layout_as_root( - &mut self, - available_space: Size, - window: &mut Window, - cx: &mut App, - ) -> Size; -} - -/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window. -pub struct Drawable { - /// The drawn element. - pub element: E, - phase: ElementDrawPhase, -} - -#[derive(Default)] -enum ElementDrawPhase { - #[default] - Start, - RequestLayout { - layout_id: LayoutId, - global_id: Option, - inspector_id: Option, - request_layout: RequestLayoutState, - }, - LayoutComputed { - layout_id: LayoutId, - global_id: Option, - inspector_id: Option, - available_space: Size, - request_layout: RequestLayoutState, - }, - Prepaint { - node_id: DispatchNodeId, - global_id: Option, - inspector_id: Option, - bounds: Bounds, - request_layout: RequestLayoutState, - prepaint: PrepaintState, - }, - Painted, -} - -/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window. -impl Drawable { - pub(crate) fn new(element: E) -> Self { - Drawable { - element, - phase: ElementDrawPhase::Start, - } - } - - fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId { - match mem::take(&mut self.phase) { - ElementDrawPhase::Start => { - let global_id = self.element.id().map(|element_id| { - window.element_id_stack.push(element_id); - GlobalElementId(Arc::from(&*window.element_id_stack)) - }); - - let inspector_id; - #[cfg(any(feature = "inspector", debug_assertions))] - { - inspector_id = self.element.source_location().map(|source| { - let path = crate::InspectorElementPath { - global_id: GlobalElementId(Arc::from(&*window.element_id_stack)), - source_location: source, - }; - window.build_inspector_element_id(path) - }); - } - #[cfg(not(any(feature = "inspector", debug_assertions)))] - { - inspector_id = None; - } - - let (layout_id, request_layout) = self.element.request_layout( - global_id.as_ref(), - inspector_id.as_ref(), - window, - cx, - ); - - if global_id.is_some() { - window.element_id_stack.pop(); - } - - self.phase = ElementDrawPhase::RequestLayout { - layout_id, - global_id, - inspector_id, - request_layout, - }; - layout_id - } - _ => panic!("must call request_layout only once"), - } - } - - pub(crate) fn prepaint(&mut self, window: &mut Window, cx: &mut App) { - match mem::take(&mut self.phase) { - ElementDrawPhase::RequestLayout { - layout_id, - global_id, - inspector_id, - mut request_layout, - } - | ElementDrawPhase::LayoutComputed { - layout_id, - global_id, - inspector_id, - mut request_layout, - .. - } => { - if let Some(element_id) = self.element.id() { - window.element_id_stack.push(element_id); - debug_assert_eq!(&*global_id.as_ref().unwrap().0, &*window.element_id_stack); - } - - let bounds = window.layout_bounds(layout_id); - let mut pushed_a11y_node = false; - if window.a11y.is_active() { - if let Some(global_id) = global_id.as_ref() { - if let Some(role) = self.element.a11y_role() { - let node_id = global_id.accesskit_node_id(); - let mut node = accesskit::Node::new(role); - let scale = window.scale_factor(); - node.set_bounds(accesskit::Rect { - x0: (bounds.origin.x.0 * scale) as f64, - y0: (bounds.origin.y.0 * scale) as f64, - x1: ((bounds.origin.x.0 + bounds.size.width.0) * scale) as f64, - y1: ((bounds.origin.y.0 + bounds.size.height.0) * scale) as f64, - }); - self.element.write_a11y_info(&mut node); - window.a11y.node_bounds.insert(node_id, bounds); - pushed_a11y_node = window.a11y.nodes.push(node_id, node); - #[cfg(debug_assertions)] - if pushed_a11y_node { - let view = window - .a11y - .view_type_names - .get(&window.current_view()) - .copied(); - let source_location = self.element.source_location(); - window.a11y.nodes.record_node_info( - node_id, - crate::window::a11y::debug::NodeDebugInfo { - synthetic: false, - view, - element_id: global_id.0.last().map(|id| format!("{id:?}")), - source_location, - }, - ); - } - } - } - } - - let node_id = window.next_frame.dispatch_tree.push_node(); - let mut prepaint = self.element.prepaint( - global_id.as_ref(), - inspector_id.as_ref(), - bounds, - &mut request_layout, - window, - cx, - ); - window.next_frame.dispatch_tree.pop_node(); - - if pushed_a11y_node { - if let Some(global_id) = global_id.as_ref() { - #[cfg(debug_assertions)] - let creator = crate::window::a11y::debug::NodeCreator { - view: window - .a11y - .view_type_names - .get(&window.current_view()) - .copied(), - element_id: global_id.0.last().map(|id| format!("{id:?}")), - source_location: self.element.source_location(), - }; - let mut builder = A11ySubtreeBuilder::new( - global_id.accesskit_node_id(), - &mut window.a11y.nodes, - ); - #[cfg(debug_assertions)] - { - builder = builder.with_creator(creator); - } - self.element - .a11y_synthetic_children(&mut prepaint, &mut builder); - } - window.a11y.nodes.pop(); - } - - if global_id.is_some() { - window.element_id_stack.pop(); - } - - self.phase = ElementDrawPhase::Prepaint { - node_id, - global_id, - inspector_id, - bounds, - request_layout, - prepaint, - }; - } - _ => panic!("must call request_layout before prepaint"), - } - } - - pub(crate) fn paint( - &mut self, - window: &mut Window, - cx: &mut App, - ) -> (E::RequestLayoutState, E::PrepaintState) { - match mem::take(&mut self.phase) { - ElementDrawPhase::Prepaint { - node_id, - global_id, - inspector_id, - bounds, - mut request_layout, - mut prepaint, - .. - } => { - if let Some(element_id) = self.element.id() { - window.element_id_stack.push(element_id); - debug_assert_eq!(&*global_id.as_ref().unwrap().0, &*window.element_id_stack); - } - - window.next_frame.dispatch_tree.set_active_node(node_id); - self.element.paint( - global_id.as_ref(), - inspector_id.as_ref(), - bounds, - &mut request_layout, - &mut prepaint, - window, - cx, - ); - - if global_id.is_some() { - window.element_id_stack.pop(); - } - - self.phase = ElementDrawPhase::Painted; - (request_layout, prepaint) - } - _ => panic!("must call prepaint before paint"), - } - } - - pub(crate) fn layout_as_root( - &mut self, - available_space: Size, - window: &mut Window, - cx: &mut App, - ) -> Size { - if matches!(&self.phase, ElementDrawPhase::Start) { - self.request_layout(window, cx); - } - - let layout_id = match mem::take(&mut self.phase) { - ElementDrawPhase::RequestLayout { - layout_id, - global_id, - inspector_id, - request_layout, - } => { - window.compute_layout(layout_id, available_space, cx); - self.phase = ElementDrawPhase::LayoutComputed { - layout_id, - global_id, - inspector_id, - available_space, - request_layout, - }; - layout_id - } - ElementDrawPhase::LayoutComputed { - layout_id, - global_id, - inspector_id, - available_space: prev_available_space, - request_layout, - } => { - if available_space != prev_available_space { - window.compute_layout(layout_id, available_space, cx); - } - self.phase = ElementDrawPhase::LayoutComputed { - layout_id, - global_id, - inspector_id, - available_space, - request_layout, - }; - layout_id - } - _ => panic!("cannot measure after painting"), - }; - - window.layout_bounds(layout_id).size - } -} - -impl ElementObject for Drawable -where - E: Element, - E::RequestLayoutState: 'static, -{ - fn inner_element(&mut self) -> &mut dyn Any { - &mut self.element - } - - #[inline] - fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId { - Drawable::request_layout(self, window, cx) - } - - #[inline] - fn prepaint(&mut self, window: &mut Window, cx: &mut App) { - Drawable::prepaint(self, window, cx); - } - - #[inline] - fn paint(&mut self, window: &mut Window, cx: &mut App) { - Drawable::paint(self, window, cx); - } - - #[inline] - fn layout_as_root( - &mut self, - available_space: Size, - window: &mut Window, - cx: &mut App, - ) -> Size { - Drawable::layout_as_root(self, available_space, window, cx) - } -} - -/// A dynamically typed element that can be used to store any element type. -pub struct AnyElement(ArenaBox); - -impl AnyElement { - pub(crate) fn new(element: E) -> Self - where - E: 'static + Element, - E::RequestLayoutState: Any, - { - let element = with_element_arena(|arena| arena.alloc(|| Drawable::new(element))) - .map(|element| element as &mut dyn ElementObject); - AnyElement(element) - } - - /// Attempt to downcast a reference to the boxed element to a specific type. - pub fn downcast_mut(&mut self) -> Option<&mut T> { - self.0.inner_element().downcast_mut::() - } - - /// Request the layout ID of the element stored in this `AnyElement`. - /// Used for laying out child elements in a parent element. - pub fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId { - self.0.request_layout(window, cx) - } - - /// Prepares the element to be painted by storing its bounds, giving it a chance to draw hitboxes and - /// request autoscroll before the final paint pass is confirmed. - pub fn prepaint(&mut self, window: &mut Window, cx: &mut App) -> Option { - let focus_assigned = window.next_frame.focus.is_some(); - - self.0.prepaint(window, cx); - - if !focus_assigned && let Some(focus_id) = window.next_frame.focus { - return FocusHandle::for_id(focus_id, &cx.focus_handles); - } - - None - } - - /// Paints the element stored in this `AnyElement`. - pub fn paint(&mut self, window: &mut Window, cx: &mut App) { - self.0.paint(window, cx); - } - - /// Performs layout for this element within the given available space and returns its size. - pub fn layout_as_root( - &mut self, - available_space: Size, - window: &mut Window, - cx: &mut App, - ) -> Size { - self.0.layout_as_root(available_space, window, cx) - } - - /// Prepaints this element at the given absolute origin. - /// If any element in the subtree beneath this element is focused, its FocusHandle is returned. - pub fn prepaint_at( - &mut self, - origin: Point, - window: &mut Window, - cx: &mut App, - ) -> Option { - window.with_absolute_element_offset(origin, |window| self.prepaint(window, cx)) - } - - /// Performs layout on this element in the available space, then prepaints it at the given absolute origin. - /// If any element in the subtree beneath this element is focused, its FocusHandle is returned. - pub fn prepaint_as_root( - &mut self, - origin: Point, - available_space: Size, - window: &mut Window, - cx: &mut App, - ) -> Option { - self.layout_as_root(available_space, window, cx); - window.with_absolute_element_offset(origin, |window| self.prepaint(window, cx)) - } -} - -impl Element for AnyElement { - type RequestLayoutState = (); - type PrepaintState = (); - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - let layout_id = self.request_layout(window, cx); - (layout_id, ()) - } - - fn prepaint( - &mut self, - _: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _: Bounds, - _: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) { - self.prepaint(window, cx); - } - - fn paint( - &mut self, - _: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _: Bounds, - _: &mut Self::RequestLayoutState, - _: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - self.paint(window, cx); - } -} - -impl IntoElement for AnyElement { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } - - fn into_any_element(self) -> AnyElement { - self - } -} - -/// The empty element, which renders nothing. -pub struct Empty; - -impl IntoElement for Empty { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -impl Element for Empty { - type RequestLayoutState = (); - type PrepaintState = (); - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - ( - window.request_layout( - Style { - display: crate::Display::None, - ..Default::default() - }, - None, - cx, - ), - (), - ) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - _state: &mut Self::RequestLayoutState, - _window: &mut Window, - _cx: &mut App, - ) { - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - _prepaint: &mut Self::PrepaintState, - _window: &mut Window, - _cx: &mut App, - ) { - } -} diff --git a/crates/gpui_pre/src/elements/anchored.rs b/crates/gpui_pre/src/elements/anchored.rs deleted file mode 100644 index 29260061..00000000 --- a/crates/gpui_pre/src/elements/anchored.rs +++ /dev/null @@ -1,398 +0,0 @@ -use smallvec::SmallVec; - -use crate::{ - Anchor, AnyElement, App, Axis, Bounds, Display, Edges, Element, GlobalElementId, - InspectorElementId, IntoElement, LayoutId, ParentElement, Pixels, Point, Position, Size, Style, - Window, point, px, -}; - -/// The state that the anchored element element uses to track its children. -pub struct AnchoredState { - child_layout_ids: SmallVec<[LayoutId; 4]>, -} - -/// An anchored element that can be used to display UI that -/// will avoid overflowing the window bounds. -pub struct Anchored { - children: SmallVec<[AnyElement; 2]>, - anchor: Anchor, - fit_mode: AnchoredFitMode, - anchor_position: Option>, - position_mode: AnchoredPositionMode, - offset: Option>, -} - -/// anchored gives you an element that will avoid overflowing the window bounds. -/// Its children should have no margin to avoid measurement issues. -pub fn anchored() -> Anchored { - Anchored { - children: SmallVec::new(), - anchor: Anchor::TopLeft, - fit_mode: AnchoredFitMode::SwitchAnchor, - anchor_position: None, - position_mode: AnchoredPositionMode::Window, - offset: None, - } -} - -impl Anchored { - /// Sets which corner of the anchored element should be anchored to the current position. - pub fn anchor(mut self, anchor: Anchor) -> Self { - self.anchor = anchor; - self - } - - /// Sets the position in window coordinates - /// (otherwise the location the anchored element is rendered is used) - pub fn position(mut self, anchor: Point) -> Self { - self.anchor_position = Some(anchor); - self - } - - /// Offset the final position by this amount. - /// Useful when you want to anchor to an element but offset from it, such as in PopoverMenu. - pub fn offset(mut self, offset: Point) -> Self { - self.offset = Some(offset); - self - } - - /// Sets the position mode for this anchored element. Local will have this - /// interpret its [`Anchored::position`] as relative to the parent element. - /// While Window will have it interpret the position as relative to the window. - pub fn position_mode(mut self, mode: AnchoredPositionMode) -> Self { - self.position_mode = mode; - 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 = AnchoredFitMode::SnapToWindow; - self - } - - /// Snap to window edge and leave some margins. - pub fn snap_to_window_with_margin(mut self, edges: impl Into>) -> Self { - self.fit_mode = AnchoredFitMode::SnapToWindowWithMargin(edges.into()); - self - } -} - -impl ParentElement for Anchored { - fn extend(&mut self, elements: impl IntoIterator) { - self.children.extend(elements) - } -} - -impl Element for Anchored { - type RequestLayoutState = AnchoredState; - type PrepaintState = (); - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (crate::LayoutId, Self::RequestLayoutState) { - let child_layout_ids = self - .children - .iter_mut() - .map(|child| child.request_layout(window, cx)) - .collect::>(); - - let anchored_style = Style { - position: Position::Absolute, - display: Display::Flex, - ..Style::default() - }; - - let layout_id = window.request_layout(anchored_style, child_layout_ids.iter().copied(), cx); - - (layout_id, AnchoredState { child_layout_ids }) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - request_layout: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) { - if request_layout.child_layout_ids.is_empty() { - return; - } - - let children_bounds = request_layout - .child_layout_ids - .iter() - .map(|id| window.layout_bounds(*id)) - .reduce(|acc, bounds| acc.union(&bounds)) - .unwrap(); - - let (origin, mut desired) = self.position_mode.get_position_and_bounds( - self.anchor_position, - self.anchor, - children_bounds.size, - bounds, - self.offset, - ); - - let limits = Bounds { - origin: Point::default(), - size: window.viewport_size(), - }; - - if self.fit_mode == AnchoredFitMode::SwitchAnchor { - let mut anchor = self.anchor; - - if desired.left() < limits.left() || desired.right() > limits.right() { - let switched = Bounds::from_anchor_and_size( - anchor.other_side_along(Axis::Horizontal), - origin, - children_bounds.size, - ); - if !(switched.left() < limits.left() || switched.right() > limits.right()) { - anchor = anchor.other_side_along(Axis::Horizontal); - desired = switched - } - } - - if desired.top() < limits.top() || desired.bottom() > limits.bottom() { - let switched = Bounds::from_anchor_and_size( - anchor.other_side_along(Axis::Vertical), - origin, - children_bounds.size, - ); - if !(switched.top() < limits.top() || switched.bottom() > limits.bottom()) { - desired = switched; - } - } - } - - let client_inset = window.client_inset.unwrap_or(px(0.)); - let edges = match self.fit_mode { - AnchoredFitMode::SnapToWindowWithMargin(edges) => edges, - _ => Edges::default(), - } - .map(|edge| *edge + client_inset); - - // Snap the horizontal edges of the anchored element 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() + edges.right; - } - if desired.left() < limits.left() { - desired.origin.x = limits.origin.x + edges.left; - } - - // Snap the vertical edges of the anchored element 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() + edges.bottom; - } - if desired.top() < limits.top() { - desired.origin.y = limits.origin.y + edges.top; - } - - let offset = desired.origin - bounds.origin; - let offset = point(offset.x.round(), offset.y.round()); - - window.with_element_offset(offset, |window| { - for child in &mut self.children { - child.prepaint(window, cx); - } - }) - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: crate::Bounds, - _request_layout: &mut Self::RequestLayoutState, - _prepaint: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - for child in &mut self.children { - child.paint(window, cx); - } - } -} - -impl IntoElement for Anchored { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -/// Which algorithm to use when fitting the anchored element to be inside the window. -#[derive(Copy, Clone, PartialEq)] -pub enum AnchoredFitMode { - /// Snap the anchored element to the window edge. - SnapToWindow, - /// Snap to window edge and leave some margins. - SnapToWindowWithMargin(Edges), - /// Switch which corner anchor this anchored element is attached to. - SwitchAnchor, -} - -/// Which algorithm to use when positioning the anchored element. -#[derive(Copy, Clone, PartialEq)] -pub enum AnchoredPositionMode { - /// Position the anchored element relative to the window. - Window, - /// Position the anchored element relative to its parent. - Local, -} - -impl AnchoredPositionMode { - fn get_position_and_bounds( - &self, - anchor_position: Option>, - anchor: Anchor, - size: Size, - bounds: Bounds, - offset: Option>, - ) -> (Point, Bounds) { - let offset = offset.unwrap_or_default(); - - match self { - AnchoredPositionMode::Window => { - let anchor_position = anchor_position.unwrap_or(bounds.origin); - let bounds = Bounds::from_anchor_and_size(anchor, anchor_position + offset, size); - (anchor_position, bounds) - } - AnchoredPositionMode::Local => { - let anchor_position = anchor_position.unwrap_or_default(); - let bounds = Bounds::from_anchor_and_size( - anchor, - bounds.origin + anchor_position + offset, - size, - ); - (anchor_position, bounds) - } - } - } -} - -#[cfg(test)] -mod tests { - use crate::{ - Context, Pixels, PlatformInput, Point, TestAppContext, Window, deferred, div, point, - prelude::*, px, size, - }; - - struct AnchoredTestView { - position: Point, - } - - impl Render for AnchoredTestView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div().size_full().child( - div() - .id("scroll-container") - .overflow_y_scroll() - .size_full() - .child(div().h(px(2000.)).w_full()) - .child( - deferred( - super::anchored() - .snap_to_window() - .position(self.position) - .child( - div() - .id("menu") - .debug_selector(|| "MENU".into()) - .w(px(200.)) - .h(px(300.)), - ), - ) - .with_priority(1), - ), - ) - } - } - - #[gpui::test] - fn test_anchored_position_without_scroll(cx: &mut TestAppContext) { - let window = cx.open_window(size(px(800.), px(600.)), |_, _| AnchoredTestView { - position: point(px(100.), px(100.)), - }); - - cx.run_until_parked(); - - let menu_bounds = window - .update(cx, |_, window, _| { - window.rendered_frame.debug_bounds.get("MENU").copied() - }) - .unwrap() - .expect("MENU debug bounds not found"); - - assert_eq!(menu_bounds.origin, point(px(100.), px(100.))); - assert_eq!(menu_bounds.size, size(px(200.), px(300.))); - } - - #[gpui::test] - fn test_anchored_position_when_scrolled(cx: &mut TestAppContext) { - let window = cx.open_window(size(px(800.), px(600.)), |_, _| AnchoredTestView { - position: point(px(100.), px(100.)), - }); - - cx.run_until_parked(); - - window - .update(cx, |_, window, cx| { - let event = gpui::ScrollWheelEvent { - position: point(px(400.), px(300.)), - delta: gpui::ScrollDelta::Pixels(point(px(0.), px(-1000.))), - ..Default::default() - }; - window.dispatch_event(PlatformInput::ScrollWheel(event), cx); - }) - .unwrap(); - - cx.run_until_parked(); - - let menu_bounds = window - .update(cx, |_, window, _| { - window.rendered_frame.debug_bounds.get("MENU").copied() - }) - .unwrap() - .expect("MENU debug bounds not found"); - - assert_eq!(menu_bounds.origin, point(px(100.), px(100.))); - assert_eq!(menu_bounds.size, size(px(200.), px(300.))); - } - - #[gpui::test] - fn test_anchored_snaps_to_window(cx: &mut TestAppContext) { - let window = cx.open_window(size(px(800.), px(600.)), |_, _| AnchoredTestView { - position: point(px(100.), px(500.)), - }); - - cx.run_until_parked(); - - let menu_bounds = window - .update(cx, |_, window, _| { - window.rendered_frame.debug_bounds.get("MENU").copied() - }) - .unwrap() - .expect("MENU debug bounds not found"); - - assert_eq!(menu_bounds.origin, point(px(100.), px(300.))); - assert_eq!(menu_bounds.size, size(px(200.), px(300.))); - } -} diff --git a/crates/gpui_pre/src/elements/animation.rs b/crates/gpui_pre/src/elements/animation.rs deleted file mode 100644 index e0768935..00000000 --- a/crates/gpui_pre/src/elements/animation.rs +++ /dev/null @@ -1,1028 +0,0 @@ -use scheduler::Instant; -use std::{cell::Cell, rc::Rc, time::Duration}; - -use crate::{ - AnyElement, App, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, - ParentElement, SpringAnimation, SpringConfig, SpringPlayback, SpringState, SpringTarget, - Window, -}; - -pub use easing::*; -use smallvec::SmallVec; - -/// An animation that can be applied to an element. -#[derive(Clone)] -pub struct Animation { - /// The amount of time for which this animation should run - pub duration: Duration, - /// Whether to repeat this animation when it finishes - pub oneshot: bool, - /// Whether to derive the phase from a shared clock. See [`Animation::repeat_synced`]. - pub synced: bool, - /// A function that maps normalized time to an animated value. - /// The result may exceed 0..1 for easing functions that overshoot. - pub easing: Rc f32>, - /// The maximum number of times per second this animation re-renders. - /// When `None`, the animation re-renders on every frame. - pub max_fps: Option, -} - -impl Animation { - /// Create a new animation with the given duration. - /// By default the animation will only run once and will use a linear easing function. - pub fn new(duration: Duration) -> Self { - Self { - duration, - oneshot: true, - synced: false, - easing: Rc::new(linear), - max_fps: None, - } - } - - /// Set the animation to loop when it finishes. - pub fn repeat(mut self) -> Self { - self.oneshot = false; - self - } - - /// Set the animation to loop when it finishes, phase-locked to a clock shared by the whole [`App`]. - pub fn repeat_synced(mut self) -> Self { - self.oneshot = false; - self.synced = true; - self - } - - /// Sets the easing function used to map normalized time to an animated value. - /// - /// The output is not clamped, allowing physical easing functions such as - /// springs to overshoot. - pub fn with_easing(mut self, easing: impl Fn(f32) -> f32 + 'static) -> Self { - self.easing = Rc::new(easing); - self - } - - /// Limit how often this animation re-renders. Instead of re-rendering on - /// every frame, the animation schedules its next render `1 / max_fps` - /// seconds after the current one. Values that are not finite and positive - /// are ignored. - pub fn with_max_fps(mut self, max_fps: f32) -> Self { - self.max_fps = Some(max_fps); - self - } -} - -/// An extension trait for adding the animation wrapper to both Elements and Components -/// -/// Animations rendered through this trait automatically respect -/// [`App::reduce_motion`](crate::App::reduce_motion): when it is set, -/// the element is rendered in a static state (the end state for oneshot -/// animations, the start state for repeating ones) and no animation frames are -/// scheduled. -pub trait AnimationExt { - /// Render this component or element with an animation - fn with_animation( - self, - id: impl Into, - animation: Animation, - animator: impl Fn(Self, f32) -> Self + 'static, - ) -> AnimationElement - where - Self: Sized, - { - AnimationElement { - id: id.into(), - element: Some(self), - animator: Box::new(move |this, _, value| animator(this, value)), - animations: smallvec::smallvec![animation], - } - } - - /// Render this component or element with a chain of animations - fn with_animations( - self, - id: impl Into, - animations: Vec, - animator: impl Fn(Self, usize, f32) -> Self + 'static, - ) -> AnimationElement - where - Self: Sized, - { - AnimationElement { - id: id.into(), - element: Some(self), - animator: Box::new(animator), - animations: animations.into(), - } - } - - /// Renders this component or element at the value produced by a spring. - /// - /// The element ID preserves position and velocity across target changes. - /// A newly mounted spring starts at its target unless configured with - /// [`SpringAnimation::from`]. - fn with_spring( - self, - id: impl Into, - animation: SpringAnimation, - animator: impl FnOnce(Self, T::Output) -> Self + 'static, - ) -> SpringAnimationElement - where - Self: Sized, - T: SpringTarget, - T::Output: 'static, - { - let SpringAnimation { - config, - target, - epsilon, - initial, - playback, - } = animation; - let scalar_target = target.target(); - SpringAnimationElement { - id: id.into(), - element: Some(self), - config, - target: scalar_target, - epsilon, - initial, - playback, - animator: Some(Box::new(move |this, value| { - animator(this, target.resolve(value)) - })), - } - } -} - -impl AnimationExt for E {} - -/// A GPUI element that applies an animation to another element -pub struct AnimationElement { - id: ElementId, - element: Option, - animations: SmallVec<[Animation; 1]>, - animator: Box E + 'static>, -} - -/// A GPUI element driven by a stateful spring. -pub struct SpringAnimationElement { - id: ElementId, - element: Option, - config: SpringConfig, - target: f32, - epsilon: f32, - initial: Option, - playback: SpringPlayback, - animator: Option E + 'static>>, -} - -impl ParentElement for SpringAnimationElement { - fn extend(&mut self, elements: impl IntoIterator) { - let Some(element) = &mut self.element else { - return; - }; - - element.extend(elements); - } -} - -impl SpringAnimationElement { - /// Returns a new [`SpringAnimationElement`] after applying the given function - /// to the element being animated. - pub fn map_element(mut self, f: impl FnOnce(E) -> E) -> SpringAnimationElement { - self.element = self.element.map(f); - self - } -} - -impl IntoElement for SpringAnimationElement { - type Element = SpringAnimationElement; - - fn into_element(self) -> Self::Element { - self - } -} - -impl ParentElement for AnimationElement { - fn extend(&mut self, elements: impl IntoIterator) { - let Some(element) = &mut self.element else { - return; - }; - - element.extend(elements); - } -} - -impl AnimationElement { - /// Returns a new [`AnimationElement`] after applying the given function - /// to the element being animated. - pub fn map_element(mut self, f: impl FnOnce(E) -> E) -> AnimationElement { - self.element = self.element.map(f); - self - } -} - -impl IntoElement for AnimationElement { - type Element = AnimationElement; - - fn into_element(self) -> Self::Element { - self - } -} - -struct AnimationState { - start: Instant, - animation_ix: usize, - /// Whether a throttled re-render (see [`Animation::with_max_fps`]) is - /// already scheduled, so overlapping renders don't stack extra timers. - delayed_frame_pending: Rc>, -} - -struct SpringElementState { - spring: SpringState, - target: f32, - config: SpringConfig, - initial: f32, - playback: SpringPlayback, - updated_at: Instant, -} - -impl Element for SpringAnimationElement { - type RequestLayoutState = AnyElement; - type PrepaintState = (); - - fn id(&self) -> Option { - Some(self.id.clone()) - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - global_id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (crate::LayoutId, Self::RequestLayoutState) { - window.with_element_state(global_id.unwrap(), |state, window| { - let now = Instant::now(); - let initial = self.initial.unwrap_or(self.target); - let mut state = state.unwrap_or_else(|| SpringElementState { - spring: SpringState { - position: initial, - velocity: 0.0, - }, - target: self.target, - config: self.config, - initial, - playback: self.playback, - updated_at: now, - }); - - let elapsed = now.duration_since(state.updated_at).as_secs_f32(); - match state.playback { - SpringPlayback::Running => { - state.spring = state.config.step(state.spring, state.target, elapsed); - } - SpringPlayback::Paused - | SpringPlayback::Stopped - | SpringPlayback::Completed - | SpringPlayback::Cancelled => {} - } - - state.config = self.config; - state.target = self.target; - - let done = match self.playback { - SpringPlayback::Running => { - if cx.reduce_motion() { - state.spring = SpringState { - position: state.target, - velocity: 0.0, - }; - true - } else { - let done = - state - .config - .is_settled(state.spring, state.target, self.epsilon); - if done { - state.spring = SpringState { - position: state.target, - velocity: 0.0, - }; - } - done - } - } - SpringPlayback::Paused => true, - SpringPlayback::Stopped => { - state.spring.velocity = 0.0; - true - } - SpringPlayback::Completed => { - state.spring = SpringState { - position: state.target, - velocity: 0.0, - }; - true - } - SpringPlayback::Cancelled => { - state.spring = SpringState { - position: state.initial, - velocity: 0.0, - }; - true - } - }; - state.playback = self.playback; - state.updated_at = now; - - let element = self.element.take().expect("should only be called once"); - let animator = self.animator.take().expect("should only be called once"); - let mut element = animator(element, state.spring.position).into_any_element(); - - if !done { - window.request_animation_frame(); - } - - ((element.request_layout(window, cx), element), state) - }) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: crate::Bounds, - element: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Self::PrepaintState { - element.prepaint(window, cx); - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: crate::Bounds, - element: &mut Self::RequestLayoutState, - _: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - element.paint(window, cx); - } -} - -impl Element for AnimationElement { - type RequestLayoutState = AnyElement; - type PrepaintState = (); - - fn id(&self) -> Option { - Some(self.id.clone()) - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - global_id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (crate::LayoutId, Self::RequestLayoutState) { - window.with_element_state(global_id.unwrap(), |state, window| { - let mut state = state.unwrap_or_else(|| AnimationState { - start: Instant::now(), - animation_ix: 0, - delayed_frame_pending: Rc::new(Cell::new(false)), - }); - let (animation_ix, delta, done) = if cx.reduce_motion() { - let animation_ix = self.animations.len() - 1; - let delta = if self.animations[animation_ix].oneshot { - 1.0 - } else { - 0.0 - }; - (animation_ix, delta, true) - } else { - let animation_ix = state.animation_ix; - let duration = self.animations[animation_ix].duration; - - let elapsed = if self.animations[animation_ix].synced && !duration.is_zero() { - let elapsed = cx.background_executor().now() - cx.synced_animation_epoch; - // Reduce modulo the duration before f32 conversion, which loses sub-second precision at scale. - Duration::from_nanos((elapsed.as_nanos() % duration.as_nanos()) as u64) - } else { - state.start.elapsed() - }; - let mut delta = elapsed.as_secs_f32() / duration.as_secs_f32(); - - let mut done = false; - if delta > 1.0 { - if self.animations[animation_ix].oneshot { - if animation_ix >= self.animations.len() - 1 { - done = true; - } else { - state.start = Instant::now(); - state.animation_ix += 1; - } - delta = 1.0; - } else { - delta %= 1.0; - } - } - (animation_ix, delta, done) - }; - let delta = (self.animations[animation_ix].easing)(delta); - - debug_assert!(delta.is_finite(), "animated value should be finite"); - - let element = self.element.take().expect("should only be called once"); - let mut element = (self.animator)(element, animation_ix, delta).into_any_element(); - - if !done { - match self.animations[animation_ix].max_fps { - Some(max_fps) if max_fps.is_finite() && max_fps > 0.0 => { - if !state.delayed_frame_pending.get() { - state.delayed_frame_pending.set(true); - let delayed_frame_pending = state.delayed_frame_pending.clone(); - let view = window.current_view(); - let interval = Duration::from_secs_f32(1.0 / max_fps); - window - .spawn(cx, async move |cx| { - cx.background_executor().timer(interval).await; - delayed_frame_pending.set(false); - cx.update(move |_, cx| cx.notify(view)).ok(); - }) - .detach(); - } - } - _ => window.request_animation_frame(), - } - } - - ((element.request_layout(window, cx), element), state) - }) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: crate::Bounds, - element: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Self::PrepaintState { - element.prepaint(window, cx); - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: crate::Bounds, - element: &mut Self::RequestLayoutState, - _: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - element.paint(window, cx); - } -} - -mod easing { - use std::f32::consts::PI; - - /// The linear easing function, or delta itself - pub fn linear(delta: f32) -> f32 { - delta - } - - /// The quadratic easing function, delta * delta - pub fn quadratic(delta: f32) -> f32 { - delta * delta - } - - /// The quadratic ease-in-out function, which starts and ends slowly but speeds up in the middle - pub fn ease_in_out(delta: f32) -> f32 { - if delta < 0.5 { - 2.0 * delta * delta - } else { - let x = -2.0 * delta + 2.0; - 1.0 - x * x / 2.0 - } - } - - /// The Quint ease-out function, which starts quickly and decelerates to a stop - pub fn ease_out_quint() -> impl Fn(f32) -> f32 { - move |delta| 1.0 - (1.0 - delta).powi(5) - } - - /// Apply the given easing function, first in the forward direction and then in the reverse direction - pub fn bounce(easing: impl Fn(f32) -> f32) -> impl Fn(f32) -> f32 { - move |delta| { - if delta < 0.5 { - easing(delta * 2.0) - } else { - easing((1.0 - delta) * 2.0) - } - } - } - - /// A custom easing function for pulsating alpha that slows down as it approaches 0.1 - pub fn pulsating_between(min: f32, max: f32) -> impl Fn(f32) -> f32 { - let range = max - min; - - move |delta| { - // Use a combination of sine and cubic functions for a more natural breathing rhythm - let t = (delta * 2.0 * PI).sin(); - let breath = (t * t * t + t) / 2.0; - - // Map the breath to our desired alpha range - let normalized_alpha = (breath + 1.0) / 2.0; - - min + (normalized_alpha * range) - } - } -} - -#[cfg(test)] -mod tests { - use std::{cell::RefCell, rc::Rc, time::Duration}; - - use crate::{ - Animation, Context, InteractiveElement, Pixels, Render, SpringAnimation, SpringConfig, - TestAppContext, WindowHandle, div, prelude::*, px, size, - }; - - use super::*; - - struct AnimationTestView { - rendered_deltas: Rc>>, - max_fps: Option, - } - - struct SyncedAnimationTestView { - show_second: bool, - first_deltas: Rc>>, - second_deltas: Rc>>, - } - - struct SpringAnimationTestView { - target: Pixels, - initial: Option, - playback: SpringPlayback, - rendered_values: Rc>>, - } - - impl Render for SpringAnimationTestView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let rendered_values = self.rendered_values.clone(); - let mut animation = SpringAnimation::new(SpringConfig::new(100.0, 2.0, 1.0)) - .to(self.target) - .with_epsilon(0.01) - .playback(self.playback); - if let Some(initial) = self.initial { - animation = animation.from(initial); - } - div().with_spring("spring-animation", animation, move |this, value| { - rendered_values.borrow_mut().push(value); - this.left(value) - }) - } - } - - impl Render for SyncedAnimationTestView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let record_deltas = |deltas: Rc>>| { - move |this, delta| { - deltas.borrow_mut().push(delta); - this - } - }; - div() - .size_full() - .child(div().with_animation( - "first-synced-animation", - Animation::new(Duration::from_secs(1)).repeat_synced(), - record_deltas(self.first_deltas.clone()), - )) - .when(self.show_second, |this| { - this.child(div().with_animation( - "second-synced-animation", - Animation::new(Duration::from_secs(1)).repeat_synced(), - record_deltas(self.second_deltas.clone()), - )) - }) - } - } - - impl Render for AnimationTestView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let rendered_deltas = self.rendered_deltas.clone(); - // The throttled variant syncs to the shared clock so the deltas - // follow the test scheduler's clock rather than wall time. - let mut animation = Animation::new(Duration::from_secs(1)); - if let Some(max_fps) = self.max_fps { - animation = animation.repeat_synced().with_max_fps(max_fps); - } else { - animation = animation.repeat(); - } - div().size_full().child(div().with_animation( - "repeating-animation", - animation, - move |this, delta| { - rendered_deltas.borrow_mut().push(delta); - this - }, - )) - } - } - - fn open_test_window( - cx: &mut TestAppContext, - ) -> (Rc>>, WindowHandle) { - open_test_window_with_max_fps(cx, None) - } - - fn open_test_window_with_max_fps( - cx: &mut TestAppContext, - max_fps: Option, - ) -> (Rc>>, WindowHandle) { - let rendered_deltas = Rc::new(RefCell::new(Vec::new())); - let window = cx.open_window(size(px(100.), px(100.)), { - let rendered_deltas = rendered_deltas.clone(); - move |_, _| AnimationTestView { - rendered_deltas, - max_fps, - } - }); - cx.run_until_parked(); - (rendered_deltas, window) - } - - fn simulate_next_frame(window: &WindowHandle, cx: &mut TestAppContext) -> usize { - let callback_count = window - .update(cx, |_, window, cx| window.simulate_next_frame(cx)) - .unwrap(); - cx.run_until_parked(); - callback_count - } - // Before parent-animation-element, using .with_animation - // would not allow chaining .parent after. This is just a - // build check that we can call div().id().with_animation().child() - #[test] - fn test_animation_parent() { - div() - .id("id") - // - .with_animation( - "animation", - Animation::new(Duration::from_secs(1)), - |el, _t| { - // - el - }, - ) - .child( - // - div(), - ); - } - - #[test] - fn test_spring_animation_parent() { - div() - .id("id") - .with_spring( - "spring-animation", - SpringAnimation::new(SpringConfig::new(100.0, 10.0, 1.0)) - .to(px(10.0)) - .from(px(0.0)), - |element, value| element.left(value), - ) - .child(div()); - } - - #[gpui::test] - fn test_spring_animation_preserves_velocity_when_retargeted(cx: &mut TestAppContext) { - let rendered_values = Rc::new(RefCell::new(Vec::new())); - let window = cx.open_window(size(px(100.0), px(100.0)), { - let rendered_values = rendered_values.clone(); - move |_, _| SpringAnimationTestView { - target: px(0.0), - initial: None, - playback: SpringPlayback::Running, - rendered_values, - } - }); - cx.run_until_parked(); - assert_eq!(*rendered_values.borrow(), vec![px(0.0)]); - - window - .update(cx, |view, _, cx| { - view.target = px(100.0); - cx.notify(); - }) - .unwrap(); - cx.run_until_parked(); - - cx.executor().advance_clock(Duration::from_millis(50)); - assert!(simulate_next_frame(&window, cx) > 0); - let value_before_retargeting = *rendered_values.borrow().last().unwrap(); - assert!(value_before_retargeting > px(0.0)); - assert!(value_before_retargeting < px(100.0)); - - window - .update(cx, |view, _, cx| { - view.target = px(0.0); - cx.notify(); - }) - .unwrap(); - cx.run_until_parked(); - - cx.executor().advance_clock(Duration::from_millis(5)); - assert!(simulate_next_frame(&window, cx) > 0); - let value_after_retargeting = *rendered_values.borrow().last().unwrap(); - assert!(value_after_retargeting > value_before_retargeting); - } - - #[gpui::test] - fn test_paused_spring_resumes_with_its_velocity(cx: &mut TestAppContext) { - let rendered_values = Rc::new(RefCell::new(Vec::new())); - let window = cx.open_window(size(px(100.0), px(100.0)), { - let rendered_values = rendered_values.clone(); - move |_, _| SpringAnimationTestView { - target: px(0.0), - initial: None, - playback: SpringPlayback::Running, - rendered_values, - } - }); - cx.run_until_parked(); - - window - .update(cx, |view, _, cx| { - view.target = px(100.0); - cx.notify(); - }) - .unwrap(); - cx.run_until_parked(); - cx.executor().advance_clock(Duration::from_millis(50)); - assert!(simulate_next_frame(&window, cx) > 0); - - window - .update(cx, |view, _, cx| { - view.target = px(0.0); - view.playback = SpringPlayback::Paused; - cx.notify(); - }) - .unwrap(); - cx.run_until_parked(); - let paused_value = *rendered_values.borrow().last().unwrap(); - - cx.executor().advance_clock(Duration::from_millis(500)); - assert!(simulate_next_frame(&window, cx) > 0); - assert_eq!(*rendered_values.borrow().last().unwrap(), paused_value); - assert_eq!(simulate_next_frame(&window, cx), 0); - - window - .update(cx, |view, _, cx| { - view.playback = SpringPlayback::Running; - cx.notify(); - }) - .unwrap(); - cx.run_until_parked(); - cx.executor().advance_clock(Duration::from_millis(5)); - assert!(simulate_next_frame(&window, cx) > 0); - assert!(*rendered_values.borrow().last().unwrap() > paused_value); - } - - #[gpui::test] - fn test_stopped_spring_resumes_without_velocity(cx: &mut TestAppContext) { - let rendered_values = Rc::new(RefCell::new(Vec::new())); - let window = cx.open_window(size(px(100.0), px(100.0)), { - let rendered_values = rendered_values.clone(); - move |_, _| SpringAnimationTestView { - target: px(0.0), - initial: None, - playback: SpringPlayback::Running, - rendered_values, - } - }); - cx.run_until_parked(); - - window - .update(cx, |view, _, cx| { - view.target = px(1_000_000.0); - cx.notify(); - }) - .unwrap(); - cx.run_until_parked(); - cx.executor().advance_clock(Duration::from_millis(50)); - assert!(simulate_next_frame(&window, cx) > 0); - - window - .update(cx, |view, _, cx| { - view.target = px(0.0); - view.playback = SpringPlayback::Stopped; - cx.notify(); - }) - .unwrap(); - cx.run_until_parked(); - let stopped_value = *rendered_values.borrow().last().unwrap(); - - cx.executor().advance_clock(Duration::from_millis(500)); - assert!(simulate_next_frame(&window, cx) > 0); - assert_eq!(*rendered_values.borrow().last().unwrap(), stopped_value); - assert_eq!(simulate_next_frame(&window, cx), 0); - - window - .update(cx, |view, _, cx| { - view.target = stopped_value; - view.playback = SpringPlayback::Running; - cx.notify(); - }) - .unwrap(); - cx.run_until_parked(); - assert_eq!(*rendered_values.borrow().last().unwrap(), stopped_value); - assert_eq!(simulate_next_frame(&window, cx), 0); - } - - #[gpui::test] - fn test_cancelled_and_completed_springs_resolve_their_endpoints(cx: &mut TestAppContext) { - let rendered_values = Rc::new(RefCell::new(Vec::new())); - let window = cx.open_window(size(px(100.0), px(100.0)), { - let rendered_values = rendered_values.clone(); - move |_, _| SpringAnimationTestView { - target: px(100.0), - initial: Some(px(20.0)), - playback: SpringPlayback::Running, - rendered_values, - } - }); - cx.run_until_parked(); - assert_eq!(*rendered_values.borrow(), vec![px(20.0)]); - - cx.executor().advance_clock(Duration::from_millis(50)); - assert!(simulate_next_frame(&window, cx) > 0); - assert!(*rendered_values.borrow().last().unwrap() > px(20.0)); - - window - .update(cx, |view, _, cx| { - view.playback = SpringPlayback::Cancelled; - cx.notify(); - }) - .unwrap(); - cx.run_until_parked(); - assert_eq!(*rendered_values.borrow().last().unwrap(), px(20.0)); - assert!(simulate_next_frame(&window, cx) > 0); - assert_eq!(simulate_next_frame(&window, cx), 0); - - window - .update(cx, |view, _, cx| { - view.playback = SpringPlayback::Completed; - cx.notify(); - }) - .unwrap(); - cx.run_until_parked(); - assert_eq!(*rendered_values.borrow().last().unwrap(), px(100.0)); - assert_eq!(simulate_next_frame(&window, cx), 0); - } - - #[gpui::test] - fn test_spring_animation_respects_reduced_motion(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_reduce_motion(true)); - let rendered_values = Rc::new(RefCell::new(Vec::new())); - let window = cx.open_window(size(px(100.0), px(100.0)), { - let rendered_values = rendered_values.clone(); - move |_, _| SpringAnimationTestView { - target: px(100.0), - initial: None, - playback: SpringPlayback::Running, - rendered_values, - } - }); - cx.run_until_parked(); - - assert_eq!(*rendered_values.borrow(), vec![px(100.0)]); - assert_eq!(simulate_next_frame(&window, cx), 0); - } - - #[gpui::test] - fn test_repeating_animation_schedules_animation_frames(cx: &mut TestAppContext) { - let (rendered_deltas, window) = open_test_window(cx); - - assert_eq!(rendered_deltas.borrow().len(), 1); - - for expected_frames in 2..=3 { - assert_eq!(simulate_next_frame(&window, cx), 1); - assert_eq!(rendered_deltas.borrow().len(), expected_frames); - } - } - - #[gpui::test] - fn test_max_fps_schedules_timer_driven_frames(cx: &mut TestAppContext) { - let (rendered_deltas, window) = open_test_window_with_max_fps(cx, Some(10.0)); - - // The test scheduler's clock jitters forward slightly on each poll, - // so compare against expectations loosely. - let assert_deltas_approx_eq = |expected: &[f32]| { - let actual = rendered_deltas.borrow(); - assert_eq!(actual.len(), expected.len(), "deltas: {actual:?}"); - for (actual, expected) in actual.iter().zip(expected) { - assert!( - (actual - expected).abs() < 1e-2, - "expected {expected}, got {actual}" - ); - } - }; - - assert_deltas_approx_eq(&[0.0]); - - // No per-frame callback is scheduled; re-renders are timer-driven. - assert_eq!(simulate_next_frame(&window, cx), 0); - assert_deltas_approx_eq(&[0.0]); - - cx.executor().advance_clock(Duration::from_millis(105)); - cx.run_until_parked(); - assert_deltas_approx_eq(&[0.0, 0.105]); - - cx.executor().advance_clock(Duration::from_millis(105)); - cx.run_until_parked(); - assert_deltas_approx_eq(&[0.0, 0.105, 0.21]); - } - - #[gpui::test] - fn test_synced_animations_share_phase_across_elements(cx: &mut TestAppContext) { - let first_deltas = Rc::new(RefCell::new(Vec::new())); - let second_deltas = Rc::new(RefCell::new(Vec::new())); - let window = cx.open_window(size(px(100.), px(100.)), { - let first_deltas = first_deltas.clone(); - let second_deltas = second_deltas.clone(); - move |_, _| SyncedAnimationTestView { - show_second: false, - first_deltas, - second_deltas, - } - }); - cx.run_until_parked(); - - assert_eq!(*first_deltas.borrow(), vec![0.0]); - - cx.executor().advance_clock(Duration::from_millis(250)); - simulate_next_frame(&window, cx); - assert_eq!(*first_deltas.borrow(), vec![0.0, 0.25]); - - // The second element mounts a quarter through the cycle, yet renders - // the shared phase rather than starting at zero. - window - .update(cx, |view, _, cx| { - view.show_second = true; - cx.notify(); - }) - .unwrap(); - cx.run_until_parked(); - cx.executor().advance_clock(Duration::from_millis(250)); - simulate_next_frame(&window, cx); - - assert_eq!(*second_deltas.borrow().last().unwrap(), 0.5); - assert_eq!( - *first_deltas.borrow().last().unwrap(), - *second_deltas.borrow().last().unwrap() - ); - assert!(second_deltas.borrow().iter().all(|delta| *delta > 0.0)); - - // The phase wraps around each full cycle. - cx.executor().advance_clock(Duration::from_millis(2250)); - simulate_next_frame(&window, cx); - assert_eq!(*first_deltas.borrow().last().unwrap(), 0.75); - - // Sub-second precision survives months of uptime: converting the raw - // elapsed time to f32 would round 0.25 away entirely. - cx.executor() - .advance_clock(Duration::from_secs(300 * 24 * 60 * 60) + Duration::from_millis(500)); - simulate_next_frame(&window, cx); - assert_eq!(*first_deltas.borrow().last().unwrap(), 0.25); - } - - #[gpui::test] - fn test_reduce_motion_renders_single_static_frame(cx: &mut TestAppContext) { - cx.update(|cx| cx.set_reduce_motion(true)); - let (rendered_deltas, window) = open_test_window(cx); - - assert_eq!(*rendered_deltas.borrow(), vec![0.0]); - - assert_eq!(simulate_next_frame(&window, cx), 0); - assert_eq!(*rendered_deltas.borrow(), vec![0.0]); - } -} diff --git a/crates/gpui_pre/src/elements/canvas.rs b/crates/gpui_pre/src/elements/canvas.rs deleted file mode 100644 index d57d2f60..00000000 --- a/crates/gpui_pre/src/elements/canvas.rs +++ /dev/null @@ -1,95 +0,0 @@ -use refineable::Refineable as _; - -use crate::{ - App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, Pixels, - Style, StyleRefinement, Styled, Window, -}; - -/// Construct a canvas element with the given paint callback. -/// Useful for adding short term custom drawing to a view. -pub fn canvas( - prepaint: impl 'static + FnOnce(Bounds, &mut Window, &mut App) -> T, - paint: impl 'static + FnOnce(Bounds, T, &mut Window, &mut App), -) -> Canvas { - Canvas { - prepaint: Some(Box::new(prepaint)), - paint: Some(Box::new(paint)), - style: StyleRefinement::default(), - } -} - -/// A canvas element, meant for accessing the low level paint API without defining a whole -/// custom element -pub struct Canvas { - prepaint: Option, &mut Window, &mut App) -> T>>, - paint: Option, T, &mut Window, &mut App)>>, - style: StyleRefinement, -} - -impl IntoElement for Canvas { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -impl Element for Canvas { - type RequestLayoutState = Style; - type PrepaintState = Option; - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (crate::LayoutId, Self::RequestLayoutState) { - let mut style = Style::default(); - style.refine(&self.style); - let layout_id = window.request_layout(style.clone(), [], cx); - (layout_id, style) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - _request_layout: &mut Style, - window: &mut Window, - cx: &mut App, - ) -> Option { - Some(self.prepaint.take().unwrap()(bounds, window, cx)) - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - style: &mut Style, - prepaint: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - let prepaint = prepaint.take().unwrap(); - style.paint(bounds, window, cx, |window, cx| { - (self.paint.take().unwrap())(bounds, prepaint, window, cx) - }); - } -} - -impl Styled for Canvas { - fn style(&mut self) -> &mut crate::StyleRefinement { - &mut self.style - } -} diff --git a/crates/gpui_pre/src/elements/container_query.rs b/crates/gpui_pre/src/elements/container_query.rs deleted file mode 100644 index 0363ce69..00000000 --- a/crates/gpui_pre/src/elements/container_query.rs +++ /dev/null @@ -1,126 +0,0 @@ -//! A container query element, in the spirit of CSS container queries. -//! The element's own size is determined solely by its style and the space -//! offered by its parent. - -use refineable::Refineable as _; - -use crate::{ - AnyElement, App, AvailableSpace, Bounds, Element, ElementId, GlobalElementId, - InspectorElementId, IntoElement, LayoutId, Pixels, Size, Style, StyleRefinement, Styled, - Window, relative, -}; - -/// Construct a container query element with the given render callback. -/// The callback receives the size the element was assigned during layout and -/// returns the contents to display within it. -/// -/// By default the element fills its parent (equivalent to `.size_full()`); -/// use the [`Styled`] methods to size it differently. Because the contents -/// don't exist until after layout, they cannot influence the element's size. -/// -/// # Example -/// -/// ``` -/// # use gpui::{container_query, div, px, IntoElement, ParentElement}; -/// container_query(|size, _window, _cx| { -/// if size.width < px(240.) { -/// div().child("Narrow layout") -/// } else { -/// div().child("Wide layout") -/// } -/// }); -/// ``` -pub fn container_query( - render: impl 'static + FnOnce(Size, &mut Window, &mut App) -> E, -) -> ContainerQuery -where - E: IntoElement, -{ - let mut base_style = StyleRefinement::default(); - base_style.size.width = Some(relative(1.).into()); - base_style.size.height = Some(relative(1.).into()); - - ContainerQuery { - render: Some(Box::new(|size, window, cx| { - render(size, window, cx).into_any_element() - })), - style: base_style, - } -} - -/// A container query element, created with [`container_query`]. -pub struct ContainerQuery { - render: Option, &mut Window, &mut App) -> AnyElement>>, - style: StyleRefinement, -} - -impl Element for ContainerQuery { - type RequestLayoutState = (); - type PrepaintState = Option; - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - let mut style = Style::default(); - style.refine(&self.style); - let layout_id = window.request_layout(style, [], cx); - (layout_id, ()) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Option { - let render = self.render.take()?; - let mut child = render(bounds.size, window, cx); - child.layout_as_root(bounds.size.map(AvailableSpace::Definite), window, cx); - child.prepaint_at(bounds.origin, window, cx); - Some(child) - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - prepaint: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - if let Some(child) = prepaint { - child.paint(window, cx); - } - } -} - -impl IntoElement for ContainerQuery { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -impl Styled for ContainerQuery { - fn style(&mut self) -> &mut StyleRefinement { - &mut self.style - } -} diff --git a/crates/gpui_pre/src/elements/deferred.rs b/crates/gpui_pre/src/elements/deferred.rs deleted file mode 100644 index ddf324a6..00000000 --- a/crates/gpui_pre/src/elements/deferred.rs +++ /dev/null @@ -1,206 +0,0 @@ -use crate::{ - AnyElement, App, Bounds, Element, GlobalElementId, InspectorElementId, IntoElement, LayoutId, - Pixels, Window, -}; - -/// Builds a `Deferred` element, which delays the layout and paint of its child. -pub fn deferred(child: impl IntoElement) -> Deferred { - Deferred { - child: Some(child.into_any_element()), - priority: 0, - } -} - -/// An element which delays the painting of its child until after all of -/// its ancestors, while keeping its layout as part of the current element tree. -pub struct Deferred { - child: Option, - priority: usize, -} - -impl Deferred { - /// Sets the `priority` value of the `deferred` element, which - /// determines the drawing order relative to other deferred elements, - /// with higher values being drawn on top. - pub fn with_priority(mut self, priority: usize) -> Self { - self.priority = priority; - self - } -} - -impl Element for Deferred { - type RequestLayoutState = (); - type PrepaintState = (); - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, ()) { - let layout_id = self.child.as_mut().unwrap().request_layout(window, cx); - (layout_id, ()) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - window: &mut Window, - _cx: &mut App, - ) { - let child = self.child.take().unwrap(); - let element_offset = window.element_offset(); - window.defer_draw(child, element_offset, self.priority, None) - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - _prepaint: &mut Self::PrepaintState, - _window: &mut Window, - _cx: &mut App, - ) { - } -} - -impl IntoElement for Deferred { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -impl Deferred { - /// Sets a priority for the element. A higher priority conceptually means painting the element - /// on top of deferred draws with a lower priority (i.e. closer to the viewer). - pub fn priority(mut self, priority: usize) -> Self { - self.priority = priority; - self - } -} - -#[cfg(test)] -mod tests { - use crate::{ - Context, Entity, StyleRefinement, TestAppContext, Window, anchored, deferred, div, point, - prelude::*, px, size, - }; - - /// A stand-in for a dock panel hosting a popover (deferred draw) whose - /// content opens another popover (a deferred draw created while - /// prepainting the first one's content). - struct PanelView; - - impl Render for PanelView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div().key_context("Panel").size_full().child( - deferred( - anchored().position(point(px(10.), px(10.))).child( - div().key_context("Popover").w(px(200.)).h(px(200.)).child( - deferred( - anchored().position(point(px(30.), px(30.))).child( - div() - .key_context("NestedMenu") - .debug_selector(|| "NESTED_MENU".into()) - .w(px(50.)) - .h(px(50.)), - ), - ) - .with_priority(2), - ), - ), - ) - .with_priority(1), - ) - } - } - - struct RootView { - panel: Entity, - } - - impl Render for RootView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div().key_context("Root").size_full().child( - self.panel - .clone() - .cached(StyleRefinement::default().size_full()), - ) - } - } - - /// Regression test for a crash with nested deferred draws (e.g. a popover - /// menu inside a popover hosted by a cached dock panel). Prepaint indices - /// recorded during the deferred draw rounds must index the same - /// `deferred_draws` vector that `reuse_prepaint` slices on the next frame; - /// previously they were measured against a transient per-round vector, so - /// reusing the panel's subtree grafted the wrong deferred draws and - /// panicked in the dispatch tree. - #[gpui::test] - fn test_nested_deferred_draws_with_reused_views(cx: &mut TestAppContext) { - let window = cx.open_window(size(px(800.), px(600.)), |_, cx| { - let panel = cx.new(|_| PanelView); - RootView { panel } - }); - cx.run_until_parked(); - - let menu_bounds = window - .update(cx, |_, window, _| { - window - .rendered_frame - .debug_bounds - .get("NESTED_MENU") - .copied() - }) - .unwrap() - .expect("NESTED_MENU debug bounds not found"); - assert_eq!(menu_bounds.size, size(px(50.), px(50.))); - - // Re-render only the root view; the panel is cached, so its subtree - - // including both deferred draw records - is reused from the previous - // frame. - window.update(cx, |_, _, cx| cx.notify()).unwrap(); - cx.run_until_parked(); - - // Reuse the subtree a second time, exercising ranges that were - // themselves recorded during a reused frame. - window.update(cx, |_, _, cx| cx.notify()).unwrap(); - cx.run_until_parked(); - - // Re-render the panel itself again to prove the popovers still draw. - window - .update(cx, |root, _, cx| { - root.panel.update(cx, |_, cx| cx.notify()); - }) - .unwrap(); - cx.run_until_parked(); - - window - .update(cx, |_, window, _| { - assert_eq!(window.rendered_frame.deferred_draws.len(), 2); - assert!( - window - .rendered_frame - .debug_bounds - .contains_key("NESTED_MENU") - ); - }) - .unwrap(); - } -} diff --git a/crates/gpui_pre/src/elements/div.rs b/crates/gpui_pre/src/elements/div.rs deleted file mode 100644 index a23a59e1..00000000 --- a/crates/gpui_pre/src/elements/div.rs +++ /dev/null @@ -1,5450 +0,0 @@ -//! Div is the central, reusable element that most GPUI trees will be built from. -//! It functions as a container for other elements, and provides a number of -//! useful features for laying out and styling its children as well as binding -//! mouse events and action handlers. It is meant to be similar to the HTML `

` -//! element, but for GPUI. -//! -//! # Build your own div -//! -//! GPUI does not directly provide APIs for stateful, multi step events like `click` -//! and `drag`. We want GPUI users to be able to build their own abstractions for -//! their own needs. However, as a UI framework, we're also obliged to provide some -//! building blocks to make the process of building your own elements easier. -//! For this we have the [`Interactivity`] and the [`StyleRefinement`] structs, as well -//! as several associated traits. Together, these provide the full suite of Dom-like events -//! and Tailwind-like styling that you can use to build your own custom elements. Div is -//! constructed by combining these two systems into an all-in-one element. - -use crate::{ - point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent, - DispatchPhase, Display, Element, ElementId, Entity, EntityId, ExternalDragPayload, - ExternalDragPayloadSource, FocusHandle, Global, GlobalElementId, Hitbox, HitboxBehavior, - HitboxId, InspectorElementId, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, - KeyboardButton, KeyboardClickEvent, LayoutId, ModifiersChangedEvent, MouseButton, - MouseClickEvent, MouseDownEvent, MouseExitEvent, MouseMoveEvent, MousePressureEvent, - MouseUpEvent, OngoingScroll, Overflow, ParentElement, PinchEvent, Pixels, Point, Render, - ScrollWheelEvent, SharedString, Size, Style, StyleRefinement, Styled, Task, TooltipId, - Visibility, Window, WindowControlArea, -}; -use collections::HashMap; -use gpui_util::ResultExt; -use refineable::Refineable; -use smallvec::SmallVec; -use std::{ - any::{Any, TypeId}, - cell::RefCell, - cmp::Ordering, - fmt::Debug, - marker::PhantomData, - mem, - rc::Rc, - sync::Arc, - time::Duration, -}; - -use super::ImageCacheProvider; - -#[cfg(feature = "stacker")] -type StackSafe = stacksafe::StackSafe; -#[cfg(not(feature = "stacker"))] -type StackSafe = T; - -const DRAG_THRESHOLD: f64 = 2.; -const DEFAULT_TOOLTIP_SHOW_DELAY: Duration = Duration::from_millis(500); -const HOVERABLE_TOOLTIP_HIDE_DELAY: Duration = Duration::from_millis(500); - -/// The styling information for a given group. -pub struct GroupStyle { - /// The identifier for this group. - pub group: SharedString, - - /// The specific style refinement that this group would apply - /// to its children. - pub style: Box, -} - -/// An event for when a drag is moving over this element, with the given state type. -pub struct DragMoveEvent { - /// The mouse move event that triggered this drag move event. - pub event: MouseMoveEvent, - - /// The bounds of this element. - pub bounds: Bounds, - drag: PhantomData, - dragged_item: Arc, -} - -impl DragMoveEvent { - /// Returns the drag state for this event. - pub fn drag<'b>(&self, cx: &'b App) -> &'b T { - cx.active_drag - .as_ref() - .and_then(|drag| drag.value.downcast_ref::()) - .expect("DragMoveEvent is only valid when the stored active drag is of the same type.") - } - - /// An item that is about to be dropped. - pub fn dragged_item(&self) -> &dyn Any { - self.dragged_item.as_ref() - } -} - -impl Interactivity { - /// Create an `Interactivity`, capturing the caller location in debug mode. - #[cfg(any(feature = "inspector", debug_assertions))] - #[track_caller] - pub fn new() -> Interactivity { - Interactivity { - source_location: Some(core::panic::Location::caller()), - ..Default::default() - } - } - - /// Create an `Interactivity`, capturing the caller location in debug mode. - #[cfg(not(any(feature = "inspector", debug_assertions)))] - pub fn new() -> Interactivity { - Interactivity::default() - } - - /// Gets the source location of construction. Returns `None` when not in debug mode. - pub fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { - #[cfg(any(feature = "inspector", debug_assertions))] - { - self.source_location - } - - #[cfg(not(any(feature = "inspector", debug_assertions)))] - { - None - } - } - - /// Bind the given callback to the mouse down event for the given mouse button, during the bubble phase. - /// The imperative API equivalent of [`InteractiveElement::on_mouse_down`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to the view state from this callback. - pub fn on_mouse_down( - &mut self, - button: MouseButton, - listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, - ) { - self.mouse_down_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Bubble - && event.button == button - && hitbox.is_hovered(window) - { - (listener)(event, window, cx) - } - })); - } - - /// Bind the given callback to the mouse down event for any button, during the capture phase. - /// The imperative API equivalent of [`InteractiveElement::capture_any_mouse_down`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn capture_any_mouse_down( - &mut self, - listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, - ) { - self.mouse_down_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Capture && hitbox.is_hovered(window) { - (listener)(event, window, cx) - } - })); - } - - /// Bind the given callback to the mouse down event for any button, during the bubble phase. - /// The imperative API equivalent to [`InteractiveElement::on_any_mouse_down`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_any_mouse_down( - &mut self, - listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, - ) { - self.mouse_down_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { - (listener)(event, window, cx) - } - })); - } - - /// Bind the given callback to the mouse pressure event, during the bubble phase - /// the imperative API equivalent to [`InteractiveElement::on_mouse_pressure`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_mouse_pressure( - &mut self, - listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static, - ) { - self.mouse_pressure_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { - (listener)(event, window, cx) - } - })); - } - - /// Bind the given callback to the mouse pressure event, during the capture phase - /// the imperative API equivalent to [`InteractiveElement::on_mouse_pressure`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn capture_mouse_pressure( - &mut self, - listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static, - ) { - self.mouse_pressure_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Capture && hitbox.is_hovered(window) { - (listener)(event, window, cx) - } - })); - } - - /// Bind the given callback to the mouse up event for the given button, during the bubble phase. - /// The imperative API equivalent to [`InteractiveElement::on_mouse_up`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_mouse_up( - &mut self, - button: MouseButton, - listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, - ) { - self.mouse_up_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Bubble - && event.button == button - && hitbox.is_hovered(window) - { - (listener)(event, window, cx) - } - })); - } - - /// Bind the given callback to the mouse up event for any button, during the capture phase. - /// The imperative API equivalent to [`InteractiveElement::capture_any_mouse_up`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn capture_any_mouse_up( - &mut self, - listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, - ) { - self.mouse_up_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Capture && hitbox.is_hovered(window) { - (listener)(event, window, cx) - } - })); - } - - /// Bind the given callback to the mouse up event for any button, during the bubble phase. - /// The imperative API equivalent to [`Interactivity::on_any_mouse_up`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_any_mouse_up( - &mut self, - listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, - ) { - self.mouse_up_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { - (listener)(event, window, cx) - } - })); - } - - /// Bind the given callback to the mouse down event, on any button, during the capture phase, - /// when the mouse is outside of the bounds of this element. - /// The imperative API equivalent to [`InteractiveElement::on_mouse_down_out`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_mouse_down_out( - &mut self, - listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, - ) { - self.mouse_down_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Capture - && !window.has_active_prompt() - && !hitbox.contains(&window.mouse_position()) - { - (listener)(event, window, cx) - } - })); - } - - /// Bind the given callback to the mouse up event, for the given button, during the capture phase, - /// when the mouse is outside of the bounds of this element. - /// The imperative API equivalent to [`InteractiveElement::on_mouse_up_out`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_mouse_up_out( - &mut self, - button: MouseButton, - listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, - ) { - self.mouse_up_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Capture - && event.button == button - && !hitbox.is_hovered(window) - { - (listener)(event, window, cx); - } - })); - } - - /// Bind the given callback to the mouse move event, during the bubble phase. - /// The imperative API equivalent to [`InteractiveElement::on_mouse_move`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_mouse_move( - &mut self, - listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static, - ) { - self.mouse_move_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { - (listener)(event, window, cx); - } - })); - } - - /// Bind the given callback to the mouse exit event, during the bubble phase. - /// The imperative API equivalent to [`InteractiveElement::on_mouse_exit`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_mouse_exit( - &mut self, - listener: impl Fn(&MouseExitEvent, &mut Window, &mut App) + 'static, - ) { - self.mouse_exit_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { - (listener)(event, window, cx); - } - })); - } - - /// Bind the given callback to the mouse drag event of the given type. Note that this - /// will be called for all move events, inside or outside of this element, as long as the - /// drag was started with this element under the mouse. Useful for implementing draggable - /// UIs that don't conform to a drag and drop style interaction, like resizing. - /// The imperative API equivalent to [`InteractiveElement::on_drag_move`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_drag_move( - &mut self, - listener: impl Fn(&DragMoveEvent, &mut Window, &mut App) + 'static, - ) where - T: 'static, - { - self.mouse_move_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Capture - && let Some(drag) = &cx.active_drag - && drag.value.as_ref().type_id() == TypeId::of::() - { - (listener)( - &DragMoveEvent { - event: event.clone(), - bounds: hitbox.bounds, - drag: PhantomData, - dragged_item: Arc::clone(&drag.value), - }, - window, - cx, - ); - } - })); - } - - /// Bind the given callback to scroll wheel events during the bubble phase. - /// The imperative API equivalent to [`InteractiveElement::on_scroll_wheel`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_scroll_wheel( - &mut self, - listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static, - ) { - self.scroll_wheel_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) { - (listener)(event, window, cx); - } - })); - } - - /// Bind the given callback to pinch gesture events during the bubble phase. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_pinch(&mut self, listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static) { - self.pinch_listeners - .push(Box::new(move |event, phase, hitbox, window, cx| { - if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { - (listener)(event, window, cx); - } - })); - } - - /// Bind the given callback to pinch gesture events during the capture phase. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn capture_pinch( - &mut self, - listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static, - ) { - self.pinch_listeners - .push(Box::new(move |event, phase, _hitbox, window, cx| { - if phase == DispatchPhase::Capture { - (listener)(event, window, cx); - } else { - cx.propagate(); - } - })); - } - - /// Bind the given callback to an action dispatch during the capture phase. - /// The imperative API equivalent to [`InteractiveElement::capture_action`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn capture_action( - &mut self, - listener: impl Fn(&A, &mut Window, &mut App) + 'static, - ) { - self.action_listeners.push(( - TypeId::of::(), - Box::new(move |action, phase, window, cx| { - let action = action.downcast_ref().unwrap(); - if phase == DispatchPhase::Capture { - (listener)(action, window, cx) - } else { - cx.propagate(); - } - }), - )); - } - - /// Bind the given callback to an action dispatch during the bubble phase. - /// The imperative API equivalent to [`InteractiveElement::on_action`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - #[track_caller] - pub fn on_action(&mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static) { - self.action_listeners.push(( - TypeId::of::(), - Box::new(move |action, phase, window, cx| { - let action = action.downcast_ref().unwrap(); - if phase == DispatchPhase::Bubble { - (listener)(action, window, cx) - } - }), - )); - } - - /// Bind the given callback to an action dispatch, based on a dynamic action parameter - /// instead of a type parameter. Useful for component libraries that want to expose - /// action bindings to their users. - /// The imperative API equivalent to [`InteractiveElement::on_boxed_action`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_boxed_action( - &mut self, - action: &dyn Action, - listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static, - ) { - let action = action.boxed_clone(); - self.action_listeners.push(( - (*action).type_id(), - Box::new(move |_, phase, window, cx| { - if phase == DispatchPhase::Bubble { - (listener)(&*action, window, cx) - } - }), - )); - } - - /// Bind the given callback to key down events during the bubble phase. - /// The imperative API equivalent to [`InteractiveElement::on_key_down`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_key_down( - &mut self, - listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static, - ) { - self.key_down_listeners - .push(Box::new(move |event, phase, window, cx| { - if phase == DispatchPhase::Bubble { - (listener)(event, window, cx) - } - })); - } - - /// Bind the given callback to key down events during the capture phase. - /// The imperative API equivalent to [`InteractiveElement::capture_key_down`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn capture_key_down( - &mut self, - listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static, - ) { - self.key_down_listeners - .push(Box::new(move |event, phase, window, cx| { - if phase == DispatchPhase::Capture { - listener(event, window, cx) - } - })); - } - - /// Bind the given callback to key up events during the bubble phase. - /// The imperative API equivalent to [`InteractiveElement::on_key_up`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static) { - self.key_up_listeners - .push(Box::new(move |event, phase, window, cx| { - if phase == DispatchPhase::Bubble { - listener(event, window, cx) - } - })); - } - - /// Bind the given callback to key up events during the capture phase. - /// The imperative API equivalent to [`InteractiveElement::on_key_up`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn capture_key_up( - &mut self, - listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static, - ) { - self.key_up_listeners - .push(Box::new(move |event, phase, window, cx| { - if phase == DispatchPhase::Capture { - listener(event, window, cx) - } - })); - } - - /// Bind the given callback to modifiers changing events. - /// The imperative API equivalent to [`InteractiveElement::on_modifiers_changed`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_modifiers_changed( - &mut self, - listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static, - ) { - self.modifiers_changed_listeners - .push(Box::new(move |event, window, cx| { - listener(event, window, cx) - })); - } - - /// Bind the given callback to drop events of the given type, whether or not the drag started on this element. - /// The imperative API equivalent to [`InteractiveElement::on_drop`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_drop(&mut self, listener: impl Fn(&T, &mut Window, &mut App) + 'static) { - self.drop_listeners.push(( - TypeId::of::(), - Box::new(move |dragged_value, window, cx| { - listener(dragged_value.downcast_ref().unwrap(), window, cx); - }), - )); - } - - /// Use the given predicate to determine whether or not a drop event should be dispatched to this element. - /// The imperative API equivalent to [`InteractiveElement::can_drop`]. - pub fn can_drop( - &mut self, - predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static, - ) { - self.can_drop_predicate = Some(Box::new(predicate)); - } - - /// Bind the given callback to click events of this element. - /// The imperative API equivalent to [`StatefulInteractiveElement::on_click`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) - where - Self: Sized, - { - self.click_listeners.push(Rc::new(move |event, window, cx| { - listener(event, window, cx) - })); - } - - /// Bind the given callback to non-primary click events of this element. - /// The imperative API equivalent to [`StatefulInteractiveElement::on_aux_click`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_aux_click(&mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) - where - Self: Sized, - { - self.aux_click_listeners - .push(Rc::new(move |event, window, cx| { - listener(event, window, cx) - })); - } - - /// On drag initiation, this callback will be used to create a new view to render the dragged value for a - /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with - /// the [`Self::on_drag_move`] API. - /// The imperative API equivalent to [`StatefulInteractiveElement::on_drag`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_drag( - &mut self, - value: T, - constructor: impl Fn(&T, Point, &mut Window, &mut App) -> Entity + 'static, - ) where - Self: Sized, - T: 'static, - W: 'static + Render, - { - debug_assert!( - self.drag_listener.is_none(), - "calling on_drag more than once on the same element is not supported" - ); - self.drag_listener = Some(DragListener { - value: Arc::new(value), - render: Box::new(move |value, offset, window, cx| { - constructor(value.downcast_ref().unwrap(), offset, window, cx).into() - }), - external_payload: None, - }); - } - - /// Registers a callback resolving a payload to offer the platform if a drag started by this - /// element leaves the window. It is invoked at most once per drag gesture, when the pointer - /// exits the viewport. Must be called after [`Self::on_drag`], with the same dragged value - /// type `T`. - pub fn external_drag_payload( - &mut self, - resolver: impl Fn(&T, &mut Window, &mut App) -> Option + 'static, - ) where - Self: Sized, - T: 'static, - { - let Some(drag_listener) = self.drag_listener.as_mut() else { - debug_assert!(false, "external_drag_payload must be called after on_drag"); - return; - }; - debug_assert!( - drag_listener.value.as_ref().type_id() == TypeId::of::(), - "external_drag_payload must use the same dragged value type as on_drag" - ); - debug_assert!( - drag_listener.external_payload.is_none(), - "calling external_drag_payload more than once on the same element is not supported" - ); - drag_listener.external_payload = Some(Box::new(move |value, window, cx| { - resolver(value.downcast_ref::()?, window, cx) - })); - } - - /// Bind the given callback on the hover start and end events of this element. Note that the boolean - /// passed to the callback is true when the hover starts and false when it ends. - /// Transitions caused by layout changes under a stationary mouse also invoke the callback. - /// - /// By default, keyboard input suppresses hover until the next mouse move, mouse down, or touch. Set - /// [`HoverListenerMode::InputModalityIndependent`] with [`Self::hover_listener_mode`] to - /// continue hit-testing hover after keyboard input. - /// The imperative API equivalent to [`StatefulInteractiveElement::on_hover`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static) - where - Self: Sized, - { - debug_assert!( - self.hover_listener.is_none(), - "calling on_hover more than once on the same element is not supported" - ); - self.hover_listener = Some(Box::new(listener)); - } - - /// Sets how [`Self::on_hover`] responds to key presses while the mouse is stationary. - /// This affects only the hover listener, not hover styles or tooltips. The imperative API - /// equivalent to [`StatefulInteractiveElement::hover_listener_mode`]. - pub fn hover_listener_mode(&mut self, mode: HoverListenerMode) - where - Self: Sized, - { - self.hover_listener_mode = mode; - } - - /// Use the given callback to construct a new tooltip view when the mouse hovers over this element. - /// The imperative API equivalent to [`StatefulInteractiveElement::tooltip`]. - pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) - where - Self: Sized, - { - debug_assert!( - self.tooltip_builder.is_none(), - "calling tooltip more than once on the same element is not supported" - ); - self.tooltip_builder = Some(TooltipBuilder { - build: Rc::new(build_tooltip), - hoverable: false, - }); - } - - /// Use the given callback to construct a new tooltip view when the mouse hovers over this element. - /// The tooltip itself is also hoverable and won't disappear when the user moves the mouse into - /// the tooltip. The imperative API equivalent to [`StatefulInteractiveElement::hoverable_tooltip`]. - pub fn hoverable_tooltip( - &mut self, - build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static, - ) where - Self: Sized, - { - debug_assert!( - self.tooltip_builder.is_none(), - "calling tooltip more than once on the same element is not supported" - ); - self.tooltip_builder = Some(TooltipBuilder { - build: Rc::new(build_tooltip), - hoverable: true, - }); - } - - /// Set the delay before this element's tooltip is shown. - /// The imperative API equivalent to [`StatefulInteractiveElement::tooltip_show_delay`]. - pub fn tooltip_show_delay(&mut self, delay: Duration) { - self.tooltip_show_delay = Some(delay); - } - - /// Block the mouse from all interactions with elements behind this element's hitbox. Typically - /// `block_mouse_except_scroll` should be preferred. - /// - /// The imperative API equivalent to [`InteractiveElement::occlude`] - pub fn occlude_mouse(&mut self) { - self.hitbox_behavior = HitboxBehavior::BlockMouse; - } - - /// Set the bounds of this element as a window control area for the platform window. - /// The imperative API equivalent to [`InteractiveElement::window_control_area`] - pub fn window_control_area(&mut self, area: WindowControlArea) { - self.window_control = Some(area); - } - - /// Block non-scroll mouse interactions with elements behind this element's hitbox. - /// The imperative API equivalent to [`InteractiveElement::block_mouse_except_scroll`]. - /// - /// See [`Hitbox::is_hovered`] for details. - pub fn block_mouse_except_scroll(&mut self) { - self.hitbox_behavior = HitboxBehavior::BlockMouseExceptScroll; - } - - fn has_pinch_listeners(&self) -> bool { - !self.pinch_listeners.is_empty() - } -} - -/// A trait for elements that want to use the standard GPUI event handlers that don't -/// require any state. -pub trait InteractiveElement: Sized { - /// Retrieve the interactivity state associated with this element - fn interactivity(&mut self) -> &mut Interactivity; - - /// Assign this element to a group of elements that can be styled together - fn group(mut self, group: impl Into) -> Self { - self.interactivity().group = Some(group.into()); - self - } - - /// Assign this element an ID, so that it can be used with interactivity - fn id(mut self, id: impl Into) -> Stateful { - self.interactivity().element_id = Some(id.into()); - - Stateful { element: self } - } - - /// Track the focus state of the given focus handle on this element. - /// If the focus handle is focused by the application, this element will - /// apply its focused styles. - fn track_focus(mut self, focus_handle: &FocusHandle) -> Self { - self.interactivity().focusable = true; - self.interactivity().tracked_focus_handle = Some(focus_handle.clone()); - self - } - - /// Set whether this element is a tab stop. - /// - /// When false, the element remains in tab-index order but cannot be reached via keyboard navigation. - /// Useful for container elements: focus the container, then call `window.focus_next(cx)` to focus - /// the first tab stop inside it while having the container element itself be unreachable via the keyboard. - /// Should only be used with `tab_index`. - fn tab_stop(mut self, tab_stop: bool) -> Self { - self.interactivity().tab_stop = tab_stop; - self - } - - /// Set index of the tab stop order, and set this node as a tab stop. - /// This will default the element to being a tab stop. See [`Self::tab_stop`] for more information. - /// This should only be used in conjunction with `tab_group` - /// in order to not interfere with the tab index of other elements. - fn tab_index(mut self, index: isize) -> Self { - self.interactivity().focusable = true; - self.interactivity().tab_index = Some(index); - self.interactivity().tab_stop = true; - self - } - - /// Designate this div as a "tab group". Tab groups have their own location in the tab-index order, - /// but for children of the tab group, the tab index is reset to 0. This can be useful for swapping - /// the order of tab stops within the group, without having to renumber all the tab stops in the whole - /// application. - fn tab_group(mut self) -> Self { - self.interactivity().tab_group = true; - if self.interactivity().tab_index.is_none() { - self.interactivity().tab_index = Some(0); - } - self - } - - /// Set the keymap context for this element. This will be used to determine - /// which action to dispatch from the keymap. - fn key_context(mut self, key_context: C) -> Self - where - C: TryInto, - E: std::fmt::Display, - { - if let Some(key_context) = key_context.try_into().log_err() { - self.interactivity().key_context = Some(key_context); - } - self - } - - /// Apply the given style to this element when the mouse hovers over it - fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self { - debug_assert!( - self.interactivity().hover_style.is_none(), - "hover style already set" - ); - self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default()))); - self - } - - /// Apply the given style to this element when the mouse hovers over a group member - fn group_hover( - mut self, - group_name: impl Into, - f: impl FnOnce(StyleRefinement) -> StyleRefinement, - ) -> Self { - self.interactivity().group_hover_style = Some(GroupStyle { - group: group_name.into(), - style: Box::new(f(StyleRefinement::default())), - }); - self - } - - /// Bind the given callback to the mouse down event for the given mouse button. - /// The fluent API equivalent to [`Interactivity::on_mouse_down`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to the view state from this callback. - fn on_mouse_down( - mut self, - button: MouseButton, - listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_mouse_down(button, listener); - self - } - - #[cfg(any(test, feature = "test-support"))] - /// Set a key that can be used to look up this element's bounds - /// in the [`crate::VisualTestContext::debug_bounds`] map - /// This is a noop in release builds - fn debug_selector(mut self, f: impl FnOnce() -> String) -> Self { - self.interactivity().debug_selector = Some(f()); - self - } - - #[cfg(not(any(test, feature = "test-support")))] - /// Set a key that can be used to look up this element's bounds - /// in the [`crate::VisualTestContext::debug_bounds`] map - /// This is a noop in release builds - #[inline] - fn debug_selector(self, _: impl FnOnce() -> String) -> Self { - self - } - - /// Bind the given callback to the mouse down event for any button, during the capture phase. - /// The fluent API equivalent to [`Interactivity::capture_any_mouse_down`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn capture_any_mouse_down( - mut self, - listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().capture_any_mouse_down(listener); - self - } - - /// Bind the given callback to the mouse down event for any button, during the capture phase. - /// The fluent API equivalent to [`Interactivity::on_any_mouse_down`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_any_mouse_down( - mut self, - listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_any_mouse_down(listener); - self - } - - /// Bind the given callback to the mouse up event for the given button, during the bubble phase. - /// The fluent API equivalent to [`Interactivity::on_mouse_up`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_mouse_up( - mut self, - button: MouseButton, - listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_mouse_up(button, listener); - self - } - - /// Bind the given callback to the mouse up event for any button, during the capture phase. - /// The fluent API equivalent to [`Interactivity::capture_any_mouse_up`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn capture_any_mouse_up( - mut self, - listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().capture_any_mouse_up(listener); - self - } - - /// Bind the given callback to the mouse pressure event, during the bubble phase - /// the fluent API equivalent to [`Interactivity::on_mouse_pressure`] - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_mouse_pressure( - mut self, - listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_mouse_pressure(listener); - self - } - - /// Bind the given callback to the mouse pressure event, during the capture phase - /// the fluent API equivalent to [`Interactivity::on_mouse_pressure`] - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn capture_mouse_pressure( - mut self, - listener: impl Fn(&MousePressureEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().capture_mouse_pressure(listener); - self - } - - /// Bind the given callback to the mouse down event, on any button, during the capture phase, - /// when the mouse is outside of the bounds of this element. - /// The fluent API equivalent to [`Interactivity::on_mouse_down_out`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_mouse_down_out( - mut self, - listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_mouse_down_out(listener); - self - } - - /// Bind the given callback to the mouse up event, for the given button, during the capture phase, - /// when the mouse is outside of the bounds of this element. - /// The fluent API equivalent to [`Interactivity::on_mouse_up_out`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_mouse_up_out( - mut self, - button: MouseButton, - listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_mouse_up_out(button, listener); - self - } - - /// Bind the given callback to the mouse move event, during the bubble phase. - /// The fluent API equivalent to [`Interactivity::on_mouse_move`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_mouse_move( - mut self, - listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_mouse_move(listener); - self - } - - /// Bind the given callback to the mouse exit event, during the bubble phase. - /// The fluent API equivalent to [`Interactivity::on_mouse_exit`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_mouse_exit( - mut self, - listener: impl Fn(&MouseExitEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_mouse_exit(listener); - self - } - - /// Bind the given callback to the mouse drag event of the given type. Note that this - /// will be called for all move events, inside or outside of this element, as long as the - /// drag was started with this element under the mouse. Useful for implementing draggable - /// UIs that don't conform to a drag and drop style interaction, like resizing. - /// The fluent API equivalent to [`Interactivity::on_drag_move`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_drag_move( - mut self, - listener: impl Fn(&DragMoveEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_drag_move(listener); - self - } - - /// Bind the given callback to scroll wheel events during the bubble phase. - /// The fluent API equivalent to [`Interactivity::on_scroll_wheel`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_scroll_wheel( - mut self, - listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_scroll_wheel(listener); - self - } - - /// Bind the given callback to pinch gesture events during the bubble phase. - /// The fluent API equivalent to [`Interactivity::on_pinch`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_pinch(mut self, listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static) -> Self { - self.interactivity().on_pinch(listener); - self - } - - /// Bind the given callback to pinch gesture events during the capture phase. - /// The fluent API equivalent to [`Interactivity::capture_pinch`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn capture_pinch( - mut self, - listener: impl Fn(&PinchEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().capture_pinch(listener); - self - } - /// Capture the given action, before normal action dispatch can fire. - /// The fluent API equivalent to [`Interactivity::capture_action`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn capture_action( - mut self, - listener: impl Fn(&A, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().capture_action(listener); - self - } - - /// Bind the given callback to an action dispatch during the bubble phase. - /// The fluent API equivalent to [`Interactivity::on_action`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - #[track_caller] - fn on_action( - mut self, - listener: impl Fn(&A, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_action(listener); - self - } - - /// Bind the given callback to an action dispatch, based on a dynamic action parameter - /// instead of a type parameter. Useful for component libraries that want to expose - /// action bindings to their users. - /// The fluent API equivalent to [`Interactivity::on_boxed_action`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_boxed_action( - mut self, - action: &dyn Action, - listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_boxed_action(action, listener); - self - } - - /// Bind the given callback to key down events during the bubble phase. - /// The fluent API equivalent to [`Interactivity::on_key_down`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_key_down( - mut self, - listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_key_down(listener); - self - } - - /// Bind the given callback to key down events during the capture phase. - /// The fluent API equivalent to [`Interactivity::capture_key_down`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn capture_key_down( - mut self, - listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().capture_key_down(listener); - self - } - - /// Bind the given callback to key up events during the bubble phase. - /// The fluent API equivalent to [`Interactivity::on_key_up`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_key_up( - mut self, - listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_key_up(listener); - self - } - - /// Bind the given callback to key up events during the capture phase. - /// The fluent API equivalent to [`Interactivity::capture_key_up`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn capture_key_up( - mut self, - listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().capture_key_up(listener); - self - } - - /// Bind the given callback to modifiers changing events. - /// The fluent API equivalent to [`Interactivity::on_modifiers_changed`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_modifiers_changed( - mut self, - listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_modifiers_changed(listener); - self - } - - /// Apply the given style when the given data type is dragged over this element - fn drag_over( - mut self, - f: impl 'static + Fn(StyleRefinement, &S, &mut Window, &mut App) -> StyleRefinement, - ) -> Self { - self.interactivity().drag_over_styles.push(( - TypeId::of::(), - Box::new(move |currently_dragged: &dyn Any, window, cx| { - f( - StyleRefinement::default(), - currently_dragged.downcast_ref::().unwrap(), - window, - cx, - ) - }), - )); - self - } - - /// Apply the given style when the given data type is dragged over this element's group - fn group_drag_over( - mut self, - group_name: impl Into, - f: impl FnOnce(StyleRefinement) -> StyleRefinement, - ) -> Self { - self.interactivity().group_drag_over_styles.push(( - TypeId::of::(), - GroupStyle { - group: group_name.into(), - style: Box::new(f(StyleRefinement::default())), - }, - )); - self - } - - /// Bind the given callback to drop events of the given type, whether or not the drag started on this element. - /// The fluent API equivalent to [`Interactivity::on_drop`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_drop( - mut self, - listener: impl Fn(&T, &mut Window, &mut App) + 'static, - ) -> Self { - self.interactivity().on_drop(listener); - self - } - - /// Use the given predicate to determine whether or not a drop event should be dispatched to this element. - /// The fluent API equivalent to [`Interactivity::can_drop`]. - fn can_drop( - mut self, - predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static, - ) -> Self { - self.interactivity().can_drop(predicate); - self - } - - /// Block the mouse from all interactions with elements behind this element's hitbox. Typically - /// `block_mouse_except_scroll` should be preferred. - /// The fluent API equivalent to [`Interactivity::occlude_mouse`]. - fn occlude(mut self) -> Self { - self.interactivity().occlude_mouse(); - self - } - - /// Set the bounds of this element as a window control area for the platform window. - /// The fluent API equivalent to [`Interactivity::window_control_area`]. - fn window_control_area(mut self, area: WindowControlArea) -> Self { - self.interactivity().window_control_area(area); - self - } - - /// Block non-scroll mouse interactions with elements behind this element's hitbox. - /// The fluent API equivalent to [`Interactivity::block_mouse_except_scroll`]. - /// - /// See [`Hitbox::is_hovered`] for details. - fn block_mouse_except_scroll(mut self) -> Self { - self.interactivity().block_mouse_except_scroll(); - self - } - - /// Set the given styles to be applied when this element, specifically, is focused. - /// Requires that the element is focusable. Elements can be made focusable using [`InteractiveElement::track_focus`]. - fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self - where - Self: Sized, - { - self.interactivity().focus_style = Some(Box::new(f(StyleRefinement::default()))); - self - } - - /// Set the given styles to be applied when this element is inside another element that is focused. - /// Requires that the element is focusable. Elements can be made focusable using [`InteractiveElement::track_focus`]. - fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self - where - Self: Sized, - { - self.interactivity().in_focus_style = Some(Box::new(f(StyleRefinement::default()))); - self - } - - /// Set the given styles to be applied when this element is focused via keyboard navigation. - /// This is similar to CSS's `:focus-visible` pseudo-class - it only applies when the element - /// is focused AND the user is navigating via keyboard (not mouse clicks). - /// Requires that the element is focusable. Elements can be made focusable using [`InteractiveElement::track_focus`]. - fn focus_visible(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self - where - Self: Sized, - { - self.interactivity().focus_visible_style = Some(Box::new(f(StyleRefinement::default()))); - self - } -} - -/// A trait for elements that want to use the standard GPUI interactivity features -/// that require state. -pub trait StatefulInteractiveElement: InteractiveElement { - /// Set the accessible role for this element. - /// - /// See the [accessibility guide](crate::_accessibility) for an overview. - fn role(mut self, role: accesskit::Role) -> Self { - debug_assert!( - role != accesskit::Role::GenericContainer, - "GenericContainer is filtered out of the a11y tree and has no effect" - ); - self.interactivity().override_role = Some(role); - self - } - - /// Set the author-provided identifier exposed to accessibility clients. - /// - /// Unlike the GPUI element ID, this value is visible outside the process. - /// Keep it stable and unique within its accessibility tree. - /// AccessKit maps it to platform identifiers where supported, including - /// UIA `AutomationId` on Windows, `AXIdentifier` on macOS, and AT-SPI - /// `AccessibleId` on Linux stacks whose deployed adapter exposes it. - fn accessibility_id(mut self, id: impl Into) -> Self { - self.interactivity().aria.author_id = Some(id.into()); - self - } - - /// Set the accessible label for this element. - fn aria_label(mut self, label: impl Into) -> Self { - self.interactivity().aria.label = Some(label.into()); - self - } - - /// Set the accessible description for this element. Unlike the label (which - /// names the element), the description provides supplementary information - /// that assistive technology announces after the name, role, and value - - /// for example a settings subtitle or a hint. - fn aria_description(mut self, description: impl Into) -> Self { - self.interactivity().aria.description = Some(description.into()); - self - } - - /// Set the keyboard shortcut(s) that activate this element, announced by - /// assistive technology (maps to AccessKit's `keyboard_shortcut`). - /// - /// Note that this does not create a keymap. It simply instructs assistive - /// technology what the keymap is. - fn aria_keyshortcuts(mut self, keyshortcuts: impl Into) -> Self { - self.interactivity().aria.keyshortcuts = Some(keyshortcuts.into()); - self - } - - /// Report this element as the focused node in the accessibility tree, - /// overriding the element that holds real keyboard focus — but only while - /// one of its ancestors actually holds focus. - /// - /// This implements the `aria-activedescendant` pattern for composite - /// widgets that keep keyboard focus on a container (e.g. a menu or - /// listbox) while a child is "selected": set this on the selected child so - /// assistive technology announces and highlights it as focused. - /// - /// The element must also have a [`role`][Self::role] (and an id) so it - /// produces an accessibility node. Unlike the web's container-side - /// `aria-activedescendant`, this is set on the descendant; GPUI honors it - /// only when a focused ancestor is present in the tree, so it is safe to - /// set unconditionally on the selected child — if the container isn't - /// focused, the claim is ignored. - fn aria_active_descendant(mut self) -> Self { - self.interactivity().report_active_descendant_focus = true; - self - } - - /// Contribute synthetic accessibility nodes — nodes that don't correspond - /// to any element — as children of this element's a11y node. For example, - /// text runs describing an editor's text content. - /// - /// The closure is called after this element is prepainted, and only if it - /// contributed a node to the accessibility tree (i.e. it has an id and a - /// [`role`][StatefulInteractiveElement::role]). - /// - /// See [`Element::a11y_synthetic_children`] for details. - fn a11y_synthetic_children( - mut self, - f: impl FnOnce(&mut crate::A11ySubtreeBuilder) + 'static, - ) -> Self { - self.interactivity().a11y_synthetic_children = Some(Box::new(f)); - self - } - - /// Set the selected state for this element. - fn aria_selected(mut self, selected: bool) -> Self { - self.interactivity().aria.selected = Some(selected); - self - } - - /// Set the ARIA current-value state for this element. - /// - /// AccessKit 0.24 carries `aria-current`, but the published gpui-pre - /// 0.3.3 builder surface omitted the corresponding setter. Keep the - /// extension next to the other accessibility builders so callers can - /// expose current-page/current-step semantics without inventing a second - /// node or mutating the AccessKit tree outside the element. - fn aria_current(mut self, current: accesskit::AriaCurrent) -> Self { - self.interactivity().aria.current = Some(current); - self - } - - /// Set the expanded state for this element. - fn aria_expanded(mut self, expanded: bool) -> Self { - self.interactivity().aria.expanded = Some(expanded); - self - } - - /// Set the toggled state for this element. - fn aria_toggled(mut self, toggled: accesskit::Toggled) -> Self { - self.interactivity().aria.toggled = Some(toggled); - self - } - - /// Set the numeric value for this element. - fn aria_numeric_value(mut self, value: f64) -> Self { - self.interactivity().aria.numeric_value = Some(value); - self - } - - /// Set the step by which assistive technology should expect the numeric - /// value of this element to change (e.g. when incrementing a spin button). - fn aria_numeric_value_step(mut self, step: f64) -> Self { - self.interactivity().aria.numeric_value_step = Some(step); - self - } - - /// Set the string value of this element, e.g. the text content of a simple - /// text input. - fn aria_value(mut self, value: impl Into) -> Self { - self.interactivity().aria.value = Some(value.into()); - self - } - - /// Set the placeholder text reported to assistive technology for this - /// element, shown when a text input is empty. - fn aria_placeholder(mut self, placeholder: impl Into) -> Self { - self.interactivity().aria.placeholder = Some(placeholder.into()); - self - } - - /// Set the minimum numeric value for this element. - fn aria_min_numeric_value(mut self, value: f64) -> Self { - self.interactivity().aria.min_numeric_value = Some(value); - self - } - - /// Set the maximum numeric value for this element. - fn aria_max_numeric_value(mut self, value: f64) -> Self { - self.interactivity().aria.max_numeric_value = Some(value); - self - } - - /// Set the orientation of this element. - fn aria_orientation(mut self, orientation: accesskit::Orientation) -> Self { - self.interactivity().aria.orientation = Some(orientation); - self - } - - /// Set the heading level of this element. - fn aria_level(mut self, level: usize) -> Self { - self.interactivity().aria.level = Some(level); - self - } - - /// Set the position in set of this element. - fn aria_position_in_set(mut self, position: usize) -> Self { - self.interactivity().aria.position_in_set = Some(position); - self - } - - /// Set the size of set for this element. - fn aria_size_of_set(mut self, size: usize) -> Self { - self.interactivity().aria.size_of_set = Some(size); - self - } - - /// Set the row index for this element. - fn aria_row_index(mut self, index: usize) -> Self { - self.interactivity().aria.row_index = Some(index); - self - } - - /// Set the column index for this element. - fn aria_column_index(mut self, index: usize) -> Self { - self.interactivity().aria.column_index = Some(index); - self - } - - /// Set the row count for this element. - fn aria_row_count(mut self, count: usize) -> Self { - self.interactivity().aria.row_count = Some(count); - self - } - - /// Set the column count for this element. - fn aria_column_count(mut self, count: usize) -> Self { - self.interactivity().aria.column_count = Some(count); - self - } - - /// Register a handler for an accessibility action on this element. - /// The handler is called when a screen reader requests the given action. - /// - /// See the [accessibility guide](crate::_accessibility) for an overview. - fn on_a11y_action( - mut self, - action: accesskit::Action, - listener: impl FnMut(Option<&accesskit::ActionData>, &mut crate::Window, &mut crate::App) - + 'static, - ) -> Self { - self.interactivity() - .a11y_action_listeners - .push((action, Box::new(listener))); - self - } - - /// Set this element to focusable. - fn focusable(mut self) -> Self { - self.interactivity().focusable = true; - self - } - - /// Set the overflow x and y to scroll. - fn overflow_scroll(mut self) -> Self { - self.interactivity().base_style.overflow.x = Some(Overflow::Scroll); - self.interactivity().base_style.overflow.y = Some(Overflow::Scroll); - self - } - - /// Set the overflow x to scroll. - fn overflow_x_scroll(mut self) -> Self { - self.interactivity().base_style.overflow.x = Some(Overflow::Scroll); - self - } - - /// Set the overflow y to scroll. - fn overflow_y_scroll(mut self) -> Self { - self.interactivity().base_style.overflow.y = Some(Overflow::Scroll); - self - } - - /// Restrict scrolling of this element to the axis of the input gesture. - /// - /// See [`Style::restrict_scroll_to_axis`](crate::Style::restrict_scroll_to_axis) for details. - fn restrict_scroll_to_axis(mut self) -> Self { - self.interactivity().base_style.restrict_scroll_to_axis = Some(true); - self - } - - /// Track the scroll state of this element with the given handle. - fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self { - self.interactivity().tracked_scroll_handle = Some(scroll_handle.clone()); - self - } - - /// Track the scroll state of this element with the given handle. - fn anchor_scroll(mut self, scroll_anchor: Option) -> Self { - self.interactivity().scroll_anchor = scroll_anchor; - self - } - - /// Set the given styles to be applied when this element is active. - fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self - where - Self: Sized, - { - self.interactivity().active_style = Some(Box::new(f(StyleRefinement::default()))); - self - } - - /// Set the given styles to be applied when this element's group is active. - fn group_active( - mut self, - group_name: impl Into, - f: impl FnOnce(StyleRefinement) -> StyleRefinement, - ) -> Self - where - Self: Sized, - { - self.interactivity().group_active_style = Some(GroupStyle { - group: group_name.into(), - style: Box::new(f(StyleRefinement::default())), - }); - self - } - - /// Bind the given callback to click events of this element. - /// The fluent API equivalent to [`Interactivity::on_click`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self - where - Self: Sized, - { - self.interactivity().on_click(listener); - self - } - - /// Bind the given callback to non-primary click events of this element. - /// The fluent API equivalent to [`Interactivity::on_aux_click`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_aux_click( - mut self, - listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, - ) -> Self - where - Self: Sized, - { - self.interactivity().on_aux_click(listener); - self - } - - /// On drag initiation, this callback will be used to create a new view to render the dragged value for a - /// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with - /// the [`InteractiveElement::on_drag_move`] API. - /// The callback also has access to the offset of triggering click from the origin of parent element. - /// The fluent API equivalent to [`Interactivity::on_drag`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_drag( - mut self, - value: T, - constructor: impl Fn(&T, Point, &mut Window, &mut App) -> Entity + 'static, - ) -> Self - where - Self: Sized, - T: 'static, - W: 'static + Render, - { - self.interactivity().on_drag(value, constructor); - self - } - - /// Registers a callback resolving a payload to offer the platform if a drag started by this - /// element leaves the window. It is invoked at most once per drag gesture, when the pointer - /// exits the viewport. Must be called after [`Self::on_drag`], with the same dragged value - /// type `T`. - /// The fluent API equivalent to [`Interactivity::external_drag_payload`]. - fn external_drag_payload( - mut self, - resolver: impl Fn(&T, &mut Window, &mut App) -> Option + 'static, - ) -> Self - where - Self: Sized, - T: 'static, - { - self.interactivity().external_drag_payload(resolver); - self - } - - /// Bind the given callback on the hover start and end events of this element. Note that the boolean - /// passed to the callback is true when the hover starts and false when it ends. - /// Transitions caused by layout changes under a stationary mouse also invoke the callback. - /// - /// By default, keyboard input suppresses hover until the next mouse move, mouse down, or touch. Set - /// [`HoverListenerMode::InputModalityIndependent`] with [`Self::hover_listener_mode`] to - /// continue hit-testing hover after keyboard input. - /// The fluent API equivalent to [`Interactivity::on_hover`]. - /// - /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback. - fn on_hover(mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self - where - Self: Sized, - { - self.interactivity().on_hover(listener); - self - } - - /// Sets how [`Self::on_hover`] responds to key presses while the mouse is stationary. - /// This affects only the hover listener, not hover styles or tooltips. The fluent API - /// equivalent to [`Interactivity::hover_listener_mode`]. - fn hover_listener_mode(mut self, mode: HoverListenerMode) -> Self - where - Self: Sized, - { - self.interactivity().hover_listener_mode(mode); - self - } - - /// Use the given callback to construct a new tooltip view when the mouse hovers over this element. - /// The fluent API equivalent to [`Interactivity::tooltip`]. - fn tooltip(mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self - where - Self: Sized, - { - self.interactivity().tooltip(build_tooltip); - self - } - - /// Use the given callback to construct a new tooltip view when the mouse hovers over this element. - /// The tooltip itself is also hoverable and won't disappear when the user moves the mouse into - /// the tooltip. The fluent API equivalent to [`Interactivity::hoverable_tooltip`]. - fn hoverable_tooltip( - mut self, - build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static, - ) -> Self - where - Self: Sized, - { - self.interactivity().hoverable_tooltip(build_tooltip); - self - } - - /// Set the delay before this element's tooltip is shown. - /// The fluent API equivalent to [`Interactivity::tooltip_show_delay`]. - fn tooltip_show_delay(mut self, delay: Duration) -> Self - where - Self: Sized, - { - self.interactivity().tooltip_show_delay(delay); - self - } -} - -pub(crate) type MouseDownListener = - Box; -pub(crate) type MouseUpListener = - Box; -pub(crate) type MousePressureListener = - Box; -pub(crate) type MouseMoveListener = - Box; -pub(crate) type MouseExitListener = - Box; - -pub(crate) type ScrollWheelListener = - Box; - -pub(crate) type PinchListener = - Box; - -pub(crate) type ClickListener = Rc; - -/// Controls how [`StatefulInteractiveElement::on_hover`] responds to key presses while the mouse -/// is stationary. -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub enum HoverListenerMode { - /// Use input-modality-aware hit testing. Keyboard input suppresses hover until the mouse moves - /// again, unless pointer capture or an active mouse-down interaction keeps the listener hovered. - #[default] - InputModalityAware, - /// Use hit testing even when the last input was from the keyboard. This changes only - /// keyboard-modality filtering; all other [`StatefulInteractiveElement::on_hover`] behavior - /// remains unchanged. - InputModalityIndependent, -} - -impl HoverListenerMode { - fn is_hovered(self, hitbox: &Hitbox, window: &Window) -> bool { - match self { - Self::InputModalityAware => hitbox.is_hovered(window), - Self::InputModalityIndependent => hitbox.id.is_hovered_ignoring_last_input(window), - } - } -} - -pub(crate) struct DragListener { - value: Arc, - render: Box, &mut Window, &mut App) -> AnyView + 'static>, - external_payload: Option, -} - -type ExternalDragPayloadResolver = - Box Option + 'static>; - -type DropListener = Box; - -type CanDropPredicate = Box bool + 'static>; - -pub(crate) struct TooltipBuilder { - build: Rc AnyView + 'static>, - hoverable: bool, -} - -pub(crate) type KeyDownListener = - Box; - -pub(crate) type KeyUpListener = - Box; - -pub(crate) type ModifiersChangedListener = - Box; - -pub(crate) type ActionListener = - Box; - -/// Construct a new [`Div`] element -#[track_caller] -pub fn div() -> Div { - Div { - interactivity: Interactivity::new(), - children: SmallVec::default(), - prepaint_listener: None, - image_cache: None, - prepaint_order_fn: None, - } -} - -/// A [`Div`] element, the all-in-one element for building complex UIs in GPUI -pub struct Div { - interactivity: Interactivity, - children: SmallVec<[StackSafe; 2]>, - prepaint_listener: Option>, &mut Window, &mut App) + 'static>>, - image_cache: Option>, - prepaint_order_fn: Option SmallVec<[usize; 8]>>>, -} - -impl Div { - /// Add a listener to be called when the children of this `Div` are prepainted. - /// This allows you to store the [`Bounds`] of the children for later use. - pub fn on_children_prepainted( - mut self, - listener: impl Fn(Vec>, &mut Window, &mut App) + 'static, - ) -> Self { - self.prepaint_listener = Some(Box::new(listener)); - self - } - - /// Add an image cache at the location of this div in the element tree. - pub fn image_cache(mut self, cache: impl ImageCacheProvider) -> Self { - self.image_cache = Some(Box::new(cache)); - self - } - - /// Specify a function that determines the order in which children are prepainted. - /// - /// The function is called at prepaint time and should return a vector of child indices - /// in the desired prepaint order. Each index should appear exactly once. - /// - /// This is useful when the prepaint of one child affects state that another child reads. - /// For example, in split editor views, the editor with an autoscroll request should - /// be prepainted first so its scroll position update is visible to the other editor. - pub fn with_dynamic_prepaint_order( - mut self, - order_fn: impl Fn(&mut Window, &mut App) -> SmallVec<[usize; 8]> + 'static, - ) -> Self { - self.prepaint_order_fn = Some(Box::new(order_fn)); - self - } -} - -/// A frame state for a `Div` element, which contains layout IDs for its children. -/// -/// This struct is used internally by the `Div` element to manage the layout state of its children -/// during the UI update cycle. It holds a small vector of `LayoutId` values, each corresponding to -/// a child element of the `Div`. These IDs are used to query the layout engine for the computed -/// bounds of the children after the layout phase is complete. -pub struct DivFrameState { - child_layout_ids: SmallVec<[LayoutId; 2]>, -} - -/// Interactivity state displayed an manipulated in the inspector. -#[derive(Clone)] -pub struct DivInspectorState { - /// The inspected element's base style. This is used for both inspecting and modifying the - /// state. In the future it will make sense to separate the read and write, possibly tracking - /// the modifications. - #[cfg(any(feature = "inspector", debug_assertions))] - pub base_style: Box, - /// Inspects the bounds of the element. - pub bounds: Bounds, - /// Size of the children of the element, or `bounds.size` if it has no children. - pub content_size: Size, -} - -impl Styled for Div { - fn style(&mut self) -> &mut StyleRefinement { - &mut self.interactivity.base_style - } -} - -impl InteractiveElement for Div { - fn interactivity(&mut self) -> &mut Interactivity { - &mut self.interactivity - } -} - -impl ParentElement for Div { - fn extend(&mut self, elements: impl IntoIterator) { - #[cfg(feature = "stacker")] - self.children - .extend(elements.into_iter().map(StackSafe::new)); - #[cfg(not(feature = "stacker"))] - self.children.extend(elements); - } -} - -impl Element for Div { - type RequestLayoutState = DivFrameState; - type PrepaintState = Option; - - fn id(&self) -> Option { - self.interactivity.element_id.clone() - } - - fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { - self.interactivity.source_location() - } - - fn a11y_role(&self) -> Option { - // Nodes with `GenericContainer` should never be reported to accesskit. - // Equivalent to an HTML div with no role. - self.interactivity - .override_role - .filter(|role| *role != accesskit::Role::GenericContainer) - } - - fn write_a11y_info(&self, node: &mut accesskit::Node) { - self.interactivity.write_a11y_info(node); - } - - fn a11y_synthetic_children( - &mut self, - _prepaint: &mut Self::PrepaintState, - builder: &mut crate::A11ySubtreeBuilder, - ) { - if let Some(f) = self.interactivity.a11y_synthetic_children.take() { - f(builder); - } - } - - #[cfg_attr(feature = "stacker", stacksafe::stacksafe)] - fn request_layout( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - let mut child_layout_ids = SmallVec::new(); - let image_cache = self - .image_cache - .as_mut() - .map(|provider| provider.provide(window, cx)); - - let layout_id = window.with_image_cache(image_cache, |window| { - self.interactivity.request_layout( - global_id, - inspector_id, - window, - cx, - |style, window, cx| { - window.with_text_style(style.text_style().cloned(), |window| { - child_layout_ids = self - .children - .iter_mut() - .map(|child| child.request_layout(window, cx)) - .collect::>(); - window.request_layout(style, child_layout_ids.iter().copied(), cx) - }) - }, - ) - }); - - (layout_id, DivFrameState { child_layout_ids }) - } - - #[cfg_attr(feature = "stacker", stacksafe::stacksafe)] - fn prepaint( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - request_layout: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Option { - let image_cache = self - .image_cache - .as_mut() - .map(|provider| provider.provide(window, cx)); - - let has_prepaint_listener = self.prepaint_listener.is_some(); - let mut children_bounds = Vec::with_capacity(if has_prepaint_listener { - request_layout.child_layout_ids.len() - } else { - 0 - }); - - let mut child_min = point(Pixels::MAX, Pixels::MAX); - let mut child_max = Point::default(); - if let Some(handle) = self.interactivity.scroll_anchor.as_ref() { - *handle.last_origin.borrow_mut() = bounds.origin - window.element_offset(); - } - let content_size = if request_layout.child_layout_ids.is_empty() { - bounds.size - } else if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() { - let mut state = scroll_handle.0.borrow_mut(); - state.child_bounds = Vec::with_capacity(request_layout.child_layout_ids.len()); - for child_layout_id in &request_layout.child_layout_ids { - let child_bounds = window.layout_bounds(*child_layout_id); - child_min = child_min.min(&child_bounds.origin); - child_max = child_max.max(&child_bounds.bottom_right()); - state.child_bounds.push(child_bounds); - } - (child_max - child_min).into() - } else { - for child_layout_id in &request_layout.child_layout_ids { - let child_bounds = window.layout_bounds(*child_layout_id); - child_min = child_min.min(&child_bounds.origin); - child_max = child_max.max(&child_bounds.bottom_right()); - - if has_prepaint_listener { - children_bounds.push(child_bounds); - } - } - (child_max - child_min).into() - }; - - if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() { - scroll_handle.scroll_to_active_item(); - } - - self.interactivity.prepaint( - global_id, - inspector_id, - bounds, - content_size, - window, - cx, - |style, scroll_offset, hitbox, window, cx| { - // skip children - if style.display == Display::None { - return hitbox; - } - - window.with_image_cache(image_cache, |window| { - window.with_element_offset(scroll_offset, |window| { - if let Some(order_fn) = &self.prepaint_order_fn { - let order = order_fn(window, cx); - for idx in order { - if let Some(child) = self.children.get_mut(idx) { - child.prepaint(window, cx); - } - } - } else { - for child in &mut self.children { - child.prepaint(window, cx); - } - } - }); - - if let Some(listener) = self.prepaint_listener.as_ref() { - listener(children_bounds, window, cx); - } - }); - - hitbox - }, - ) - } - - #[cfg_attr(feature = "stacker", stacksafe::stacksafe)] - fn paint( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - hitbox: &mut Option, - window: &mut Window, - cx: &mut App, - ) { - let image_cache = self - .image_cache - .as_mut() - .map(|provider| provider.provide(window, cx)); - - window.with_image_cache(image_cache, |window| { - self.interactivity.paint( - global_id, - inspector_id, - bounds, - hitbox.as_ref(), - window, - cx, - |style, window, cx| { - // skip children - if style.display == Display::None { - return; - } - - for child in &mut self.children { - child.paint(window, cx); - } - }, - ) - }); - } -} - -impl IntoElement for Div { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -#[derive(Default)] -pub(crate) struct AriaProperties { - pub(crate) author_id: Option, - pub(crate) label: Option, - pub(crate) description: Option, - pub(crate) keyshortcuts: Option, - pub(crate) selected: Option, - pub(crate) current: Option, - pub(crate) expanded: Option, - pub(crate) toggled: Option, - pub(crate) numeric_value: Option, - pub(crate) min_numeric_value: Option, - pub(crate) max_numeric_value: Option, - pub(crate) numeric_value_step: Option, - pub(crate) value: Option, - pub(crate) placeholder: Option, - pub(crate) orientation: Option, - pub(crate) level: Option, - pub(crate) position_in_set: Option, - pub(crate) size_of_set: Option, - pub(crate) row_index: Option, - pub(crate) column_index: Option, - pub(crate) row_count: Option, - pub(crate) column_count: Option, -} - -/// The interactivity struct. Powers all of the general-purpose -/// interactivity in the `Div` element. -#[derive(Default)] -pub struct Interactivity { - /// The element ID of the element. In id is required to support a stateful subset of the interactivity such as on_click. - pub element_id: Option, - /// Whether the element was clicked. This will only be present after layout. - pub active: Option, - /// Whether the element was hovered. This will only be present after paint if an hitbox - /// was created for the interactive element. - pub hovered: Option, - pub(crate) tooltip_id: Option, - pub(crate) content_size: Size, - pub(crate) key_context: Option, - pub(crate) focusable: bool, - pub(crate) tracked_focus_handle: Option, - pub(crate) tracked_scroll_handle: Option, - pub(crate) scroll_anchor: Option, - pub(crate) scroll_offset: Option>>>, - pub(crate) ongoing_scroll: Option>>, - pub(crate) group: Option, - /// The base style of the element, before any modifications are applied - /// by focus, active, etc. - pub base_style: Box, - pub(crate) focus_style: Option>, - pub(crate) in_focus_style: Option>, - pub(crate) focus_visible_style: Option>, - pub(crate) hover_style: Option>, - pub(crate) group_hover_style: Option, - pub(crate) active_style: Option>, - pub(crate) group_active_style: Option, - pub(crate) drag_over_styles: Vec<( - TypeId, - Box StyleRefinement>, - )>, - pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>, - pub(crate) mouse_down_listeners: Vec, - pub(crate) mouse_up_listeners: Vec, - pub(crate) mouse_pressure_listeners: Vec, - pub(crate) mouse_move_listeners: Vec, - pub(crate) mouse_exit_listeners: Vec, - pub(crate) scroll_wheel_listeners: Vec, - pub(crate) pinch_listeners: Vec, - pub(crate) key_down_listeners: Vec, - pub(crate) key_up_listeners: Vec, - pub(crate) modifiers_changed_listeners: Vec, - pub(crate) action_listeners: Vec<(TypeId, ActionListener)>, - pub(crate) drop_listeners: Vec<(TypeId, DropListener)>, - pub(crate) can_drop_predicate: Option, - pub(crate) click_listeners: Vec, - pub(crate) aux_click_listeners: Vec, - pub(crate) drag_listener: Option, - pub(crate) hover_listener: Option>, - pub(crate) hover_listener_mode: HoverListenerMode, - pub(crate) tooltip_builder: Option, - pub(crate) tooltip_show_delay: Option, - pub(crate) window_control: Option, - pub(crate) hitbox_behavior: HitboxBehavior, - pub(crate) tab_index: Option, - pub(crate) tab_group: bool, - pub(crate) tab_stop: bool, - - pub(crate) a11y_action_listeners: - Vec<(accesskit::Action, crate::window::a11y::A11yActionListener)>, - pub(crate) a11y_synthetic_children: Option>, - pub(crate) report_active_descendant_focus: bool, - pub(crate) override_role: Option, - pub(crate) aria: AriaProperties, - - #[cfg(any(feature = "inspector", debug_assertions))] - pub(crate) source_location: Option<&'static core::panic::Location<'static>>, - - #[cfg(any(test, feature = "test-support"))] - pub(crate) debug_selector: Option, -} - -impl Interactivity { - /// Layout this element according to this interactivity state's configured styles - pub fn request_layout( - &mut self, - global_id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - f: impl FnOnce(Style, &mut Window, &mut App) -> LayoutId, - ) -> LayoutId { - #[cfg(any(feature = "inspector", debug_assertions))] - window.with_inspector_state( - _inspector_id, - cx, - |inspector_state: &mut Option, _window| { - if let Some(inspector_state) = inspector_state { - self.base_style = inspector_state.base_style.clone(); - } else { - *inspector_state = Some(DivInspectorState { - base_style: self.base_style.clone(), - bounds: Default::default(), - content_size: Default::default(), - }) - } - }, - ); - - window.with_optional_element_state::( - global_id, - |element_state, window| { - let mut element_state = - element_state.map(|element_state| element_state.unwrap_or_default()); - - if let Some(element_state) = element_state.as_ref() - && cx.has_active_drag() - { - if let Some(pending_mouse_down) = element_state.pending_mouse_down.as_ref() { - *pending_mouse_down.borrow_mut() = None; - } - if let Some(clicked_state) = element_state.clicked_state.as_ref() { - *clicked_state.borrow_mut() = ElementClickedState::default(); - } - } - - // Ensure we store a focus handle in our element state if we're focusable. - // If there's an explicit focus handle we're tracking, use that. Otherwise - // create a new handle and store it in the element state, which lives for as - // as frames contain an element with this id. - if self.focusable - && self.tracked_focus_handle.is_none() - && let Some(element_state) = element_state.as_mut() - { - let mut handle = element_state - .focus_handle - .get_or_insert_with(|| cx.focus_handle()) - .clone() - .tab_stop(self.tab_stop); - - if let Some(index) = self.tab_index { - handle = handle.tab_index(index); - } - - self.tracked_focus_handle = Some(handle); - } - - if let Some(scroll_handle) = self.tracked_scroll_handle.as_ref() { - let scroll_handle_state = scroll_handle.0.borrow(); - self.scroll_offset = Some(scroll_handle_state.offset.clone()); - self.ongoing_scroll = Some(scroll_handle_state.ongoing_scroll.clone()); - } else if (self.base_style.overflow.x == Some(Overflow::Scroll) - || self.base_style.overflow.y == Some(Overflow::Scroll)) - && let Some(element_state) = element_state.as_mut() - { - self.scroll_offset = Some( - element_state - .scroll_offset - .get_or_insert_with(Rc::default) - .clone(), - ); - self.ongoing_scroll = Some( - element_state - .ongoing_scroll - .get_or_insert_with(|| Rc::new(RefCell::new(OngoingScroll::default()))) - .clone(), - ); - } - - let style = self.compute_style_internal(None, element_state.as_mut(), window, cx); - let layout_id = f(style, window, cx); - (layout_id, element_state) - }, - ) - } - - /// Commit the bounds of this element according to this interactivity state's configured styles. - pub fn prepaint( - &mut self, - global_id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - content_size: Size, - window: &mut Window, - cx: &mut App, - f: impl FnOnce(&Style, Point, Option, &mut Window, &mut App) -> R, - ) -> R { - self.content_size = content_size; - - #[cfg(any(feature = "inspector", debug_assertions))] - window.with_inspector_state( - _inspector_id, - cx, - |inspector_state: &mut Option, _window| { - if let Some(inspector_state) = inspector_state { - inspector_state.bounds = bounds; - inspector_state.content_size = content_size; - } - }, - ); - - if let Some(focus_handle) = self.tracked_focus_handle.as_ref() { - window.set_focus_handle(focus_handle, cx); - - if window.a11y.is_active() { - if let Some(global_id) = global_id { - let node_id = global_id.accesskit_node_id(); - window.a11y.set_focusable(node_id, focus_handle.id); - if focus_handle.is_focused(window) { - window.a11y.set_focus(node_id); - } - } else if focus_handle.is_focused(window) { - // Focusable, but with no element id it can't have an - // accessibility node, so screen readers fall back to the - // whole window. - window - .a11y - .note_focus_without_node(focus_handle.id, "it has no element id"); - } - } - } - - if self.report_active_descendant_focus && window.a11y.is_active() { - if let Some(global_id) = global_id { - window - .a11y - .set_active_descendant(global_id.accesskit_node_id()); - } - } - window.with_optional_element_state::( - global_id, - |element_state, window| { - let mut element_state = - element_state.map(|element_state| element_state.unwrap_or_default()); - let style = self.compute_style_internal(None, element_state.as_mut(), window, cx); - - if let Some(element_state) = element_state.as_mut() { - if let Some(clicked_state) = element_state.clicked_state.as_ref() { - let clicked_state = clicked_state.borrow(); - self.active = Some(clicked_state.element); - } - if self.hover_style.is_some() || self.group_hover_style.is_some() { - element_state - .hover_state - .get_or_insert_with(Default::default); - } - if let Some(active_tooltip) = element_state.active_tooltip.as_ref() { - if self.tooltip_builder.is_some() { - self.tooltip_id = set_tooltip_on_window(active_tooltip, window); - } else { - // If there is no longer a tooltip builder, remove the active tooltip. - element_state.active_tooltip.take(); - } - } - } - - window.with_text_style(style.text_style().cloned(), |window| { - window.with_content_mask( - style.overflow_mask(bounds, window.rem_size(), window.scale_factor()), - |window| { - let hitbox = if self.should_insert_hitbox(&style, window, cx) { - Some(window.insert_hitbox(bounds, self.hitbox_behavior)) - } else { - None - }; - - let scroll_offset = - self.clamp_scroll_position(bounds, &style, window, cx); - let result = f(&style, scroll_offset, hitbox, window, cx); - (result, element_state) - }, - ) - }) - }, - ) - } - - fn should_insert_hitbox(&self, style: &Style, window: &Window, cx: &App) -> bool { - self.hitbox_behavior != HitboxBehavior::Normal - || self.window_control.is_some() - || style.mouse_cursor.is_some() - || self.group.is_some() - || self.scroll_offset.is_some() - || self.tracked_focus_handle.is_some() - || self.hover_style.is_some() - || self.group_hover_style.is_some() - || self.hover_listener.is_some() - || !self.mouse_up_listeners.is_empty() - || !self.mouse_pressure_listeners.is_empty() - || !self.mouse_down_listeners.is_empty() - || !self.mouse_move_listeners.is_empty() - || !self.mouse_exit_listeners.is_empty() - || !self.click_listeners.is_empty() - || !self.aux_click_listeners.is_empty() - || !self.scroll_wheel_listeners.is_empty() - || self.has_pinch_listeners() - || self.drag_listener.is_some() - || !self.drop_listeners.is_empty() - || !self.drag_over_styles.is_empty() - || self.tooltip_builder.is_some() - || window.is_inspector_picking(cx) - } - - fn clamp_scroll_position( - &self, - bounds: Bounds, - style: &Style, - window: &mut Window, - _cx: &mut App, - ) -> Point { - fn round_to_two_decimals(pixels: Pixels) -> Pixels { - const ROUNDING_FACTOR: f32 = 100.0; - (pixels * ROUNDING_FACTOR).round() / ROUNDING_FACTOR - } - - if let Some(scroll_offset) = self.scroll_offset.as_ref() { - let mut scroll_to_bottom = false; - let mut tracked_scroll_handle = self - .tracked_scroll_handle - .as_ref() - .map(|handle| handle.0.borrow_mut()); - if let Some(mut scroll_handle_state) = tracked_scroll_handle.as_deref_mut() { - scroll_handle_state.overflow = style.overflow; - scroll_to_bottom = mem::take(&mut scroll_handle_state.scroll_to_bottom); - } - - let rem_size = window.rem_size(); - // Taffy lays the box out with the padding snapped to the device pixel - // grid (`to_taffy`); recomputed unsnapped, e.g. py_1 at a fractional - // rem size, it exceeds `bounds` and leaves the box scrollable by the - // sub-pixel difference. - let padding = style - .padding - .to_pixels(bounds.size.into(), rem_size) - .map(|edge| window.pixel_snap(*edge)); - let padding_size = size(padding.left + padding.right, padding.top + padding.bottom); - // The floating point values produced by Taffy and ours often vary - // slightly after ~5 decimal places. This can lead to cases where after - // subtracting these, the container becomes scrollable for less than - // 0.00000x pixels. As we generally don't benefit from a precision that - // high for the maximum scroll, we round the scroll max to 2 decimal - // places here. - let padded_content_size = self.content_size + padding_size; - let scroll_max = Point::from(padded_content_size - bounds.size) - .map(round_to_two_decimals) - .max(&Default::default()); - // Clamp scroll offset in case scroll max is smaller now (e.g., if children - // were removed or the bounds became larger). - let mut scroll_offset = scroll_offset.borrow_mut(); - - scroll_offset.x = scroll_offset.x.clamp(-scroll_max.x, px(0.)); - if scroll_to_bottom { - scroll_offset.y = -scroll_max.y; - } else { - scroll_offset.y = scroll_offset.y.clamp(-scroll_max.y, px(0.)); - } - - if let Some(mut scroll_handle_state) = tracked_scroll_handle { - scroll_handle_state.max_offset = scroll_max; - scroll_handle_state.bounds = bounds; - } - - *scroll_offset - } else { - Point::default() - } - } - - /// Paint this element according to this interactivity state's configured styles - /// and bind the element's mouse and keyboard events. - /// - /// content_size is the size of the content of the element, which may be larger than the - /// element's bounds if the element is scrollable. - /// - /// the final computed style will be passed to the provided function, along - /// with the current scroll offset - pub fn paint( - &mut self, - global_id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - hitbox: Option<&Hitbox>, - window: &mut Window, - cx: &mut App, - f: impl FnOnce(&Style, &mut Window, &mut App), - ) { - self.hovered = hitbox.map(|hitbox| hitbox.is_hovered(window)); - window.with_optional_element_state::( - global_id, - |element_state, window| { - let mut element_state = - element_state.map(|element_state| element_state.unwrap_or_default()); - - let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx); - - #[cfg(any(feature = "test-support", test))] - if let Some(debug_selector) = &self.debug_selector { - window - .next_frame - .debug_bounds - .insert(debug_selector.clone(), bounds); - } - - self.paint_hover_group_handler(window, cx); - - if style.visibility == Visibility::Hidden { - return ((), element_state); - } - - let mut tab_group = None; - if self.tab_group { - tab_group = self.tab_index; - } - - window.with_element_opacity(style.opacity, |window| { - style.paint(bounds, window, cx, |window: &mut Window, cx: &mut App| { - window.with_text_style(style.text_style().cloned(), |window| { - window.with_content_mask( - style.overflow_mask( - bounds, - window.rem_size(), - window.scale_factor(), - ), - |window| { - window.with_tab_group(tab_group, |window| { - // Register the container's own focus handle *inside* its - // tab group, so that focusing the container and then - // calling `focus_next` descends into this group's first - // item. Inserting it before `with_tab_group` would give the - // container a shallower tab path than its children; with - // sibling groups every container would then sort ahead of - // every item, and `focus_next` from a container would jump - // to the first item in the whole window instead of its own. - if let Some(focus_handle) = &self.tracked_focus_handle { - window.next_frame.tab_stops.insert(focus_handle); - } - if let Some(hitbox) = hitbox { - #[cfg(debug_assertions)] - self.paint_debug_info( - global_id, hitbox, &style, window, cx, - ); - - if let Some(drag) = cx.active_drag.as_ref() { - if let Some(mouse_cursor) = drag.cursor_style { - window.set_window_cursor_style(mouse_cursor); - } - } else { - if let Some(mouse_cursor) = style.mouse_cursor { - window.set_cursor_style(mouse_cursor, hitbox); - } - } - - if let Some(group) = self.group.clone() { - GroupHitboxes::push(group, hitbox.id, cx); - } - - if let Some(area) = self.window_control { - window.insert_window_control_hitbox( - area, - hitbox.clone(), - ); - } - - self.paint_mouse_listeners( - hitbox, - element_state.as_mut(), - window, - cx, - ); - self.paint_scroll_listener(hitbox, &style, window, cx); - } - - self.paint_keyboard_listeners(window, cx); - - if window.a11y.is_active() { - if let Some(global_id) = global_id { - if !self.a11y_action_listeners.is_empty() { - let node_id = global_id.accesskit_node_id(); - for (action, listener) in - self.a11y_action_listeners.drain(..) - { - window.on_a11y_action( - node_id, action, listener, - ); - } - } - } - } - - f(&style, window, cx); - - if let Some(_hitbox) = hitbox { - #[cfg(any(feature = "inspector", debug_assertions))] - window.insert_inspector_hitbox( - _hitbox.id, - _inspector_id, - cx, - ); - - if let Some(group) = self.group.as_ref() { - GroupHitboxes::pop(group, cx); - } - } - }) - }, - ); - }); - }); - }); - - ((), element_state) - }, - ); - } - - #[cfg(debug_assertions)] - fn paint_debug_info( - &self, - global_id: Option<&GlobalElementId>, - hitbox: &Hitbox, - style: &Style, - window: &mut Window, - cx: &mut App, - ) { - use crate::{BorderStyle, TextAlign}; - - if let Some(global_id) = global_id - && (style.debug || style.debug_below || cx.has_global::()) - && hitbox.is_hovered(window) - { - const FONT_SIZE: crate::Pixels = crate::Pixels(10.); - let element_id = format!("{global_id:?}"); - let str_len = element_id.len(); - - let render_debug_text = |window: &mut Window| { - if let Some(text) = window - .text_system() - .shape_text( - element_id.into(), - FONT_SIZE, - &[window.text_style().to_run(str_len)], - None, - None, - ) - .ok() - .and_then(|mut text| text.pop()) - { - text.paint(hitbox.origin, FONT_SIZE, TextAlign::Left, None, window, cx) - .ok(); - - let text_bounds = crate::Bounds { - origin: hitbox.origin, - size: text.size(FONT_SIZE), - }; - if let Some(source_location) = self.source_location - && text_bounds.contains(&window.mouse_position()) - && window.modifiers().secondary() - { - let secondary_held = window.modifiers().secondary(); - window.on_key_event({ - move |e: &crate::ModifiersChangedEvent, _phase, window, _cx| { - if e.modifiers.secondary() != secondary_held - && text_bounds.contains(&window.mouse_position()) - { - window.refresh(); - } - } - }); - - let was_hovered = hitbox.is_hovered(window); - let current_view = window.current_view(); - window.on_mouse_event({ - let hitbox = hitbox.clone(); - move |_: &MouseMoveEvent, phase, window, cx| { - if phase == DispatchPhase::Capture { - let hovered = hitbox.is_hovered(window); - if hovered != was_hovered { - cx.notify(current_view) - } - } - } - }); - - window.on_mouse_event({ - let hitbox = hitbox.clone(); - move |e: &crate::MouseDownEvent, phase, window, cx| { - if text_bounds.contains(&e.position) - && phase.capture() - && hitbox.is_hovered(window) - { - cx.stop_propagation(); - let Ok(dir) = std::env::current_dir() else { - return; - }; - - eprintln!( - "This element was created at:\n{}:{}:{}", - dir.join(source_location.file()).to_string_lossy(), - source_location.line(), - source_location.column() - ); - } - } - }); - window.paint_quad(crate::outline( - crate::Bounds { - origin: hitbox.origin - + crate::point(crate::px(0.), FONT_SIZE - px(2.)), - size: crate::Size { - width: text_bounds.size.width, - height: crate::px(1.), - }, - }, - crate::red(), - BorderStyle::default(), - )) - } - } - }; - - window.with_text_style( - Some(crate::TextStyleRefinement { - color: Some(crate::red()), - line_height: Some(FONT_SIZE.into()), - background_color: Some(crate::white()), - ..Default::default() - }), - render_debug_text, - ) - } - } - - fn paint_mouse_listeners( - &mut self, - hitbox: &Hitbox, - element_state: Option<&mut InteractiveElementState>, - window: &mut Window, - cx: &mut App, - ) { - let is_focused = self - .tracked_focus_handle - .as_ref() - .map(|handle| handle.is_focused(window)) - .unwrap_or(false); - - // If this element can be focused, register a mouse down listener - // that will automatically transfer focus when hitting the element. - // This behavior can be suppressed by using `cx.prevent_default()`. - if let Some(focus_handle) = self.tracked_focus_handle.clone() { - let hitbox = hitbox.clone(); - window.on_mouse_event(move |_: &MouseDownEvent, phase, window, cx| { - if phase == DispatchPhase::Bubble - && hitbox.is_hovered(window) - && !window.default_prevented() - { - window.focus(&focus_handle, cx); - // If there is a parent that is also focusable, prevent it - // from transferring focus because we already did so. - window.prevent_default(); - } - }); - } - - for listener in self.mouse_down_listeners.drain(..) { - let hitbox = hitbox.clone(); - window.on_mouse_event(move |event: &MouseDownEvent, phase, window, cx| { - listener(event, phase, &hitbox, window, cx); - }) - } - - for listener in self.mouse_up_listeners.drain(..) { - let hitbox = hitbox.clone(); - window.on_mouse_event(move |event: &MouseUpEvent, phase, window, cx| { - listener(event, phase, &hitbox, window, cx); - }) - } - - for listener in self.mouse_pressure_listeners.drain(..) { - let hitbox = hitbox.clone(); - window.on_mouse_event(move |event: &MousePressureEvent, phase, window, cx| { - listener(event, phase, &hitbox, window, cx); - }) - } - - for listener in self.mouse_move_listeners.drain(..) { - let hitbox = hitbox.clone(); - window.on_mouse_event(move |event: &MouseMoveEvent, phase, window, cx| { - listener(event, phase, &hitbox, window, cx); - }) - } - - for listener in self.mouse_exit_listeners.drain(..) { - let hitbox = hitbox.clone(); - window.on_mouse_event(move |event: &MouseExitEvent, phase, window, cx| { - listener(event, phase, &hitbox, window, cx); - }) - } - - for listener in self.scroll_wheel_listeners.drain(..) { - let hitbox = hitbox.clone(); - window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| { - listener(event, phase, &hitbox, window, cx); - }) - } - - for listener in self.pinch_listeners.drain(..) { - let hitbox = hitbox.clone(); - window.on_mouse_event(move |event: &PinchEvent, phase, window, cx| { - listener(event, phase, &hitbox, window, cx); - }) - } - - if self.hover_style.is_some() - || self.base_style.mouse_cursor.is_some() - || cx.active_drag.is_some() && !self.drag_over_styles.is_empty() - { - let hitbox = hitbox.clone(); - let hover_state = self.hover_style.as_ref().and_then(|_| { - element_state - .as_ref() - .and_then(|state| state.hover_state.as_ref()) - .cloned() - }); - let current_view = window.current_view(); - - window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| { - let hovered = hitbox.is_hovered(window); - let was_hovered = hover_state - .as_ref() - .is_some_and(|state| state.borrow().element); - if phase == DispatchPhase::Capture && hovered != was_hovered { - if let Some(hover_state) = &hover_state { - hover_state.borrow_mut().element = hovered; - cx.notify(current_view); - } - } - }); - } - - if let Some(group_hover) = self.group_hover_style.as_ref() { - if let Some(group_hitbox_id) = GroupHitboxes::get(&group_hover.group, cx) { - let hover_state = element_state - .as_ref() - .and_then(|element| element.hover_state.as_ref()) - .cloned(); - let current_view = window.current_view(); - - window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| { - let group_hovered = group_hitbox_id.is_hovered(window); - let was_group_hovered = hover_state - .as_ref() - .is_some_and(|state| state.borrow().group); - if phase == DispatchPhase::Capture && group_hovered != was_group_hovered { - if let Some(hover_state) = &hover_state { - hover_state.borrow_mut().group = group_hovered; - cx.notify(current_view); - } - } - }); - } - } - - let drag_cursor_style = self.base_style.as_ref().mouse_cursor; - - let mut drag_listener = mem::take(&mut self.drag_listener); - let drop_listeners = mem::take(&mut self.drop_listeners); - let click_listeners = mem::take(&mut self.click_listeners); - let aux_click_listeners = mem::take(&mut self.aux_click_listeners); - let can_drop_predicate = mem::take(&mut self.can_drop_predicate); - - if !drop_listeners.is_empty() { - let hitbox = hitbox.clone(); - window.on_mouse_event({ - move |_: &MouseUpEvent, phase, window, cx| { - if let Some(drag) = &cx.active_drag - && phase == DispatchPhase::Bubble - && hitbox.is_hovered(window) - { - let drag_state_type = drag.value.as_ref().type_id(); - for (drop_state_type, listener) in &drop_listeners { - if *drop_state_type == drag_state_type { - let drag = cx - .active_drag - .take() - .expect("checked for type drag state type above"); - - let mut can_drop = true; - if let Some(predicate) = &can_drop_predicate { - can_drop = predicate(drag.value.as_ref(), window, cx); - } - - if can_drop { - listener(drag.value.as_ref(), window, cx); - window.refresh(); - cx.stop_propagation(); - } - } - } - } - } - }); - } - - if let Some(element_state) = element_state { - if !click_listeners.is_empty() - || !aux_click_listeners.is_empty() - || drag_listener.is_some() - { - let pending_mouse_down = element_state - .pending_mouse_down - .get_or_insert_with(Default::default) - .clone(); - - let pending_keyboard_down = element_state - .pending_keyboard_down - .get_or_insert_with(Default::default) - .clone(); - - let clicked_state = element_state - .clicked_state - .get_or_insert_with(Default::default) - .clone(); - - window.on_mouse_event({ - let pending_mouse_down = pending_mouse_down.clone(); - let hitbox = hitbox.clone(); - let has_aux_click_listeners = !aux_click_listeners.is_empty(); - move |event: &MouseDownEvent, phase, window, _cx| { - if phase == DispatchPhase::Bubble - && (event.button == MouseButton::Left || has_aux_click_listeners) - && hitbox.is_hovered(window) - { - *pending_mouse_down.borrow_mut() = Some(event.clone()); - window.refresh(); - } - } - }); - - window.on_mouse_event({ - let pending_mouse_down = pending_mouse_down.clone(); - let hitbox = hitbox.clone(); - move |event: &MouseMoveEvent, phase, window, cx| { - if phase == DispatchPhase::Capture { - return; - } - - let mut pending_mouse_down = pending_mouse_down.borrow_mut(); - if let Some(mouse_down) = pending_mouse_down.clone() - && !cx.has_active_drag() - && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD - && let Some(listener) = drag_listener.take() - && mouse_down.button == MouseButton::Left - { - *clicked_state.borrow_mut() = ElementClickedState::default(); - let cursor_offset = event.position - hitbox.origin; - let drag = (listener.render)( - listener.value.as_ref(), - cursor_offset, - window, - cx, - ); - let external_payload_source = - listener.external_payload.map(|external_payload| { - let value = listener.value.clone(); - Box::new(move |window: &mut Window, cx: &mut App| { - external_payload(value.as_ref(), window, cx) - }) - as ExternalDragPayloadSource - }); - cx.active_drag = Some(AnyDrag { - view: drag, - value: listener.value, - cursor_offset, - cursor_style: drag_cursor_style, - external_payload_source, - }); - pending_mouse_down.take(); - window.refresh(); - cx.stop_propagation(); - } - } - }); - - if is_focused { - // Record the focus generation at which an enter/space key - // down event happened on this element. The next key up - // event will be mapped to a click event if both of the - // following are true: - // - no other key events happen in between - // - the focus generation is the same (implying focus did not move) - // - // This design avoids an ABA problem that happens if you - // store the focus handle that registered the keypress. - window.on_key_event({ - let pending_keyboard_down = pending_keyboard_down.clone(); - move |event: &KeyDownEvent, phase, window, _cx| { - if phase.bubble() && !window.default_prevented() { - let stroke = &event.keystroke; - let is_activation_key = (stroke.key.eq("enter") - || stroke.key.eq("space")) - && !stroke.modifiers.modified(); - *pending_keyboard_down.borrow_mut() = - is_activation_key.then_some(window.focus_generation); - } - } - }); - - // Press enter, space to trigger click, when the element is focused. - window.on_key_event({ - let click_listeners = click_listeners.clone(); - let hitbox = hitbox.clone(); - move |event: &KeyUpEvent, phase, window, cx| { - if phase.bubble() && !window.default_prevented() { - let stroke = &event.keystroke; - let keyboard_button = if stroke.key.eq("enter") { - Some(KeyboardButton::Enter) - } else if stroke.key.eq("space") { - Some(KeyboardButton::Space) - } else { - None - }; - - if let Some(button) = keyboard_button - && !stroke.modifiers.modified() - { - let pending = - std::mem::take(&mut *pending_keyboard_down.borrow_mut()); - if pending != Some(window.focus_generation) { - return; - } - - let click_event = ClickEvent::Keyboard(KeyboardClickEvent { - button, - bounds: hitbox.bounds, - }); - - for listener in &click_listeners { - listener(&click_event, window, cx); - } - } else { - // Releasing any other key mid-press means - // this isn't a clean activation, so cancel - // the pending keydown. - *pending_keyboard_down.borrow_mut() = None; - } - } - } - }); - } - - window.on_mouse_event({ - let mut captured_mouse_down = None; - let hitbox = hitbox.clone(); - move |event: &MouseUpEvent, phase, window, cx| match phase { - // Clear the pending mouse down during the capture phase, - // so that it happens even if another event handler stops - // propagation. - DispatchPhase::Capture => { - let mut pending_mouse_down = pending_mouse_down.borrow_mut(); - if pending_mouse_down.is_some() && hitbox.is_hovered(window) { - captured_mouse_down = pending_mouse_down.take(); - window.refresh(); - } else if pending_mouse_down.is_some() { - // Clear the pending mouse down event (without firing click handlers) - // if the hitbox is not being hovered. - // This avoids dragging elements that changed their position - // immediately after being clicked. - // See https://github.com/zed-industries/zed/issues/24600 for more details - pending_mouse_down.take(); - window.refresh(); - } - } - // Fire click handlers during the bubble phase. - DispatchPhase::Bubble => { - if let Some(mouse_down) = captured_mouse_down.take() { - let btn = mouse_down.button; - - let mouse_click = ClickEvent::Mouse(MouseClickEvent { - down: mouse_down, - up: event.clone(), - }); - - match btn { - MouseButton::Left => { - for listener in &click_listeners { - listener(&mouse_click, window, cx); - } - } - _ => { - for listener in &aux_click_listeners { - listener(&mouse_click, window, cx); - } - } - } - } - } - } - }); - } - - if let Some(hover_listener) = self.hover_listener.take() { - let was_hovered = element_state - .hover_listener_state - .get_or_insert_with(Default::default) - .clone(); - let has_mouse_down = element_state - .pending_mouse_down - .get_or_insert_with(Default::default) - .clone(); - let hover_listener = Rc::new(hover_listener); - let hover_listener_state = was_hovered.clone(); - let update_hover = move |is_hovered: bool, window: &mut Window, cx: &mut App| { - let mut was_hovered = hover_listener_state.borrow_mut(); - if is_hovered != *was_hovered { - *was_hovered = is_hovered; - drop(was_hovered); - hover_listener(&is_hovered, window, cx); - } - }; - let hover_listener_mode = self.hover_listener_mode; - - if has_mouse_down.borrow().is_none() { - let is_hovered = - !cx.has_active_drag() && hover_listener_mode.is_hovered(hitbox, window); - if is_hovered != *was_hovered.borrow() { - let update_hover = update_hover.clone(); - window.defer(cx, move |window, cx| { - update_hover(is_hovered, window, cx); - }); - } - } - - window.on_mouse_event({ - let update_hover = update_hover.clone(); - let hitbox = hitbox.clone(); - move |_: &MouseMoveEvent, phase, window, cx| { - if phase == DispatchPhase::Bubble { - let is_hovered = has_mouse_down.borrow().is_none() - && !cx.has_active_drag() - && hover_listener_mode.is_hovered(&hitbox, window); - update_hover(is_hovered, window, cx); - } - } - }); - - // The pointer can leave the window without a final MouseMove, so also - // clear hover on MouseExited. - window.on_mouse_event(move |_: &MouseExitEvent, phase, window, cx| { - if phase == DispatchPhase::Bubble { - update_hover(false, window, cx); - } - }); - } - - if let Some(tooltip_builder) = self.tooltip_builder.take() { - let active_tooltip = element_state - .active_tooltip - .get_or_insert_with(Default::default) - .clone(); - let pending_mouse_down = element_state - .pending_mouse_down - .get_or_insert_with(Default::default) - .clone(); - - let tooltip_is_hoverable = tooltip_builder.hoverable; - let build_tooltip = Rc::new(move |window: &mut Window, cx: &mut App| { - Some(((tooltip_builder.build)(window, cx), tooltip_is_hoverable)) - }); - // Use bounds instead of testing hitbox since this is called during prepaint. - let check_is_hovered_during_prepaint = Rc::new({ - let pending_mouse_down = pending_mouse_down.clone(); - let source_bounds = hitbox.bounds; - move |window: &Window| { - !window.last_input_was_keyboard() - && pending_mouse_down.borrow().is_none() - && source_bounds.contains(&window.mouse_position()) - } - }); - let check_is_hovered = Rc::new({ - let hitbox = hitbox.clone(); - move |window: &Window| { - pending_mouse_down.borrow().is_none() && hitbox.is_hovered(window) - } - }); - register_tooltip_mouse_handlers( - &active_tooltip, - self.tooltip_id, - build_tooltip, - check_is_hovered, - check_is_hovered_during_prepaint, - self.tooltip_show_delay, - window, - ); - } - - // We unconditionally bind both the mouse up and mouse down active state handlers - // Because we might not get a chance to render a frame before the mouse up event arrives. - let active_state = element_state - .clicked_state - .get_or_insert_with(Default::default) - .clone(); - - { - let active_state = active_state.clone(); - window.on_mouse_event(move |_: &MouseUpEvent, phase, window, _cx| { - if phase == DispatchPhase::Capture && active_state.borrow().is_clicked() { - *active_state.borrow_mut() = ElementClickedState::default(); - window.refresh(); - } - }); - } - - { - let active_group_hitbox = self - .group_active_style - .as_ref() - .and_then(|group_active| GroupHitboxes::get(&group_active.group, cx)); - let hitbox = hitbox.clone(); - window.on_mouse_event(move |_: &MouseDownEvent, phase, window, _cx| { - if phase == DispatchPhase::Bubble && !window.default_prevented() { - let group_hovered = active_group_hitbox - .is_some_and(|group_hitbox_id| group_hitbox_id.is_hovered(window)); - let element_hovered = hitbox.is_hovered(window); - if group_hovered || element_hovered { - *active_state.borrow_mut() = ElementClickedState { - group: group_hovered, - element: element_hovered, - }; - window.refresh(); - } - } - }); - } - } - } - - fn paint_keyboard_listeners(&mut self, window: &mut Window, _cx: &mut App) { - let key_down_listeners = mem::take(&mut self.key_down_listeners); - let key_up_listeners = mem::take(&mut self.key_up_listeners); - let modifiers_changed_listeners = mem::take(&mut self.modifiers_changed_listeners); - let action_listeners = mem::take(&mut self.action_listeners); - if let Some(context) = self.key_context.clone() { - window.set_key_context(context); - } - - for listener in key_down_listeners { - window.on_key_event(move |event: &KeyDownEvent, phase, window, cx| { - listener(event, phase, window, cx); - }) - } - - for listener in key_up_listeners { - window.on_key_event(move |event: &KeyUpEvent, phase, window, cx| { - listener(event, phase, window, cx); - }) - } - - for listener in modifiers_changed_listeners { - window.on_modifiers_changed(move |event: &ModifiersChangedEvent, window, cx| { - listener(event, window, cx); - }) - } - - for (action_type, listener) in action_listeners { - window.on_action(action_type, listener) - } - } - - fn paint_hover_group_handler(&self, window: &mut Window, cx: &mut App) { - let group_hitbox = self - .group_hover_style - .as_ref() - .and_then(|group_hover| GroupHitboxes::get(&group_hover.group, cx)); - - if let Some(group_hitbox) = group_hitbox { - let was_hovered = group_hitbox.is_hovered(window); - let current_view = window.current_view(); - window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| { - let hovered = group_hitbox.is_hovered(window); - if phase == DispatchPhase::Capture && hovered != was_hovered { - cx.notify(current_view); - } - }); - } - } - - fn paint_scroll_listener( - &self, - hitbox: &Hitbox, - style: &Style, - window: &mut Window, - _cx: &mut App, - ) { - if let Some(scroll_offset) = self.scroll_offset.clone() { - let ongoing_scroll = self.ongoing_scroll.clone(); - let overflow = style.overflow; - let allow_concurrent_scroll = style.allow_concurrent_scroll; - let restrict_scroll_to_axis = style.restrict_scroll_to_axis; - let line_height = window.line_height(); - let hitbox = hitbox.clone(); - let current_view = window.current_view(); - window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| { - if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) { - let mut scroll_offset = scroll_offset.borrow_mut(); - let old_scroll_offset = *scroll_offset; - let mut delta = event.delta.pixel_delta(line_height); - - if restrict_scroll_to_axis - && event.delta.precise() - && let Some(ongoing_scroll) = &ongoing_scroll - { - ongoing_scroll - .borrow_mut() - .filter(&mut delta, event.touch_phase); - } - - let mut delta_x = match overflow.x { - Overflow::Scroll if !delta.x.is_zero() => delta.x, - Overflow::Scroll - if !restrict_scroll_to_axis && overflow.y != Overflow::Scroll => - { - delta.y - } - _ => Pixels::ZERO, - }; - let mut delta_y = match overflow.y { - Overflow::Scroll if !delta.y.is_zero() => delta.y, - Overflow::Scroll - if !restrict_scroll_to_axis && overflow.x != Overflow::Scroll => - { - delta.x - } - _ => Pixels::ZERO, - }; - if !allow_concurrent_scroll && !delta_x.is_zero() && !delta_y.is_zero() { - if delta_x.abs() > delta_y.abs() { - delta_y = Pixels::ZERO; - } else { - delta_x = Pixels::ZERO; - } - } - scroll_offset.y += delta_y; - scroll_offset.x += delta_x; - if *scroll_offset != old_scroll_offset { - cx.notify(current_view); - } - } - }); - } - } - - /// Compute the visual style for this element, based on the current bounds and the element's state. - pub fn compute_style( - &self, - global_id: Option<&GlobalElementId>, - hitbox: Option<&Hitbox>, - window: &mut Window, - cx: &mut App, - ) -> Style { - window.with_optional_element_state(global_id, |element_state, window| { - let mut element_state = - element_state.map(|element_state| element_state.unwrap_or_default()); - let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx); - (style, element_state) - }) - } - - /// Called from internal methods that have already called with_element_state. - fn compute_style_internal( - &self, - hitbox: Option<&Hitbox>, - element_state: Option<&mut InteractiveElementState>, - window: &mut Window, - cx: &mut App, - ) -> Style { - let mut style = Style::default(); - style.refine(&self.base_style); - - if let Some(focus_handle) = self.tracked_focus_handle.as_ref() { - if let Some(in_focus_style) = self.in_focus_style.as_ref() - && focus_handle.within_focused(window, cx) - { - style.refine(in_focus_style); - } - - if let Some(focus_style) = self.focus_style.as_ref() - && focus_handle.is_focused(window) - { - style.refine(focus_style); - } - - if let Some(focus_visible_style) = self.focus_visible_style.as_ref() - && focus_handle.is_focused(window) - && window.last_input_was_keyboard() - { - style.refine(focus_visible_style); - } - } - - if !cx.has_active_drag() { - if let Some(group_hover) = self.group_hover_style.as_ref() { - let is_group_hovered = - if let Some(group_hitbox_id) = GroupHitboxes::get(&group_hover.group, cx) { - !window.last_input_was_touch() && group_hitbox_id.is_hovered(window) - } else if let Some(element_state) = element_state.as_ref() { - !window.last_input_was_touch() - && element_state - .hover_state - .as_ref() - .map(|state| state.borrow().group) - .unwrap_or(false) - } else { - false - }; - - if is_group_hovered { - style.refine(&group_hover.style); - } - } - - if let Some(hover_style) = self.hover_style.as_ref() { - let is_hovered = if let Some(hitbox) = hitbox { - !window.last_input_was_touch() && hitbox.is_hovered(window) - } else if let Some(element_state) = element_state.as_ref() { - !window.last_input_was_touch() - && element_state - .hover_state - .as_ref() - .map(|state| state.borrow().element) - .unwrap_or(false) - } else { - false - }; - - if is_hovered { - style.refine(hover_style); - } - } - } - - if let Some(hitbox) = hitbox { - if let Some(drag) = cx.active_drag.take() { - let mut can_drop = true; - if let Some(can_drop_predicate) = &self.can_drop_predicate { - can_drop = can_drop_predicate(drag.value.as_ref(), window, cx); - } - - if can_drop { - for (state_type, group_drag_style) in &self.group_drag_over_styles { - if let Some(group_hitbox_id) = - GroupHitboxes::get(&group_drag_style.group, cx) - && *state_type == drag.value.as_ref().type_id() - && group_hitbox_id.is_hovered(window) - { - style.refine(&group_drag_style.style); - } - } - - for (state_type, build_drag_over_style) in &self.drag_over_styles { - if *state_type == drag.value.as_ref().type_id() && hitbox.is_hovered(window) - { - style.refine(&build_drag_over_style(drag.value.as_ref(), window, cx)); - } - } - } - - style.mouse_cursor = drag.cursor_style; - cx.active_drag = Some(drag); - } - } - - if let Some(element_state) = element_state { - let clicked_state = element_state - .clicked_state - .get_or_insert_with(Default::default) - .borrow(); - if clicked_state.group - && let Some(group) = self.group_active_style.as_ref() - { - style.refine(&group.style) - } - - if let Some(active_style) = self.active_style.as_ref() - && clicked_state.element - { - style.refine(active_style) - } - } - - style - } - - pub(crate) fn write_a11y_info(&self, node: &mut accesskit::Node) { - if let Some(id) = &self.aria.author_id { - node.set_author_id(id.to_string()); - } - if let Some(label) = &self.aria.label { - node.set_label(label.to_string()); - } - if let Some(description) = &self.aria.description { - node.set_description(description.to_string()); - } - if let Some(keyshortcuts) = &self.aria.keyshortcuts { - node.set_keyboard_shortcut(keyshortcuts.to_string()); - } - if let Some(selected) = self.aria.selected { - node.set_selected(selected); - } - if let Some(current) = self.aria.current { - node.set_aria_current(current); - } - if let Some(expanded) = self.aria.expanded { - node.set_expanded(expanded); - } - if let Some(toggled) = self.aria.toggled { - node.set_toggled(toggled); - } - if let Some(value) = self.aria.numeric_value { - node.set_numeric_value(value); - } - if let Some(value) = self.aria.min_numeric_value { - node.set_min_numeric_value(value); - } - if let Some(value) = self.aria.max_numeric_value { - node.set_max_numeric_value(value); - } - if let Some(step) = self.aria.numeric_value_step { - node.set_numeric_value_step(step); - } - if let Some(value) = &self.aria.value { - node.set_value(value.to_string()); - } - if let Some(placeholder) = &self.aria.placeholder { - node.set_placeholder(placeholder.to_string()); - } - if let Some(orientation) = self.aria.orientation { - node.set_orientation(orientation); - } - if let Some(level) = self.aria.level { - node.set_level(level); - } - if let Some(position) = self.aria.position_in_set { - node.set_position_in_set(position); - } - if let Some(size) = self.aria.size_of_set { - node.set_size_of_set(size); - } - if let Some(index) = self.aria.row_index { - node.set_row_index(index); - } - if let Some(index) = self.aria.column_index { - node.set_column_index(index); - } - if let Some(count) = self.aria.row_count { - node.set_row_count(count); - } - if let Some(count) = self.aria.column_count { - node.set_column_count(count); - } - if !self.click_listeners.is_empty() { - node.add_action(accesskit::Action::Click); - } - if self.tracked_focus_handle.is_some() || self.focusable { - node.add_action(accesskit::Action::Focus); - } - for (action, _) in &self.a11y_action_listeners { - node.add_action(*action); - } - } -} - -/// The per-frame state of an interactive element. Used for tracking stateful interactions like clicks -/// and scroll offsets. -#[derive(Default)] -pub struct InteractiveElementState { - pub(crate) focus_handle: Option, - pub(crate) clicked_state: Option>>, - pub(crate) hover_state: Option>>, - pub(crate) hover_listener_state: Option>>, - pub(crate) pending_mouse_down: Option>>>, - /// Set to the window's [`focus_generation`](crate::Window::focus_generation) - /// when an Enter/Space keydown is received while this element is focused, - /// recording that we are waiting for the matching keyup to fire a keyboard - /// click. On keyup the click only fires if the stored generation still - /// matches the window's current one, i.e. focus never moved during the - /// press (mirroring the browser clearing a control's pressed state on - /// blur). `None` means no activation key is pending. - pub(crate) pending_keyboard_down: Option>>>, - pub(crate) scroll_offset: Option>>>, - ongoing_scroll: Option>>, - pub(crate) active_tooltip: Option>>>, -} - -/// Whether or not the element or a group that contains it is clicked by the mouse. -#[derive(Copy, Clone, Default, Eq, PartialEq)] -pub struct ElementClickedState { - /// True if this element's group has been clicked, false otherwise - pub group: bool, - - /// True if this element has been clicked, false otherwise - pub element: bool, -} - -impl ElementClickedState { - fn is_clicked(&self) -> bool { - self.group || self.element - } -} - -/// Whether or not the element or a group that contains it is hovered. -#[derive(Copy, Clone, Default, Eq, PartialEq)] -pub struct ElementHoverState { - /// True if this element's group is hovered, false otherwise - pub group: bool, - - /// True if this element is hovered, false otherwise - pub element: bool, -} - -pub(crate) enum ActiveTooltip { - /// Currently delaying before showing the tooltip. - WaitingForShow { _task: Task<()> }, - /// Tooltip is visible, element was hovered or for hoverable tooltips, the tooltip was hovered. - Visible { - tooltip: AnyTooltip, - is_hoverable: bool, - }, - /// Tooltip is visible and hoverable, but the mouse is no longer hovering. Currently delaying - /// before hiding it. - WaitingForHide { - tooltip: AnyTooltip, - _task: Task<()>, - }, -} - -pub(crate) fn clear_active_tooltip( - active_tooltip: &Rc>>, - window: &mut Window, -) { - match active_tooltip.borrow_mut().take() { - None => {} - Some(ActiveTooltip::WaitingForShow { .. }) => {} - Some(ActiveTooltip::Visible { .. }) => window.refresh(), - Some(ActiveTooltip::WaitingForHide { .. }) => window.refresh(), - } -} - -pub(crate) fn clear_active_tooltip_if_not_hoverable( - active_tooltip: &Rc>>, - window: &mut Window, -) { - let should_clear = match active_tooltip.borrow().as_ref() { - None => false, - Some(ActiveTooltip::WaitingForShow { .. }) => false, - Some(ActiveTooltip::Visible { is_hoverable, .. }) => !is_hoverable, - Some(ActiveTooltip::WaitingForHide { .. }) => false, - }; - if should_clear { - active_tooltip.borrow_mut().take(); - window.refresh(); - } -} - -pub(crate) fn set_tooltip_on_window( - active_tooltip: &Rc>>, - window: &mut Window, -) -> Option { - let tooltip = match active_tooltip.borrow().as_ref() { - None => return None, - Some(ActiveTooltip::WaitingForShow { .. }) => return None, - Some(ActiveTooltip::Visible { tooltip, .. }) => tooltip.clone(), - Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => tooltip.clone(), - }; - Some(window.set_tooltip(tooltip)) -} - -pub(crate) fn register_tooltip_mouse_handlers( - active_tooltip: &Rc>>, - tooltip_id: Option, - build_tooltip: Rc Option<(AnyView, bool)>>, - check_is_hovered: Rc bool>, - check_is_hovered_during_prepaint: Rc bool>, - show_delay: Option, - window: &mut Window, -) { - let current_view = window.current_view(); - let show_delay = show_delay.unwrap_or(DEFAULT_TOOLTIP_SHOW_DELAY); - - window.on_mouse_event({ - let active_tooltip = active_tooltip.clone(); - let build_tooltip = build_tooltip.clone(); - let check_is_hovered = check_is_hovered.clone(); - move |_: &MouseMoveEvent, phase, window, cx| { - handle_tooltip_mouse_move( - &active_tooltip, - &build_tooltip, - &check_is_hovered, - &check_is_hovered_during_prepaint, - tooltip_id, - current_view, - phase, - show_delay, - window, - cx, - ) - } - }); - - window.on_mouse_event({ - let active_tooltip = active_tooltip.clone(); - move |_: &MouseDownEvent, _phase, window: &mut Window, _cx| { - if !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)) { - clear_active_tooltip_if_not_hoverable(&active_tooltip, window); - } - } - }); - - window.on_mouse_event({ - let active_tooltip = active_tooltip.clone(); - move |_: &ScrollWheelEvent, _phase, window: &mut Window, _cx| { - if !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)) { - clear_active_tooltip_if_not_hoverable(&active_tooltip, window); - } - } - }); -} - -/// Handles displaying tooltips when an element is hovered. -/// -/// The mouse hovering logic also relies on being called from window prepaint in order to handle the -/// case where the element the tooltip is on is not rendered - in that case its mouse listeners are -/// also not registered. During window prepaint, the hitbox information is not available, so -/// `check_is_hovered_during_prepaint` is used which bases the check off of the absolute bounds of -/// the element. -/// -/// TODO: There's a minor bug due to the use of absolute bounds while checking during prepaint - it -/// does not know if the hitbox is occluded. In the case where a tooltip gets displayed and then -/// gets occluded after display, it will stick around until the mouse exits the hover bounds. -fn handle_tooltip_mouse_move( - active_tooltip: &Rc>>, - build_tooltip: &Rc Option<(AnyView, bool)>>, - check_is_hovered: &Rc bool>, - check_is_hovered_during_prepaint: &Rc bool>, - tooltip_id: Option, - current_view: EntityId, - phase: DispatchPhase, - show_delay: Duration, - window: &mut Window, - cx: &mut App, -) { - // Separates logic for what mutation should occur from applying it, to avoid overlapping - // RefCell borrows. - enum Action { - None, - CancelShow, - ScheduleShow, - CheckVisible, - } - - let action = match active_tooltip.borrow().as_ref() { - None => { - let is_hovered = check_is_hovered(window); - if is_hovered && phase.bubble() { - Action::ScheduleShow - } else { - Action::None - } - } - Some(ActiveTooltip::WaitingForShow { .. }) => { - let is_hovered = check_is_hovered(window); - if is_hovered { - Action::None - } else { - Action::CancelShow - } - } - Some(ActiveTooltip::Visible { is_hoverable, .. }) => { - if phase.capture() - && !check_is_hovered(window) - && (!*is_hoverable - || !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window))) - { - Action::CheckVisible - } else { - Action::None - } - } - Some(ActiveTooltip::WaitingForHide { .. }) => { - if phase.capture() - && (check_is_hovered(window) - || tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window))) - { - Action::CheckVisible - } else { - Action::None - } - } - }; - - match action { - Action::None => {} - Action::CancelShow => { - // Cancel waiting to show tooltip when it is no longer hovered. - active_tooltip.borrow_mut().take(); - } - Action::ScheduleShow => { - let delayed_show_task = window.spawn(cx, { - let weak_active_tooltip = Rc::downgrade(active_tooltip); - let build_tooltip = build_tooltip.clone(); - let check_is_hovered_during_prepaint = check_is_hovered_during_prepaint.clone(); - async move |cx| { - cx.background_executor().timer(show_delay).await; - let Some(active_tooltip) = weak_active_tooltip.upgrade() else { - return; - }; - cx.update(|window, cx| { - let new_tooltip = - build_tooltip(window, cx).map(|(view, tooltip_is_hoverable)| { - let weak_active_tooltip = Rc::downgrade(&active_tooltip); - ActiveTooltip::Visible { - tooltip: AnyTooltip { - view, - mouse_position: window.mouse_position(), - check_visible_and_update: Rc::new( - move |tooltip_bounds, window, cx| { - let Some(active_tooltip) = - weak_active_tooltip.upgrade() - else { - return false; - }; - handle_tooltip_check_visible_and_update( - &active_tooltip, - tooltip_is_hoverable, - &check_is_hovered_during_prepaint, - tooltip_bounds, - window, - cx, - ) - }, - ), - }, - is_hoverable: tooltip_is_hoverable, - } - }); - *active_tooltip.borrow_mut() = new_tooltip; - window.refresh(); - }) - .ok(); - } - }); - active_tooltip - .borrow_mut() - .replace(ActiveTooltip::WaitingForShow { - _task: delayed_show_task, - }); - } - Action::CheckVisible => cx.notify(current_view), - } -} - -/// Returns a callback which will be called by window prepaint to update tooltip visibility. The -/// purpose of doing this logic here instead of the mouse move handler is that the mouse move -/// handler won't get called when the element is not painted (e.g. via use of `visible_on_hover`). -fn handle_tooltip_check_visible_and_update( - active_tooltip: &Rc>>, - tooltip_is_hoverable: bool, - check_is_hovered: &Rc bool>, - tooltip_bounds: Bounds, - window: &mut Window, - cx: &mut App, -) -> bool { - // Separates logic for what mutation should occur from applying it, to avoid overlapping RefCell - // borrows. - enum Action { - None, - Hide, - ScheduleHide(AnyTooltip), - CancelHide(AnyTooltip), - } - - let is_hovered = check_is_hovered(window) - || (tooltip_is_hoverable && tooltip_bounds.contains(&window.mouse_position())); - let action = match active_tooltip.borrow().as_ref() { - Some(ActiveTooltip::Visible { tooltip, .. }) => { - if is_hovered { - Action::None - } else { - if tooltip_is_hoverable { - Action::ScheduleHide(tooltip.clone()) - } else { - Action::Hide - } - } - } - Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => { - if is_hovered { - Action::CancelHide(tooltip.clone()) - } else { - Action::None - } - } - None | Some(ActiveTooltip::WaitingForShow { .. }) => Action::None, - }; - - match action { - Action::None => {} - Action::Hide => clear_active_tooltip(active_tooltip, window), - Action::ScheduleHide(tooltip) => { - let delayed_hide_task = window.spawn(cx, { - let weak_active_tooltip = Rc::downgrade(active_tooltip); - async move |cx| { - cx.background_executor() - .timer(HOVERABLE_TOOLTIP_HIDE_DELAY) - .await; - let Some(active_tooltip) = weak_active_tooltip.upgrade() else { - return; - }; - if active_tooltip.borrow_mut().take().is_some() { - cx.update(|window, _cx| window.refresh()).ok(); - } - } - }); - active_tooltip - .borrow_mut() - .replace(ActiveTooltip::WaitingForHide { - tooltip, - _task: delayed_hide_task, - }); - } - Action::CancelHide(tooltip) => { - // Cancel waiting to hide tooltip when it becomes hovered. - active_tooltip.borrow_mut().replace(ActiveTooltip::Visible { - tooltip, - is_hoverable: true, - }); - } - } - - active_tooltip.borrow().is_some() -} - -#[derive(Default)] -pub(crate) struct GroupHitboxes(HashMap>); - -impl Global for GroupHitboxes {} - -impl GroupHitboxes { - pub fn get(name: &SharedString, cx: &mut App) -> Option { - cx.default_global::() - .0 - .get(name) - .and_then(|bounds_stack| bounds_stack.last()) - .cloned() - } - - pub fn push(name: SharedString, hitbox_id: HitboxId, cx: &mut App) { - cx.default_global::() - .0 - .entry(name) - .or_default() - .push(hitbox_id); - } - - pub fn pop(name: &SharedString, cx: &mut App) { - cx.default_global::().0.get_mut(name).unwrap().pop(); - } -} - -/// A wrapper around an element that can store state, produced after assigning an ElementId. -pub struct Stateful { - pub(crate) element: E, -} - -impl Styled for Stateful -where - E: Styled, -{ - fn style(&mut self) -> &mut StyleRefinement { - self.element.style() - } -} - -impl StatefulInteractiveElement for Stateful -where - E: Element, - Self: InteractiveElement, -{ -} - -impl InteractiveElement for Stateful -where - E: InteractiveElement, -{ - fn interactivity(&mut self) -> &mut Interactivity { - self.element.interactivity() - } -} - -impl Element for Stateful -where - E: Element, -{ - type RequestLayoutState = E::RequestLayoutState; - type PrepaintState = E::PrepaintState; - - fn id(&self) -> Option { - self.element.id() - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - self.element.source_location() - } - - fn a11y_role(&self) -> Option { - self.element.a11y_role() - } - - fn write_a11y_info(&self, node: &mut accesskit::Node) { - self.element.write_a11y_info(node); - } - - fn a11y_synthetic_children( - &mut self, - prepaint: &mut Self::PrepaintState, - builder: &mut crate::A11ySubtreeBuilder, - ) { - self.element.a11y_synthetic_children(prepaint, builder); - } - - fn request_layout( - &mut self, - id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - self.element.request_layout(id, inspector_id, window, cx) - } - - fn prepaint( - &mut self, - id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - state: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> E::PrepaintState { - self.element - .prepaint(id, inspector_id, bounds, state, window, cx) - } - - fn paint( - &mut self, - id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - request_layout: &mut Self::RequestLayoutState, - prepaint: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - self.element.paint( - id, - inspector_id, - bounds, - request_layout, - prepaint, - window, - cx, - ); - } -} - -impl IntoElement for Stateful -where - E: Element, -{ - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -impl ParentElement for Stateful -where - E: ParentElement, -{ - fn extend(&mut self, elements: impl IntoIterator) { - self.element.extend(elements) - } -} - -/// Represents an element that can be scrolled *to* in its parent element. -/// Contrary to [ScrollHandle::scroll_to_active_item], an anchored element does not have to be an immediate child of the parent. -#[derive(Clone)] -pub struct ScrollAnchor { - handle: ScrollHandle, - last_origin: Rc>>, -} - -impl ScrollAnchor { - /// Creates a [ScrollAnchor] associated with a given [ScrollHandle]. - pub fn for_handle(handle: ScrollHandle) -> Self { - Self { - handle, - last_origin: Default::default(), - } - } - /// Request scroll to this item on the next frame. - pub fn scroll_to(&self, window: &mut Window, _cx: &mut App) { - let this = self.clone(); - - window.on_next_frame(move |_, _| { - let viewport_bounds = this.handle.bounds(); - let self_bounds = *this.last_origin.borrow(); - this.handle.set_offset(viewport_bounds.origin - self_bounds); - }); - } -} - -#[derive(Default, Debug)] -struct ScrollHandleState { - offset: Rc>>, - ongoing_scroll: Rc>, - bounds: Bounds, - max_offset: Point, - child_bounds: Vec>, - scroll_to_bottom: bool, - overflow: Point, - active_item: Option, -} - -#[derive(Default, Debug, Clone, Copy)] -struct ScrollActiveItem { - index: usize, - strategy: ScrollStrategy, -} - -#[derive(Default, Debug, Clone, Copy)] -enum ScrollStrategy { - #[default] - FirstVisible, - Top, -} - -/// A handle to the scrollable aspects of an element. -/// Used for accessing scroll state, like the current scroll offset, -/// and for mutating the scroll state, like scrolling to a specific child. -#[derive(Clone, Debug)] -pub struct ScrollHandle(Rc>); - -impl Default for ScrollHandle { - fn default() -> Self { - Self::new() - } -} - -impl ScrollHandle { - /// Construct a new scroll handle. - pub fn new() -> Self { - Self(Rc::default()) - } - - /// Get the current scroll offset. - pub fn offset(&self) -> Point { - *self.0.borrow().offset.borrow() - } - - /// Get the maximum scroll offset. - pub fn max_offset(&self) -> Point { - self.0.borrow().max_offset - } - - /// Get the top child that's scrolled into view. - pub fn top_item(&self) -> usize { - let state = self.0.borrow(); - let top = state.bounds.top() - state.offset.borrow().y; - - match state.child_bounds.binary_search_by(|bounds| { - if top < bounds.top() { - Ordering::Greater - } else if top > bounds.bottom() { - Ordering::Less - } else { - Ordering::Equal - } - }) { - Ok(ix) => ix, - Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)), - } - } - - /// Get the bottom child that's scrolled into view. - pub fn bottom_item(&self) -> usize { - let state = self.0.borrow(); - let bottom = state.bounds.bottom() - state.offset.borrow().y; - - match state.child_bounds.binary_search_by(|bounds| { - if bottom < bounds.top() { - Ordering::Greater - } else if bottom > bounds.bottom() { - Ordering::Less - } else { - Ordering::Equal - } - }) { - Ok(ix) => ix, - Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)), - } - } - - /// Return the bounds into which this child is painted - pub fn bounds(&self) -> Bounds { - self.0.borrow().bounds - } - - /// Get the bounds for a specific child. - pub fn bounds_for_item(&self, ix: usize) -> Option> { - self.0.borrow().child_bounds.get(ix).cloned() - } - - /// Update [ScrollHandleState]'s active item for scrolling to in prepaint - pub fn scroll_to_item(&self, ix: usize) { - let mut state = self.0.borrow_mut(); - state.active_item = Some(ScrollActiveItem { - index: ix, - strategy: ScrollStrategy::default(), - }); - } - - /// Update [ScrollHandleState]'s active item for scrolling to in prepaint - /// This scrolls the minimal amount to ensure that the child is the first visible element - pub fn scroll_to_top_of_item(&self, ix: usize) { - let mut state = self.0.borrow_mut(); - state.active_item = Some(ScrollActiveItem { - index: ix, - strategy: ScrollStrategy::Top, - }); - } - - /// Scrolls the minimal amount to either ensure that the child is - /// fully visible or the top element of the view depends on the - /// scroll strategy - fn scroll_to_active_item(&self) { - let mut state = self.0.borrow_mut(); - - let Some(active_item) = state.active_item else { - return; - }; - - let active_item = match state.child_bounds.get(active_item.index) { - Some(bounds) => { - let mut scroll_offset = state.offset.borrow_mut(); - - match active_item.strategy { - ScrollStrategy::FirstVisible => { - if state.overflow.y == Overflow::Scroll { - let child_height = bounds.size.height; - let viewport_height = state.bounds.size.height; - if child_height > viewport_height { - scroll_offset.y = state.bounds.top() - bounds.top(); - } else if bounds.top() + scroll_offset.y < state.bounds.top() { - scroll_offset.y = state.bounds.top() - bounds.top(); - } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() { - scroll_offset.y = state.bounds.bottom() - bounds.bottom(); - } - } - } - ScrollStrategy::Top => { - scroll_offset.y = state.bounds.top() - bounds.top(); - } - } - - if state.overflow.x == Overflow::Scroll { - let child_width = bounds.size.width; - let viewport_width = state.bounds.size.width; - if child_width > viewport_width { - scroll_offset.x = state.bounds.left() - bounds.left(); - } else if bounds.left() + scroll_offset.x < state.bounds.left() { - scroll_offset.x = state.bounds.left() - bounds.left(); - } else if bounds.right() + scroll_offset.x > state.bounds.right() { - scroll_offset.x = state.bounds.right() - bounds.right(); - } - } - None - } - None => Some(active_item), - }; - state.active_item = active_item; - } - - /// Scrolls to the bottom. - pub fn scroll_to_bottom(&self) { - let mut state = self.0.borrow_mut(); - state.scroll_to_bottom = true; - } - - /// Set the offset explicitly. The offset is the distance from the top left of the - /// parent container to the top left of the first child. - /// As you scroll further down the offset becomes more negative. - pub fn set_offset(&self, mut position: Point) { - let state = self.0.borrow(); - *state.offset.borrow_mut() = position; - } - - /// Get the logical scroll top, based on a child index and a pixel offset. - pub fn logical_scroll_top(&self) -> (usize, Pixels) { - let ix = self.top_item(); - let state = self.0.borrow(); - - if let Some(child_bounds) = state.child_bounds.get(ix) { - ( - ix, - child_bounds.top() + state.offset.borrow().y - state.bounds.top(), - ) - } else { - (ix, px(0.)) - } - } - - /// Get the logical scroll bottom, based on a child index and a pixel offset. - pub fn logical_scroll_bottom(&self) -> (usize, Pixels) { - let ix = self.bottom_item(); - let state = self.0.borrow(); - - if let Some(child_bounds) = state.child_bounds.get(ix) { - ( - ix, - child_bounds.bottom() + state.offset.borrow().y - state.bounds.bottom(), - ) - } else { - (ix, px(0.)) - } - } - - /// Get the count of children for scrollable item. - pub fn children_count(&self) -> usize { - self.0.borrow().child_bounds.len() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - canvas, util::FluentBuilder as _, AnyWindowHandle, AppContext as _, Context, InputEvent, - Keystroke, MouseMoveEvent, TestAppContext, - }; - use std::{cell::Cell, rc::Weak}; - - struct GroupHoverTestView { - render_count: Rc>, - anonymous_paint_count: Rc>, - stateful_width: Rc>, - } - - impl Render for GroupHoverTestView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - self.render_count.set(self.render_count.get() + 1); - let anonymous_paint_count = self.anonymous_paint_count.clone(); - let stateful_width = self.stateful_width.clone(); - div().size_full().child( - div() - .ml(px(20.)) - .mt(px(20.)) - .size(px(50.)) - .relative() - .group("hover-group") - .child( - div() - .absolute() - .size_full() - .invisible() - .group_hover("hover-group", |style| style.visible()) - .child(canvas( - |_, _, _| {}, - move |_, _, _, _| { - anonymous_paint_count.set(anonymous_paint_count.get() + 1) - }, - )), - ) - .child( - div() - .id("stateful-group-hover-target") - .absolute() - .top_0() - .left_0() - .size(px(10.)) - .group_hover("hover-group", |style| style.size(px(20.))) - .child(canvas( - move |bounds, _, _| stateful_width.set(bounds.size.width), - |_, _, _, _| {}, - )), - ), - ) - } - } - - #[gpui::test] - fn group_hover_styles_update_only_on_transitions(cx: &mut TestAppContext) { - let render_count = Rc::new(Cell::new(0)); - let anonymous_paint_count = Rc::new(Cell::new(0)); - let stateful_width = Rc::new(Cell::new(px(0.))); - let window = cx.add_window({ - let render_count = render_count.clone(); - let anonymous_paint_count = anonymous_paint_count.clone(); - let stateful_width = stateful_width.clone(); - move |_, _| GroupHoverTestView { - render_count, - anonymous_paint_count, - stateful_width, - } - }); - let window = AnyWindowHandle::from(window); - - cx.update_window(window, |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - assert_eq!(anonymous_paint_count.get(), 0); - assert_eq!(stateful_width.get(), px(10.)); - - let move_mouse = |cx: &mut TestAppContext, position| { - cx.update_window(window, |_, window, cx| { - window.simulate_mouse_move(position, cx) - }) - .unwrap(); - }; - - let initial_render_count = render_count.get(); - move_mouse(cx, point(px(25.), px(25.))); - assert_eq!(render_count.get(), initial_render_count + 1); - assert_eq!(anonymous_paint_count.get(), 1); - assert_eq!(stateful_width.get(), px(20.)); - - move_mouse(cx, point(px(30.), px(30.))); - assert_eq!(render_count.get(), initial_render_count + 1); - assert_eq!(anonymous_paint_count.get(), 1); - assert_eq!(stateful_width.get(), px(20.)); - - move_mouse(cx, point(px(5.), px(5.))); - assert_eq!(render_count.get(), initial_render_count + 2); - assert_eq!(anonymous_paint_count.get(), 1); - assert_eq!(stateful_width.get(), px(10.)); - - move_mouse(cx, point(px(10.), px(10.))); - assert_eq!(render_count.get(), initial_render_count + 2); - assert_eq!(anonymous_paint_count.get(), 1); - assert_eq!(stateful_width.get(), px(10.)); - } - - struct HoverListenerLayoutTestView { - target_left: Pixels, - hover_transitions: Rc>>, - } - - impl Render for HoverListenerLayoutTestView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let hover_transitions = self.hover_transitions.clone(); - div().relative().size_full().child( - div() - .id("hover-target") - .absolute() - .left(self.target_left) - .top_0() - .size(px(20.)) - .on_click(|_, _, _| {}) - .on_hover(move |is_hovered, _, _| { - hover_transitions.borrow_mut().push(*is_hovered); - }), - ) - } - } - - #[gpui::test] - fn default_hover_listener_updates_when_layout_changes_under_stationary_mouse( - cx: &mut TestAppContext, - ) { - let hover_transitions = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let hover_transitions = hover_transitions.clone(); - move |_, _| HoverListenerLayoutTestView { - target_left: px(40.), - hover_transitions, - } - }); - let any_window = AnyWindowHandle::from(window); - - cx.update_window(any_window, |_, window, cx| { - window.draw(cx).clear(cx); - window.simulate_mouse_move(point(px(10.), px(10.)), cx); - }) - .unwrap(); - assert!(hover_transitions.borrow().is_empty()); - - window - .update(cx, |view, _, cx| { - view.target_left = px(0.); - cx.notify(); - }) - .unwrap(); - cx.update_window(any_window, |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - assert_eq!(*hover_transitions.borrow(), [true]); - - window - .update(cx, |view, _, cx| { - view.target_left = px(40.); - cx.notify(); - }) - .unwrap(); - cx.update_window(any_window, |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - assert_eq!(*hover_transitions.borrow(), [true, false]); - } - - #[gpui::test] - fn default_hover_listener_ends_after_key_press(cx: &mut TestAppContext) { - let hover_transitions = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let hover_transitions = hover_transitions.clone(); - move |_, _| HoverListenerLayoutTestView { - target_left: px(0.), - hover_transitions, - } - }); - let any_window = AnyWindowHandle::from(window); - - cx.update_window(any_window, |_, window, cx| { - window.draw(cx).clear(cx); - window.simulate_mouse_move(point(px(10.), px(10.)), cx); - }) - .unwrap(); - assert_eq!(*hover_transitions.borrow(), [true]); - - key_down(cx, any_window, "a"); - cx.update_window(any_window, |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - assert_eq!(*hover_transitions.borrow(), [true, false]); - } - - struct HoverListenerModeLayoutTestView { - target_left: Pixels, - hover_transitions: Rc>>, - } - - impl Render for HoverListenerModeLayoutTestView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let hover_transitions = self.hover_transitions.clone(); - div().relative().size_full().child( - div() - .id("hover-target") - .absolute() - .left(self.target_left) - .top_0() - .size(px(20.)) - .hover_listener_mode(HoverListenerMode::InputModalityIndependent) - .on_hover(move |is_hovered, _, _| { - hover_transitions.borrow_mut().push(*is_hovered); - }), - ) - } - } - - #[gpui::test] - fn input_modality_independent_hover_listener_updates_after_key_press(cx: &mut TestAppContext) { - let hover_transitions = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let hover_transitions = hover_transitions.clone(); - move |_, _| HoverListenerModeLayoutTestView { - target_left: px(40.), - hover_transitions, - } - }); - let any_window = AnyWindowHandle::from(window); - let pointer_position = point(px(10.), px(10.)); - - cx.update_window(any_window, |_, window, cx| { - window.draw(cx).clear(cx); - window.simulate_mouse_move(pointer_position, cx); - }) - .unwrap(); - assert!(hover_transitions.borrow().is_empty()); - - key_down(cx, any_window, "a"); - window - .update(cx, |view, _, cx| { - view.target_left = px(0.); - cx.notify(); - }) - .unwrap(); - cx.update_window(any_window, |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - assert_eq!(*hover_transitions.borrow(), [true]); - - key_down(cx, any_window, "b"); - cx.update_window(any_window, |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - assert_eq!(*hover_transitions.borrow(), [true]); - - cx.update_window(any_window, |_, window, cx| { - window.simulate_mouse_move(point(px(30.), px(10.)), cx); - }) - .unwrap(); - assert_eq!(*hover_transitions.borrow(), [true, false]); - - cx.update_window(any_window, |_, window, cx| { - window.simulate_mouse_move(pointer_position, cx); - window.dispatch_event( - MouseExitEvent { - position: pointer_position, - ..Default::default() - } - .to_platform_input(), - cx, - ); - }) - .unwrap(); - assert_eq!(*hover_transitions.borrow(), [true, false, true, false]); - } - - #[gpui::test] - fn default_hover_listener_remains_hovered_during_stationary_mouse_press( - cx: &mut TestAppContext, - ) { - let hover_transitions = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let hover_transitions = hover_transitions.clone(); - move |_, _| HoverListenerLayoutTestView { - target_left: px(0.), - hover_transitions, - } - }); - let any_window = AnyWindowHandle::from(window); - let mouse_position = point(px(10.), px(10.)); - - cx.update_window(any_window, |_, window, cx| { - window.draw(cx).clear(cx); - window.simulate_mouse_move(mouse_position, cx); - }) - .unwrap(); - assert_eq!(*hover_transitions.borrow(), [true]); - - cx.update_window(any_window, |_, window, cx| { - window.dispatch_event( - MouseDownEvent { - position: mouse_position, - button: MouseButton::Left, - modifiers: Default::default(), - click_count: 1, - first_mouse: false, - } - .to_platform_input(), - cx, - ); - window.draw(cx).clear(cx); - }) - .unwrap(); - assert_eq!(*hover_transitions.borrow(), [true]); - - cx.update_window(any_window, |_, window, cx| { - window.dispatch_event( - MouseUpEvent { - position: mouse_position, - button: MouseButton::Left, - modifiers: Default::default(), - click_count: 1, - } - .to_platform_input(), - cx, - ); - window.draw(cx).clear(cx); - }) - .unwrap(); - assert_eq!(*hover_transitions.borrow(), [true]); - } - - struct TestTooltipView; - - impl Render for TestTooltipView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div().w(px(20.)).h(px(20.)).child("tooltip") - } - } - - type CapturedActiveTooltip = Rc>>>>>; - - struct TooltipCaptureElement { - child: AnyElement, - captured_active_tooltip: CapturedActiveTooltip, - } - - impl IntoElement for TooltipCaptureElement { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } - } - - impl Element for TooltipCaptureElement { - type RequestLayoutState = (); - type PrepaintState = (); - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - (self.child.request_layout(window, cx), ()) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Self::PrepaintState { - self.child.prepaint(window, cx); - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - _prepaint: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - self.child.paint(window, cx); - window.with_global_id("target".into(), |global_id, window| { - window.with_element_state::( - global_id, - |state, _window| { - let state = state.unwrap(); - *self.captured_active_tooltip.borrow_mut() = - state.active_tooltip.as_ref().map(Rc::downgrade); - ((), state) - }, - ) - }); - } - } - - struct TooltipOwner { - captured_active_tooltip: CapturedActiveTooltip, - show_delay_override: Option, - } - - impl Render for TooltipOwner { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - TooltipCaptureElement { - child: div() - .size_full() - .child( - div() - .id("target") - .w(px(50.)) - .h(px(50.)) - .tooltip(|_, cx| cx.new(|_| TestTooltipView).into()) - .when_some(self.show_delay_override, |this, delay| { - this.tooltip_show_delay(delay) - }), - ) - .into_any_element(), - captured_active_tooltip: self.captured_active_tooltip.clone(), - } - } - } - - #[test] - fn scroll_handle_aligns_wide_children_to_left_edge() { - let handle = ScrollHandle::new(); - { - let mut state = handle.0.borrow_mut(); - state.bounds = Bounds::new(point(px(0.), px(0.)), size(px(80.), px(20.))); - state.child_bounds = vec![Bounds::new(point(px(25.), px(0.)), size(px(200.), px(20.)))]; - state.overflow.x = Overflow::Scroll; - state.active_item = Some(ScrollActiveItem { - index: 0, - strategy: ScrollStrategy::default(), - }); - } - - handle.scroll_to_active_item(); - - assert_eq!(handle.offset().x, px(-25.)); - } - - #[test] - fn scroll_handle_aligns_tall_children_to_top_edge() { - let handle = ScrollHandle::new(); - { - let mut state = handle.0.borrow_mut(); - state.bounds = Bounds::new(point(px(0.), px(0.)), size(px(20.), px(80.))); - state.child_bounds = vec![Bounds::new(point(px(0.), px(25.)), size(px(20.), px(200.)))]; - state.overflow.y = Overflow::Scroll; - state.active_item = Some(ScrollActiveItem { - index: 0, - strategy: ScrollStrategy::default(), - }); - } - - handle.scroll_to_active_item(); - - assert_eq!(handle.offset().y, px(-25.)); - } - - fn setup_tooltip_owner_test( - show_delay_override: Option, - ) -> ( - TestAppContext, - crate::AnyWindowHandle, - CapturedActiveTooltip, - ) { - let mut test_app = TestAppContext::single(); - let captured_active_tooltip: CapturedActiveTooltip = Rc::new(RefCell::new(None)); - let window = test_app.add_window({ - let captured_active_tooltip = captured_active_tooltip.clone(); - move |_, _| TooltipOwner { - captured_active_tooltip, - show_delay_override, - } - }); - let any_window = window.into(); - - test_app - .update_window(any_window, |_, window, cx| { - window.draw(cx).clear(cx); - }) - .unwrap(); - - test_app - .update_window(any_window, |_, window, cx| { - window.dispatch_event( - MouseMoveEvent { - position: point(px(10.), px(10.)), - modifiers: Default::default(), - pressed_button: None, - } - .to_platform_input(), - cx, - ); - }) - .unwrap(); - - test_app - .update_window(any_window, |_, window, cx| { - window.draw(cx).clear(cx); - }) - .unwrap(); - - (test_app, any_window, captured_active_tooltip) - } - - #[test] - fn tooltip_waiting_for_show_is_released_when_its_owner_disappears() { - let (mut test_app, any_window, captured_active_tooltip) = setup_tooltip_owner_test(None); - - let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap(); - let active_tooltip = weak_active_tooltip.upgrade().unwrap(); - assert!(matches!( - active_tooltip.borrow().as_ref(), - Some(ActiveTooltip::WaitingForShow { .. }) - )); - - test_app - .update_window(any_window, |_, window, _| { - window.remove_window(); - }) - .unwrap(); - test_app.run_until_parked(); - drop(active_tooltip); - - assert!(weak_active_tooltip.upgrade().is_none()); - } - - #[test] - fn tooltip_respects_custom_show_delay() { - let extra_delay = Duration::from_secs(1); - let show_delay_override = DEFAULT_TOOLTIP_SHOW_DELAY + extra_delay; - let (mut test_app, _any_window, captured_active_tooltip) = - setup_tooltip_owner_test(Some(show_delay_override)); - - let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap(); - let active_tooltip = weak_active_tooltip.upgrade().unwrap(); - - test_app - .dispatcher - .advance_clock(DEFAULT_TOOLTIP_SHOW_DELAY); - test_app.run_until_parked(); - - assert!(matches!( - active_tooltip.borrow().as_ref(), - Some(ActiveTooltip::WaitingForShow { .. }) - )); - - test_app.dispatcher.advance_clock(extra_delay); - test_app.run_until_parked(); - - assert!(matches!( - active_tooltip.borrow().as_ref(), - Some(ActiveTooltip::Visible { .. }) - )); - } - - #[test] - fn tooltip_is_released_when_its_owner_disappears() { - let (mut test_app, any_window, captured_active_tooltip) = setup_tooltip_owner_test(None); - - let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap(); - let active_tooltip = weak_active_tooltip.upgrade().unwrap(); - - test_app - .dispatcher - .advance_clock(DEFAULT_TOOLTIP_SHOW_DELAY); - test_app.run_until_parked(); - - assert!(matches!( - active_tooltip.borrow().as_ref(), - Some(ActiveTooltip::Visible { .. }) - )); - - test_app - .update_window(any_window, |_, window, _| { - window.remove_window(); - }) - .unwrap(); - test_app.run_until_parked(); - drop(active_tooltip); - - assert!(weak_active_tooltip.upgrade().is_none()); - } - - #[test] - fn tooltip_hides_after_mouse_leaves_origin() { - let (mut test_app, any_window, captured_active_tooltip) = setup_tooltip_owner_test(None); - - let weak_active_tooltip = captured_active_tooltip.borrow().clone().unwrap(); - let active_tooltip = weak_active_tooltip.upgrade().unwrap(); - - test_app - .dispatcher - .advance_clock(DEFAULT_TOOLTIP_SHOW_DELAY); - test_app.run_until_parked(); - - assert!(matches!( - active_tooltip.borrow().as_ref(), - Some(ActiveTooltip::Visible { .. }) - )); - - test_app - .update_window(any_window, |_, window, cx| { - window.dispatch_event( - MouseMoveEvent { - position: point(px(75.), px(75.)), - modifiers: Default::default(), - pressed_button: None, - } - .to_platform_input(), - cx, - ); - }) - .unwrap(); - - assert!(active_tooltip.borrow().is_none()); - } - - struct MouseDownOutOwner { - mouse_down_out_count: Rc>, - } - - impl Render for MouseDownOutOwner { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let mouse_down_out_count = self.mouse_down_out_count.clone(); - div() - .size_full() - .child(div().id("target").w(px(50.)).h(px(50.)).on_mouse_down_out( - move |_, _, _| { - *mouse_down_out_count.borrow_mut() += 1; - }, - )) - } - } - - #[test] - fn mouse_down_out_is_suppressed_while_window_prompt_is_active() { - let mut test_app = TestAppContext::single(); - let mouse_down_out_count = Rc::new(RefCell::new(0)); - let window = test_app.add_window({ - let mouse_down_out_count = mouse_down_out_count.clone(); - move |_, _| MouseDownOutOwner { - mouse_down_out_count, - } - }); - let any_window: AnyWindowHandle = window.into(); - - fn dispatch_mouse_down_outside_target( - test_app: &mut TestAppContext, - any_window: AnyWindowHandle, - ) { - test_app - .update_window(any_window, |_, window, cx| { - window.dispatch_event( - MouseDownEvent { - position: point(px(75.), px(75.)), - button: MouseButton::Left, - modifiers: Default::default(), - click_count: 1, - first_mouse: false, - } - .to_platform_input(), - cx, - ); - }) - .unwrap(); - } - - test_app - .update_window(any_window, |_, window, cx| { - window.draw(cx).clear(cx); - }) - .unwrap(); - - dispatch_mouse_down_outside_target(&mut test_app, any_window); - assert_eq!( - *mouse_down_out_count.borrow(), - 1, - "mouse down outside the element should fire mouse-down-out listeners" - ); - - test_app - .update_window(any_window, |_, window, cx| { - cx.set_prompt_builder(crate::fallback_prompt_renderer); - let _receiver = - window.prompt(crate::PromptLevel::Warning, "message", None, &["Ok"], cx); - assert!(window.has_active_prompt()); - window.draw(cx).clear(cx); - }) - .unwrap(); - - dispatch_mouse_down_outside_target(&mut test_app, any_window); - assert_eq!( - *mouse_down_out_count.borrow(), - 1, - "mouse down over an active prompt should not fire mouse-down-out listeners" - ); - } - - #[test] - fn test_accessibility_id_builder_writes_author_id() { - let mut element = div() - .id("buffer-font-size") - .accessibility_id("settings.buffer-font-size"); - let mut node = accesskit::Node::new(accesskit::Role::SpinButton); - - element.interactivity().write_a11y_info(&mut node); - - assert_eq!(node.author_id(), Some("settings.buffer-font-size")); - } - - #[test] - fn test_write_a11y_info_string_and_numeric_properties() { - let mut interactivity = Interactivity::default(); - interactivity.aria.author_id = Some("settings.buffer-font-size".into()); - interactivity.aria.label = Some("Buffer Font Size".into()); - interactivity.aria.value = Some("15".into()); - interactivity.aria.placeholder = Some("Search".into()); - interactivity.aria.numeric_value = Some(15.0); - interactivity.aria.min_numeric_value = Some(6.0); - interactivity.aria.max_numeric_value = Some(72.0); - interactivity.aria.numeric_value_step = Some(1.0); - - let mut node = accesskit::Node::new(accesskit::Role::SpinButton); - interactivity.write_a11y_info(&mut node); - - assert_eq!(node.author_id(), Some("settings.buffer-font-size")); - assert_eq!(node.label(), Some("Buffer Font Size")); - assert_eq!(node.value(), Some("15")); - assert_eq!(node.placeholder(), Some("Search")); - assert_eq!(node.numeric_value(), Some(15.0)); - assert_eq!(node.min_numeric_value(), Some(6.0)); - assert_eq!(node.max_numeric_value(), Some(72.0)); - assert_eq!(node.numeric_value_step(), Some(1.0)); - } - - #[test] - fn test_write_a11y_info_aria_current() { - let mut interactivity = Interactivity::default(); - interactivity.aria.current = Some(accesskit::AriaCurrent::Page); - - let mut node = accesskit::Node::new(accesskit::Role::Link); - interactivity.write_a11y_info(&mut node); - - assert_eq!(node.aria_current(), Some(accesskit::AriaCurrent::Page)); - } - - /// Two focusable, clickable elements ("a" and "b") used to exercise the - /// Enter/Space -> synthesized click press/release pairing. - struct KeyboardActivationTest { - focus_a: FocusHandle, - focus_b: FocusHandle, - clicks: Rc>>, - } - - impl Render for KeyboardActivationTest { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let clicks_a = self.clicks.clone(); - let clicks_b = self.clicks.clone(); - div() - .size_full() - .child( - div() - .id("a") - .w(px(50.)) - .h(px(50.)) - .track_focus(&self.focus_a) - .on_click(move |_, _, _| clicks_a.borrow_mut().push("a")), - ) - .child( - div() - .id("b") - .w(px(50.)) - .h(px(50.)) - .track_focus(&self.focus_b) - .on_click(move |_, _, _| clicks_b.borrow_mut().push("b")), - ) - } - } - - fn setup_keyboard_activation_test() -> ( - TestAppContext, - AnyWindowHandle, - Rc>>, - FocusHandle, - FocusHandle, - ) { - let mut cx = TestAppContext::single(); - let (focus_a, focus_b) = cx.update(|cx| (cx.focus_handle(), cx.focus_handle())); - let clicks: Rc>> = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let focus_a = focus_a.clone(); - let focus_b = focus_b.clone(); - let clicks = clicks.clone(); - move |_, _| KeyboardActivationTest { - focus_a, - focus_b, - clicks, - } - }); - (cx, window.into(), clicks, focus_a, focus_b) - } - - /// Move focus to `handle`, flush effects, then paint so the newly focused - /// element registers its key handlers for the next dispatched event. - fn focus_and_draw(cx: &mut TestAppContext, window: AnyWindowHandle, handle: &FocusHandle) { - cx.update_window(window, |_, window, cx| window.focus(handle, cx)) - .unwrap(); - cx.run_until_parked(); - cx.update_window(window, |_, window, cx| { - window.draw(cx).clear(cx); - }) - .unwrap(); - } - - fn key_down(cx: &mut TestAppContext, window: AnyWindowHandle, key: &str) { - let keystroke = Keystroke::parse(key).unwrap(); - cx.update_window(window, |_, window, cx| { - window.dispatch_event( - KeyDownEvent { - keystroke, - is_held: false, - prefer_character_input: false, - } - .to_platform_input(), - cx, - ); - }) - .unwrap(); - } - - fn key_up(cx: &mut TestAppContext, window: AnyWindowHandle, key: &str) { - let keystroke = Keystroke::parse(key).unwrap(); - cx.update_window(window, |_, window, cx| { - window.dispatch_event(KeyUpEvent { keystroke }.to_platform_input(), cx); - }) - .unwrap(); - } - - /// Pressing and releasing Enter on the same focused element fires a click. - #[test] - fn keyboard_activation_fires_click_on_same_element() { - let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test(); - - focus_and_draw(&mut cx, window, &focus_a); - key_down(&mut cx, window, "enter"); - key_up(&mut cx, window, "enter"); - - assert_eq!(*clicks.borrow(), vec!["a"]); - } - - /// A key-down whose key-up lands on a *different* element (because focus - /// moved in between) must not leak a synthesized click onto the newly - /// focused element. This is the core regression: previously the key-up - /// handler fired unconditionally on whatever was focused at key-up time. - #[test] - fn keyboard_activation_does_not_leak_across_focus_change() { - let (mut cx, window, clicks, focus_a, focus_b) = setup_keyboard_activation_test(); - - // Enter pressed while "a" is focused... - focus_and_draw(&mut cx, window, &focus_a); - key_down(&mut cx, window, "enter"); - - // ...focus moves to "b" before the release (as a confirm action would)... - focus_and_draw(&mut cx, window, &focus_b); - key_up(&mut cx, window, "enter"); - - // ...so neither element is clicked: "a" never saw the up, and "b" - // never saw the down. - assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow()); - } - - /// A keydown whose flag is left pending because focus moved away before - /// the keyup must not fire a click when focus later *returns* to the same - /// element (the menu trigger reopening case). The stamped focus generation - /// no longer matches, so the stale pending state is ignored. - #[test] - fn keyboard_activation_does_not_leak_when_focus_returns() { - let (mut cx, window, clicks, focus_a, focus_b) = setup_keyboard_activation_test(); - - // Enter pressed on "a"... - focus_and_draw(&mut cx, window, &focus_a); - key_down(&mut cx, window, "enter"); - - // ...focus leaves "a" before its keyup (so the pending state is never - // consumed), then comes back to "a"... - focus_and_draw(&mut cx, window, &focus_b); - focus_and_draw(&mut cx, window, &focus_a); - key_up(&mut cx, window, "enter"); - - // ...and the now-stale pending keydown must not fire a click. - assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow()); - } - - /// A non-activation key *released* during the press must cancel the pending - /// activation. For the sequence escape-down, space-down, escape-up, - /// space-up the space forms a clean down/up pair, but the intervening - /// escape-up means this isn't a plain space activation, so no click fires. - #[test] - fn keyboard_activation_cleared_by_intervening_key_release() { - let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test(); - - focus_and_draw(&mut cx, window, &focus_a); - key_down(&mut cx, window, "escape"); - key_down(&mut cx, window, "space"); - key_up(&mut cx, window, "escape"); - key_up(&mut cx, window, "space"); - - assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow()); - } - - /// The flag is a single activation marker, not keyed by which activation - /// key was used, so a Space down paired with an Enter up on the same - /// element still fires a click. - #[test] - fn keyboard_activation_does_not_distinguish_space_and_enter() { - let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test(); - - focus_and_draw(&mut cx, window, &focus_a); - key_down(&mut cx, window, "space"); - key_up(&mut cx, window, "enter"); - - assert_eq!(*clicks.borrow(), vec!["a"]); - } - - /// A non-activation key pressed between the activation down and up clears - /// the pending flag, suppressing the click. - #[test] - fn keyboard_activation_cleared_by_intervening_keydown() { - let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test(); - - focus_and_draw(&mut cx, window, &focus_a); - key_down(&mut cx, window, "enter"); - key_down(&mut cx, window, "a"); - key_up(&mut cx, window, "enter"); - - assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow()); - } - - /// A modified Enter (e.g. cmd-enter) is not treated as an activation key, - /// so it neither sets the pending flag nor fires a click on release. - #[test] - fn keyboard_activation_ignores_modified_keys() { - let (mut cx, window, clicks, focus_a, _focus_b) = setup_keyboard_activation_test(); - - focus_and_draw(&mut cx, window, &focus_a); - key_down(&mut cx, window, "cmd-enter"); - key_up(&mut cx, window, "cmd-enter"); - - assert!(clicks.borrow().is_empty(), "clicks: {:?}", clicks.borrow()); - } - - /// Two sibling tab groups, each a focusable container that is *not* itself a - /// tab stop and holds a single tab stop. Mirrors how the title bar and - /// status bar expose their controls as ARIA toolbars. - struct TabGroupFocus { - group_a: FocusHandle, - item_a: FocusHandle, - group_b: FocusHandle, - item_b: FocusHandle, - } - - impl Render for TabGroupFocus { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - fn group(container: &FocusHandle, item: &FocusHandle) -> Div { - div() - .track_focus(container) - .tab_group() - .child(div().track_focus(item)) - } - div() - .child(group(&self.group_a, &self.item_a)) - .child(group(&self.group_b, &self.item_b)) - } - } - - /// Focusing a tab-group container and pressing Tab (`focus_next`) must move - /// focus to the first tab stop *inside that container*, as documented on - /// [`InteractiveElement::tab_stop`]. - #[test] - fn focus_next_from_tab_group_container_enters_that_group() { - let mut cx = TestAppContext::single(); - let (group_a, item_a, group_b, item_b) = cx.update(|cx| { - ( - cx.focus_handle(), - cx.focus_handle().tab_stop(true), - cx.focus_handle(), - cx.focus_handle().tab_stop(true), - ) - }); - let window: AnyWindowHandle = cx - .add_window({ - let (group_a, item_a, group_b, item_b) = - (group_a, item_a, group_b.clone(), item_b.clone()); - move |_, _| TabGroupFocus { - group_a, - item_a, - group_b, - item_b, - } - }) - .into(); - cx.update_window(window, |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - - // Focus the *second* group's container, then advance like Tab would. - let focused = cx - .update_window(window, |_, window, cx| { - window.focus(&group_b, cx); - window.focus_next(cx); - window.focused(cx).map(|handle| handle.id) - }) - .unwrap(); - - assert_eq!(focused, Some(item_b.id)); - } - - #[gpui::test] - fn test_fractional_padding_does_not_make_a_fitting_container_scrollable( - cx: &mut TestAppContext, - ) { - struct PaddedContainer { - scroll_handle: ScrollHandle, - } - - impl Render for PaddedContainer { - fn render( - &mut self, - _window: &mut Window, - _cx: &mut Context, - ) -> impl IntoElement { - // 4.25px of padding snaps to 4px in layout, so a 42px child - // fits the 50px box exactly. - div().size_full().child( - div() - .id("container") - .h(px(50.)) - .w(px(100.)) - .py(px(4.25)) - .overflow_y_scroll() - .track_scroll(&self.scroll_handle) - .child(div().w_full().h(px(42.))), - ) - } - } - - let scroll_handle = ScrollHandle::new(); - let window: AnyWindowHandle = cx - .add_window({ - let scroll_handle = scroll_handle.clone(); - move |_, _| PaddedContainer { scroll_handle } - }) - .into(); - cx.update_window(window, |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - - assert_eq!(scroll_handle.max_offset().y, px(0.)); - } - - struct ContentSizedGrid; - - impl Render for ContentSizedGrid { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let widths = [px(100.), px(200.), px(50.)]; - div().size_full().child( - div() - .w_full() - .grid() - .grid_cols_max_content(widths.len() as u16) - .children(widths.into_iter().enumerate().map(|(index, width)| { - div() - .debug_selector(move || format!("cell-{index}")) - .w(width) - .h(px(10.)) - })), - ) - } - } - - #[gpui::test] - fn grid_cols_max_content_sizes_columns_to_their_content(cx: &mut TestAppContext) { - let window = cx.add_window(|_, _| ContentSizedGrid); - cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - - let mut bounds = |selector: &'static str| { - cx.update_window(window.into(), |_, window, _| { - window.rendered_frame.debug_bounds.get(selector).copied() - }) - .unwrap() - .unwrap_or_else(|| panic!("{selector} was not rendered")) - }; - - assert_eq!(bounds("cell-0").origin.x, px(0.)); - assert_eq!(bounds("cell-1").origin.x, px(100.)); - assert_eq!(bounds("cell-2").origin.x, px(300.)); - } -} diff --git a/crates/gpui_pre/src/elements/image_cache.rs b/crates/gpui_pre/src/elements/image_cache.rs deleted file mode 100644 index ee143613..00000000 --- a/crates/gpui_pre/src/elements/image_cache.rs +++ /dev/null @@ -1,353 +0,0 @@ -use crate::{ - AnyElement, AnyEntity, App, AppContext, Asset, AssetLogger, Bounds, Element, ElementId, Entity, - GlobalElementId, ImageAssetLoader, ImageCacheError, InspectorElementId, IntoElement, LayoutId, - ParentElement, Pixels, RenderImage, Resource, Style, StyleRefinement, Styled, Task, Window, - hash, -}; - -use futures::{FutureExt, future::Shared}; -use refineable::Refineable; -use smallvec::SmallVec; -use std::{collections::HashMap, fmt, sync::Arc}; - -/// An image cache element, all its child img elements will use the cache specified by this element. -/// Note that this could as simple as passing an `Entity` -pub fn image_cache(image_cache_provider: impl ImageCacheProvider) -> ImageCacheElement { - ImageCacheElement { - image_cache_provider: Box::new(image_cache_provider), - style: StyleRefinement::default(), - children: SmallVec::default(), - } -} - -/// A dynamically typed image cache, which can be used to store any image cache -#[derive(Clone)] -pub struct AnyImageCache { - image_cache: AnyEntity, - load_fn: fn( - image_cache: &AnyEntity, - resource: &Resource, - window: &mut Window, - cx: &mut App, - ) -> Option, ImageCacheError>>, -} - -impl From> for AnyImageCache { - fn from(image_cache: Entity) -> Self { - Self { - image_cache: image_cache.into_any(), - load_fn: any_image_cache::load::, - } - } -} - -impl AnyImageCache { - /// Load an image given a resource - /// returns the result of loading the image if it has finished loading, or None if it is still loading - pub fn load( - &self, - resource: &Resource, - window: &mut Window, - cx: &mut App, - ) -> Option, ImageCacheError>> { - (self.load_fn)(&self.image_cache, resource, window, cx) - } -} - -mod any_image_cache { - use super::*; - - pub(crate) fn load( - image_cache: &AnyEntity, - resource: &Resource, - window: &mut Window, - cx: &mut App, - ) -> Option, ImageCacheError>> { - let image_cache = image_cache.clone().downcast::().unwrap(); - image_cache.update(cx, |image_cache, cx| image_cache.load(resource, window, cx)) - } -} - -/// An image cache element. -pub struct ImageCacheElement { - image_cache_provider: Box, - style: StyleRefinement, - children: SmallVec<[AnyElement; 2]>, -} - -impl ParentElement for ImageCacheElement { - fn extend(&mut self, elements: impl IntoIterator) { - self.children.extend(elements) - } -} - -impl Styled for ImageCacheElement { - fn style(&mut self) -> &mut StyleRefinement { - &mut self.style - } -} - -impl IntoElement for ImageCacheElement { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -impl Element for ImageCacheElement { - type RequestLayoutState = SmallVec<[LayoutId; 4]>; - type PrepaintState = (); - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - let image_cache = self.image_cache_provider.provide(window, cx); - window.with_image_cache(Some(image_cache), |window| { - let child_layout_ids = self - .children - .iter_mut() - .map(|child| child.request_layout(window, cx)) - .collect::>(); - let mut style = Style::default(); - style.refine(&self.style); - let layout_id = window.request_layout(style, child_layout_ids.iter().copied(), cx); - (layout_id, child_layout_ids) - }) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Self::PrepaintState { - for child in &mut self.children { - child.prepaint(window, cx); - } - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - _prepaint: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - let image_cache = self.image_cache_provider.provide(window, cx); - window.with_image_cache(Some(image_cache), |window| { - for child in &mut self.children { - child.paint(window, cx); - } - }) - } -} - -/// An image loading task associated with an image cache. -pub type ImageLoadingTask = Shared, ImageCacheError>>>; - -/// An image cache item -pub enum ImageCacheItem { - /// The associated image is currently loading - Loading(ImageLoadingTask), - /// This item has loaded an image. - Loaded(Result, ImageCacheError>), -} - -impl std::fmt::Debug for ImageCacheItem { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let status = match self { - ImageCacheItem::Loading(_) => &"Loading...".to_string(), - ImageCacheItem::Loaded(render_image) => &format!("{:?}", render_image), - }; - f.debug_struct("ImageCacheItem") - .field("status", status) - .finish() - } -} - -impl ImageCacheItem { - /// Attempt to get the image from the cache item. - pub fn get(&mut self) -> Option, ImageCacheError>> { - match self { - ImageCacheItem::Loading(task) => { - let res = task.now_or_never()?; - *self = ImageCacheItem::Loaded(res.clone()); - Some(res) - } - ImageCacheItem::Loaded(res) => Some(res.clone()), - } - } -} - -/// An object that can handle the caching and unloading of images. -/// Implementations of this trait should ensure that images are removed from all windows when they are no longer needed. -pub trait ImageCache: 'static { - /// Load an image given a resource - /// returns the result of loading the image if it has finished loading, or None if it is still loading - fn load( - &mut self, - resource: &Resource, - window: &mut Window, - cx: &mut App, - ) -> Option, ImageCacheError>>; -} - -/// An object that can create an ImageCache during the render phase. -/// See the ImageCache trait for more information. -pub trait ImageCacheProvider: 'static { - /// Called during the request_layout phase to create an ImageCache. - fn provide(&mut self, _window: &mut Window, _cx: &mut App) -> AnyImageCache; -} - -impl ImageCacheProvider for Entity { - fn provide(&mut self, _window: &mut Window, _cx: &mut App) -> AnyImageCache { - self.clone().into() - } -} - -/// An implementation of ImageCache, that uses an LRU caching strategy to unload images when the cache is full -pub struct RetainAllImageCache(HashMap); - -impl fmt::Debug for RetainAllImageCache { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("HashMapImageCache") - .field("num_images", &self.0.len()) - .finish() - } -} - -impl RetainAllImageCache { - /// Create a new image cache. - #[inline] - pub fn new(cx: &mut App) -> Entity { - let e = cx.new(|_cx| RetainAllImageCache(HashMap::new())); - cx.observe_release(&e, |image_cache, cx| { - for (_, mut item) in std::mem::replace(&mut image_cache.0, HashMap::new()) { - if let Some(Ok(image)) = item.get() { - cx.drop_image(image, None); - } - } - }) - .detach(); - e - } - - /// Load an image from the given source. - /// - /// Returns `None` if the image is loading. - pub fn load( - &mut self, - source: &Resource, - window: &mut Window, - cx: &mut App, - ) -> Option, ImageCacheError>> { - let hash = hash(source); - - if let Some(item) = self.0.get_mut(&hash) { - return item.get(); - } - - let fut = AssetLogger::::load(source.clone(), cx); - let task = cx.background_executor().spawn(fut).shared(); - self.0.insert(hash, ImageCacheItem::Loading(task.clone())); - - let entity = window.current_view(); - window - .spawn(cx, { - async move |cx| { - _ = task.await; - cx.on_next_frame(move |_, cx| { - cx.notify(entity); - }); - } - }) - .detach(); - - None - } - - /// Clear the image cache. - pub fn clear(&mut self, window: &mut Window, cx: &mut App) { - for (_, mut item) in std::mem::replace(&mut self.0, HashMap::new()) { - if let Some(Ok(image)) = item.get() { - cx.drop_image(image, Some(window)); - } - } - } - - /// Remove the image from the cache by the given source. - pub fn remove(&mut self, source: &Resource, window: &mut Window, cx: &mut App) { - let hash = hash(source); - if let Some(mut item) = self.0.remove(&hash) - && let Some(Ok(image)) = item.get() - { - cx.drop_image(image, Some(window)); - } - } - - /// Returns the number of images in the cache. - pub fn len(&self) -> usize { - self.0.len() - } - - /// Returns true if the cache is empty. - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } -} - -impl ImageCache for RetainAllImageCache { - fn load( - &mut self, - resource: &Resource, - window: &mut Window, - cx: &mut App, - ) -> Option, ImageCacheError>> { - RetainAllImageCache::load(self, resource, window, cx) - } -} - -/// Constructs a retain-all image cache that uses the element state associated with the given ID. -pub fn retain_all(id: impl Into) -> RetainAllImageCacheProvider { - RetainAllImageCacheProvider { id: id.into() } -} - -/// A provider struct for creating a retain-all image cache inline -pub struct RetainAllImageCacheProvider { - id: ElementId, -} - -impl ImageCacheProvider for RetainAllImageCacheProvider { - fn provide(&mut self, window: &mut Window, cx: &mut App) -> AnyImageCache { - window - .with_global_id(self.id.clone(), |global_id, window| { - window.with_element_state::, _>( - global_id, - |cache, _window| { - let mut cache = cache.unwrap_or_else(|| RetainAllImageCache::new(cx)); - (cache.clone(), cache) - }, - ) - }) - .into() - } -} diff --git a/crates/gpui_pre/src/elements/img.rs b/crates/gpui_pre/src/elements/img.rs deleted file mode 100644 index 2c77e5f3..00000000 --- a/crates/gpui_pre/src/elements/img.rs +++ /dev/null @@ -1,983 +0,0 @@ -use crate::{ - AnyElement, AnyImageCache, App, Asset, AssetLogger, Bounds, DefiniteLength, Element, ElementId, - Entity, GlobalElementId, Hitbox, Image, ImageCache, InspectorElementId, InteractiveElement, - Interactivity, IntoElement, LayoutId, Length, ObjectFit, Pixels, RenderImage, Resource, - SharedString, SharedUri, StyleRefinement, Styled, Task, Window, decode_static_image, - decode_static_image_from_decoder, px, -}; -use anyhow::Result; - -use futures::Future; -use gpui_util::ResultExt; -use image::{ - AnimationDecoder, ImageError, ImageFormat, Rgba, - codecs::{gif::GifDecoder, webp::WebPDecoder}, -}; -use scheduler::Instant; -use smallvec::SmallVec; -use std::{ - fs, - io::{self, Cursor}, - ops::{Deref, DerefMut}, - path::{Path, PathBuf}, - str::FromStr, - sync::Arc, - time::Duration, -}; -use thiserror::Error; - -use super::{Stateful, StatefulInteractiveElement}; - -/// The delay before showing the loading state. -pub const LOADING_DELAY: Duration = Duration::from_millis(200); - -/// A type alias to the resource loader that the `img()` element uses. -/// -/// Note: that this is only for Resources, like URLs or file paths. -/// Custom loaders, or external images will not use this asset loader -pub type ImgResourceLoader = AssetLogger; - -/// A source of image content. -#[derive(Clone)] -pub enum ImageSource { - /// The image content will be loaded from some resource location - Resource(Resource), - /// Cached image data - Render(Arc), - /// Cached image data - Image(Arc), - /// A custom loading function to use - Custom(Arc Option, ImageCacheError>>>), -} - -fn is_uri(uri: &str) -> bool { - url::Url::from_str(uri).is_ok() -} - -impl From for ImageSource { - fn from(value: SharedUri) -> Self { - Self::Resource(Resource::Uri(value)) - } -} - -impl<'a> From<&'a str> for ImageSource { - fn from(s: &'a str) -> Self { - if is_uri(s) { - Self::Resource(Resource::Uri(s.to_string().into())) - } else { - Self::Resource(Resource::Embedded(s.to_string().into())) - } - } -} - -impl From for ImageSource { - fn from(s: String) -> Self { - if is_uri(&s) { - Self::Resource(Resource::Uri(s.into())) - } else { - Self::Resource(Resource::Embedded(s.into())) - } - } -} - -impl From for ImageSource { - fn from(s: SharedString) -> Self { - s.as_ref().into() - } -} - -impl From<&Path> for ImageSource { - fn from(value: &Path) -> Self { - Self::Resource(value.to_path_buf().into()) - } -} - -impl From> for ImageSource { - fn from(value: Arc) -> Self { - Self::Resource(value.into()) - } -} - -impl From for ImageSource { - fn from(value: PathBuf) -> Self { - Self::Resource(value.into()) - } -} - -impl From> for ImageSource { - fn from(value: Arc) -> Self { - Self::Render(value) - } -} - -impl From> for ImageSource { - fn from(value: Arc) -> Self { - Self::Image(value) - } -} - -impl From for ImageSource -where - F: Fn(&mut Window, &mut App) -> Option, ImageCacheError>> + 'static, -{ - fn from(value: F) -> Self { - Self::Custom(Arc::new(value)) - } -} - -/// The style of an image element. -pub struct ImageStyle { - grayscale: bool, - object_fit: ObjectFit, - loading: Option AnyElement>>, - fallback: Option AnyElement>>, -} - -impl Default for ImageStyle { - fn default() -> Self { - Self { - grayscale: false, - object_fit: ObjectFit::Contain, - loading: None, - fallback: None, - } - } -} - -/// Style an image element. -pub trait StyledImage: Sized { - /// Get a mutable [ImageStyle] from the element. - fn image_style(&mut self) -> &mut ImageStyle; - - /// Set the image to be displayed in grayscale. - fn grayscale(mut self, grayscale: bool) -> Self { - self.image_style().grayscale = grayscale; - self - } - - /// Set the object fit for the image. - fn object_fit(mut self, object_fit: ObjectFit) -> Self { - self.image_style().object_fit = object_fit; - self - } - - /// Set a fallback function that will be invoked to render an error view should - /// the image fail to load. - fn with_fallback(mut self, fallback: impl Fn() -> AnyElement + 'static) -> Self { - self.image_style().fallback = Some(Box::new(fallback)); - self - } - - /// Set a fallback function that will be invoked to render a view while the image - /// is still being loaded. - fn with_loading(mut self, loading: impl Fn() -> AnyElement + 'static) -> Self { - self.image_style().loading = Some(Box::new(loading)); - self - } -} - -impl StyledImage for Img { - fn image_style(&mut self) -> &mut ImageStyle { - &mut self.style - } -} - -impl StyledImage for Stateful { - fn image_style(&mut self) -> &mut ImageStyle { - &mut self.element.style - } -} - -/// An image element. -pub struct Img { - interactivity: Interactivity, - source: ImageSource, - style: ImageStyle, - image_cache: Option, -} - -/// Create a new image element. -#[track_caller] -pub fn img(source: impl Into) -> Img { - Img { - interactivity: Interactivity::new(), - source: source.into(), - style: ImageStyle::default(), - image_cache: None, - } -} - -impl Img { - /// A list of all format extensions currently supported by this img element - pub fn extensions() -> &'static [&'static str] { - // This is the list in [image::ImageFormat::from_extension] + `svg` - &[ - "avif", "jpg", "jpeg", "png", "gif", "webp", "tif", "tiff", "tga", "dds", "bmp", "ico", - "hdr", "exr", "pbm", "pam", "ppm", "pgm", "ff", "farbfeld", "qoi", "svg", - ] - } - - /// Sets the image cache for the current node. - /// - /// If the `image_cache` is not explicitly provided, the function will determine the image cache by: - /// - /// 1. Checking if any ancestor node of the current node contains an `ImageCacheElement`, If such a node exists, the image cache specified by that ancestor will be used. - /// 2. If no ancestor node contains an `ImageCacheElement`, the global image cache will be used as a fallback. - /// - /// This mechanism provides a flexible way to manage image caching, allowing precise control when needed, - /// while ensuring a default behavior when no cache is explicitly specified. - #[inline] - pub fn image_cache(self, image_cache: &Entity) -> Self { - Self { - image_cache: Some(image_cache.clone().into()), - ..self - } - } -} - -impl Deref for Stateful { - type Target = Img; - - fn deref(&self) -> &Self::Target { - &self.element - } -} - -impl DerefMut for Stateful { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.element - } -} - -/// The image state between frames -struct ImgState { - frame_index: usize, - last_frame_time: Option, - started_loading: Option<(Instant, Task<()>)>, -} - -/// The image layout state between frames -pub struct ImgLayoutState { - frame_index: usize, - replacement: Option, -} - -impl Element for Img { - type RequestLayoutState = ImgLayoutState; - type PrepaintState = Option; - - fn id(&self) -> Option { - self.interactivity.element_id.clone() - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - self.interactivity.source_location() - } - - fn request_layout( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - let mut layout_state = ImgLayoutState { - frame_index: 0, - replacement: None, - }; - - window.with_optional_element_state(global_id, |state, window| { - let mut state = state.map(|state| { - state.unwrap_or(ImgState { - frame_index: 0, - last_frame_time: None, - started_loading: None, - }) - }); - - let mut frame_index = state.as_ref().map(|state| state.frame_index).unwrap_or(0); - - let layout_id = self.interactivity.request_layout( - global_id, - inspector_id, - window, - cx, - |mut style, window, cx| { - let mut replacement_id = None; - - match self.source.use_data( - self.image_cache - .clone() - .or_else(|| window.image_cache_stack.last().cloned()), - window, - cx, - ) { - Some(Ok(data)) => { - let frame_count = data.frame_count(); - let max_frame_index = frame_count.saturating_sub(1); - - if let Some(state) = &mut state { - state.frame_index = state.frame_index.min(max_frame_index); - if frame_count > 1 && !cx.reduce_motion() { - if window.is_window_active() { - let current_time = Instant::now(); - if let Some(last_frame_time) = state.last_frame_time { - let elapsed = current_time - last_frame_time; - let frame_duration = - Duration::from(data.delay(state.frame_index)); - - if elapsed >= frame_duration { - state.frame_index = - (state.frame_index + 1) % frame_count; - state.last_frame_time = - Some(current_time - (elapsed - frame_duration)); - } - } else { - state.last_frame_time = Some(current_time); - } - } else { - state.last_frame_time = None; - } - } else { - state.last_frame_time = None; - } - state.started_loading = None; - frame_index = state.frame_index; - } - - let image_size = data.render_size(frame_index); - - if style.aspect_ratio.is_none() { - style.aspect_ratio = Some(image_size.width / image_size.height); - } - - if let Length::Auto = style.size.width { - style.size.width = match style.size.height { - Length::Definite(DefiniteLength::Absolute(abs_length)) => { - let height_px = abs_length.to_pixels(window.rem_size()); - Length::Definite( - px(image_size.width.0 * height_px.0 - / image_size.height.0) - .into(), - ) - } - _ => Length::Definite(image_size.width.into()), - }; - } - - if let Length::Auto = style.size.height { - style.size.height = match style.size.width { - Length::Definite(DefiniteLength::Absolute(abs_length)) => { - let width_px = abs_length.to_pixels(window.rem_size()); - Length::Definite( - px(image_size.height.0 * width_px.0 - / image_size.width.0) - .into(), - ) - } - _ => Length::Definite(image_size.height.into()), - }; - } - - if global_id.is_some() - && data.frame_count() > 1 - && window.is_window_active() - && !cx.reduce_motion() - { - window.request_animation_frame(); - } - } - Some(_err) => { - if let Some(fallback) = self.style.fallback.as_ref() { - let mut element = fallback(); - replacement_id = Some(element.request_layout(window, cx)); - layout_state.replacement = Some(element); - } - if let Some(state) = &mut state { - state.started_loading = None; - } - } - None => { - if let Some(state) = &mut state { - if let Some((started_loading, _)) = state.started_loading { - if started_loading.elapsed() > LOADING_DELAY - && let Some(loading) = self.style.loading.as_ref() - { - let mut element = loading(); - replacement_id = Some(element.request_layout(window, cx)); - layout_state.replacement = Some(element); - } - } else { - let current_view = window.current_view(); - let task = window.spawn(cx, async move |cx| { - cx.background_executor().timer(LOADING_DELAY).await; - cx.update(move |_, cx| { - cx.notify(current_view); - }) - .ok(); - }); - state.started_loading = Some((Instant::now(), task)); - } - } - } - } - - window.request_layout(style, replacement_id, cx) - }, - ); - - layout_state.frame_index = frame_index; - - ((layout_id, layout_state), state) - }) - } - - fn prepaint( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - request_layout: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Self::PrepaintState { - self.interactivity.prepaint( - global_id, - inspector_id, - bounds, - bounds.size, - window, - cx, - |_, _, hitbox, window, cx| { - if let Some(replacement) = &mut request_layout.replacement { - replacement.prepaint(window, cx); - } - - hitbox - }, - ) - } - - fn paint( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - layout_state: &mut Self::RequestLayoutState, - hitbox: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - let source = self.source.clone(); - self.interactivity.paint( - global_id, - inspector_id, - bounds, - hitbox.as_ref(), - window, - cx, - |style, window, cx| { - if let Some(Ok(data)) = source.use_data( - self.image_cache - .clone() - .or_else(|| window.image_cache_stack.last().cloned()), - window, - cx, - ) { - if data.frame_count() == 0 { - return; - } - let new_bounds = self - .style - .object_fit - .get_bounds(bounds, data.size(layout_state.frame_index)); - let corner_radii = style.corner_radii.to_pixels(window.rem_size()); - window - .paint_image( - bounds, - new_bounds, - corner_radii, - data, - layout_state.frame_index, - self.style.grayscale, - ) - .log_err(); - } else if let Some(replacement) = &mut layout_state.replacement { - replacement.paint(window, cx); - } - }, - ) - } -} - -impl Styled for Img { - fn style(&mut self) -> &mut StyleRefinement { - &mut self.interactivity.base_style - } -} - -impl InteractiveElement for Img { - fn interactivity(&mut self) -> &mut Interactivity { - &mut self.interactivity - } -} - -impl IntoElement for Img { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -impl StatefulInteractiveElement for Img {} - -impl ImageSource { - pub(crate) fn use_data( - &self, - cache: Option, - window: &mut Window, - cx: &mut App, - ) -> Option, ImageCacheError>> { - match self { - ImageSource::Resource(resource) => { - if let Some(cache) = cache { - cache.load(resource, window, cx) - } else { - window.use_asset::(resource, cx) - } - } - ImageSource::Custom(loading_fn) => loading_fn(window, cx), - ImageSource::Render(data) => Some(Ok(data.to_owned())), - ImageSource::Image(data) => window.use_asset::>(data, cx), - } - } - - pub(crate) fn get_data( - &self, - cache: Option, - window: &mut Window, - cx: &mut App, - ) -> Option, ImageCacheError>> { - match self { - ImageSource::Resource(resource) => { - if let Some(cache) = cache { - cache.load(resource, window, cx) - } else { - window.get_asset::(resource, cx) - } - } - ImageSource::Custom(loading_fn) => loading_fn(window, cx), - ImageSource::Render(data) => Some(Ok(data.to_owned())), - ImageSource::Image(data) => window.get_asset::>(data, cx), - } - } - - /// Remove this image source from the asset system - pub fn remove_asset(&self, cx: &mut App) { - match self { - ImageSource::Resource(resource) => { - cx.remove_asset::(resource); - } - ImageSource::Custom(_) | ImageSource::Render(_) => {} - ImageSource::Image(data) => cx.remove_asset::>(data), - } - } - - /// Check whether this image source is present in the asset system (loading - /// or loaded), without fetching it. - #[cfg(any(test, feature = "test-support"))] - pub fn is_asset_cached(&self, cx: &App) -> bool { - match self { - ImageSource::Resource(resource) => cx.has_asset::(resource), - ImageSource::Custom(_) | ImageSource::Render(_) => false, - ImageSource::Image(data) => cx.has_asset::>(data), - } - } -} - -#[derive(Clone)] -enum ImageDecoder {} - -impl Asset for ImageDecoder { - type Source = Arc; - type Output = Result, ImageCacheError>; - - fn load( - source: Self::Source, - cx: &mut App, - ) -> impl Future + Send + 'static { - let renderer = cx.svg_renderer(); - async move { source.to_image_data(renderer).map_err(Into::into) } - } -} - -/// An image loader for the GPUI asset system -#[derive(Clone)] -pub enum ImageAssetLoader {} - -impl Asset for ImageAssetLoader { - type Source = Resource; - type Output = Result, ImageCacheError>; - - fn load( - source: Self::Source, - cx: &mut App, - ) -> impl Future + Send + 'static { - let client = cx.http_client(); - // TODO: Can we make SVGs always rescale? - // let scale_factor = cx.scale_factor(); - let svg_renderer = cx.svg_renderer(); - let asset_source = cx.asset_source().clone(); - async move { - let bytes = match source.clone() { - Resource::Path(uri) => fs::read(uri.as_ref())?, - Resource::Uri(uri) => { - use anyhow::Context as _; - use futures::AsyncReadExt as _; - - let mut response = client - .get(uri.as_ref(), ().into(), true) - .await - .with_context(|| format!("loading image asset from {uri:?}"))?; - let mut body = Vec::new(); - response.body_mut().read_to_end(&mut body).await?; - if !response.status().is_success() { - let mut body = String::from_utf8_lossy(&body).into_owned(); - let first_line = body.lines().next().unwrap_or("").trim_end(); - body.truncate(first_line.len()); - return Err(ImageCacheError::BadStatus { - uri, - status: response.status(), - body, - }); - } - body - } - Resource::Embedded(path) => { - let data = asset_source.load(&path).ok().flatten(); - if let Some(data) = data { - data.to_vec() - } else { - return Err(ImageCacheError::Asset( - format!("Embedded resource not found: {}", path).into(), - )); - } - } - }; - - if let Ok(format) = image::guess_format(&bytes) { - let data = match format { - ImageFormat::Gif => { - let decoder = GifDecoder::new(Cursor::new(&bytes))?; - let mut frames = SmallVec::new(); - - for frame in decoder.into_frames() { - match frame { - Ok(mut frame) => { - // Convert from RGBA to BGRA. - for pixel in frame.buffer_mut().chunks_exact_mut(4) { - pixel.swap(0, 2); - } - frames.push(frame); - } - Err(err) => { - log::debug!( - "Skipping GIF frame in {source:?} due to decode error: {err}" - ); - } - } - } - - if frames.is_empty() { - return Err(ImageCacheError::Other(Arc::new(anyhow::anyhow!( - "GIF could not be decoded: all frames failed ({source:?})" - )))); - } - - frames - } - ImageFormat::WebP => { - let mut decoder = WebPDecoder::new(Cursor::new(&bytes))?; - - if decoder.has_animation() { - let _ = decoder.set_background_color(Rgba([0, 0, 0, 0])); - let mut frames = SmallVec::new(); - - for frame in decoder.into_frames() { - match frame { - Ok(mut frame) => { - // Convert from RGBA to BGRA. - for pixel in frame.buffer_mut().chunks_exact_mut(4) { - pixel.swap(0, 2); - } - frames.push(frame); - } - Err(err) => { - log::debug!( - "Skipping WebP frame in {source:?} due to decode error: {err}" - ); - } - } - } - - if frames.is_empty() { - return Err(ImageCacheError::Other(Arc::new(anyhow::anyhow!( - "WebP could not be decoded: all frames failed ({source:?})" - )))); - } - - frames - } else { - decode_static_image_from_decoder(decoder)? - } - } - _ => decode_static_image(&bytes, format)?, - }; - - Ok(Arc::new(RenderImage::new(data))) - } else { - svg_renderer - .render_single_frame(&bytes, 1.0) - .map_err(Into::into) - } - } - } -} - -/// An error that can occur when interacting with the image cache. -#[derive(Debug, Error, Clone)] -pub enum ImageCacheError { - /// Some other kind of error occurred - #[error("error: {0}")] - Other(#[from] Arc), - /// An error that occurred while reading the image from disk. - #[error("IO error: {0}")] - Io(Arc), - /// An error that occurred while processing an image. - #[error("unexpected http status for {uri}: {status}, body: {body}")] - BadStatus { - /// The URI of the image. - uri: SharedUri, - /// The HTTP status code. - status: http_client::StatusCode, - /// The HTTP response body. - body: String, - }, - /// An error that occurred while processing an asset. - #[error("asset error: {0}")] - Asset(SharedString), - /// An error that occurred while processing an image. - #[error("image error: {0}")] - Image(Arc), - /// An error that occurred while processing an SVG. - #[error("svg error: {0}")] - Usvg(Arc), -} - -impl From for ImageCacheError { - fn from(value: anyhow::Error) -> Self { - Self::Other(Arc::new(value)) - } -} - -impl From for ImageCacheError { - fn from(value: io::Error) -> Self { - Self::Io(Arc::new(value)) - } -} - -impl From for ImageCacheError { - fn from(value: usvg::Error) -> Self { - Self::Usvg(Arc::new(value)) - } -} - -impl From for ImageCacheError { - fn from(value: image::ImageError) -> Self { - Self::Image(Arc::new(value)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ParentElement as _, TestAppContext, canvas, div, point, px, size}; - use image::{Frame, ImageBuffer, Rgba}; - - const TEST_IMG_ID: &str = "test-img"; - - fn test_image(frame_count: usize) -> Arc { - let frame = Frame::new(ImageBuffer::from_pixel(1, 1, Rgba([0, 0, 0, 0]))); - Arc::new(RenderImage::new(SmallVec::from_iter( - (0..frame_count).map(|_| frame.clone()), - ))) - } - - fn test_image_with_size(width: u32, height: u32) -> Arc { - let frame = Frame::new(ImageBuffer::from_pixel(width, height, Rgba([0, 0, 0, 0]))); - Arc::new(RenderImage::new(SmallVec::from_elem(frame, 1))) - } - - /// Overwrites the cached `frame_index` of the sibling `img` during paint. - fn seed_frame_index(frame_index: usize) -> impl IntoElement { - canvas( - |_, _, _| (), - move |_, _, window, _| { - window.with_global_id(TEST_IMG_ID.into(), |id, window| { - window.with_element_state::(id, |state, _| { - let mut state = state.expect("img state should be initialized"); - state.frame_index = frame_index; - ((), state) - }); - }); - }, - ) - } - - #[gpui::test] - fn zero_frame_image_does_not_panic_on_paint(cx: &mut TestAppContext) { - cx.add_empty_window() - .draw(point(px(0.), px(0.)), size(px(100.), px(100.)), |_, _| { - img(ImageSource::Render(test_image(0))).into_any_element() - }); - } - - #[gpui::test] - fn image_object_fit_cover_crops_to_element_bounds(cx: &mut TestAppContext) { - let window = cx.add_empty_window(); - let image = test_image_with_size(200, 100); - window.draw(point(px(0.), px(0.)), size(px(100.), px(100.)), |_, _| { - img(ImageSource::Render(image.clone())) - .size_full() - .object_fit(ObjectFit::Fill) - .into_any_element() - }); - let full_tile_bounds = window.update(|window, _| { - window - .rendered_frame - .scene - .polychrome_sprites - .last() - .expect("fill image should paint a sprite") - .tile - .bounds - }); - - window.draw(point(px(10.), px(20.)), size(px(100.), px(100.)), |_, _| { - img(ImageSource::Render(image)) - .size_full() - .object_fit(ObjectFit::Cover) - .into_any_element() - }); - - let (rendered_bounds, rendered_tile_bounds, scale_factor) = window.update(|window, _| { - let sprite = window - .rendered_frame - .scene - .polychrome_sprites - .last() - .expect("cover image should paint a sprite"); - (sprite.bounds, sprite.tile.bounds, window.scale_factor()) - }); - assert_eq!( - rendered_bounds, - Bounds { - origin: point(px(10.).scale(scale_factor), px(20.).scale(scale_factor)), - size: size(px(100.).scale(scale_factor), px(100.).scale(scale_factor)), - } - ); - assert_eq!( - ( - rendered_tile_bounds.origin.x.0 - full_tile_bounds.origin.x.0, - rendered_tile_bounds.origin.y.0 - full_tile_bounds.origin.y.0, - rendered_tile_bounds.size.width.0, - rendered_tile_bounds.size.height.0, - ), - (50, 0, 100, 100), - ); - } - - #[gpui::test] - fn explicit_aspect_ratio_is_not_overridden_by_intrinsic_ratio(cx: &mut TestAppContext) { - let window = cx.add_empty_window(); - - // A portrait image in a square container - window.draw(point(px(0.), px(0.)), size(px(100.), px(100.)), |_, _| { - div() - .size(px(100.)) - .overflow_hidden() - .child( - img(ImageSource::Render(test_image_with_size(100, 200))) - .size_full() - .aspect_square() - .object_fit(ObjectFit::Contain), - ) - .into_any_element() - }); - - let (rendered_bounds, scale_factor) = window.update(|window, _| { - let sprite = window - .rendered_frame - .scene - .polychrome_sprites - .last() - .expect("contained image should paint a sprite"); - (sprite.bounds, window.scale_factor()) - }); - - // The element stays 100x100, so the image is letterboxed horizontally - assert_eq!( - rendered_bounds, - Bounds { - origin: point(px(25.).scale(scale_factor), px(0.).scale(scale_factor)), - size: size(px(50.).scale(scale_factor), px(100.).scale(scale_factor)), - } - ); - } - - #[gpui::test] - fn image_object_fit_cover_clamps_corner_radii_to_visible_bounds(cx: &mut TestAppContext) { - let window = cx.add_empty_window(); - window.draw(point(px(0.), px(0.)), size(px(100.), px(100.)), |_, _| { - img(ImageSource::Render(test_image_with_size(200, 100))) - .size_full() - .rounded(px(100.)) - .object_fit(ObjectFit::Cover) - .into_any_element() - }); - - let (corner_radius, expected_corner_radius) = window.update(|window, _| { - ( - window - .rendered_frame - .scene - .polychrome_sprites - .last() - .map(|sprite| sprite.corner_radii.top_left), - px(50.).scale(window.scale_factor()), - ) - }); - assert_eq!(corner_radius, Some(expected_corner_radius)); - } - - #[gpui::test] - fn stale_frame_index_is_clamped_when_image_changes(cx: &mut TestAppContext) { - let window = cx.add_empty_window(); - - // Assert that a cached frame_index from a previous multi-frame image - // does not cause an out-of-bounds panic when the image is replaced - // with one that has fewer frames. - window.draw(point(px(0.), px(0.)), size(px(100.), px(100.)), |_, _| { - div() - .child(img(ImageSource::Render(test_image(5))).id(TEST_IMG_ID)) - .child(seed_frame_index(4)) - .into_any_element() - }); - window.draw(point(px(0.), px(0.)), size(px(100.), px(100.)), |_, _| { - img(ImageSource::Render(test_image(1))) - .id(TEST_IMG_ID) - .into_any_element() - }); - } -} diff --git a/crates/gpui_pre/src/elements/list.rs b/crates/gpui_pre/src/elements/list.rs deleted file mode 100644 index 730aa6ec..00000000 --- a/crates/gpui_pre/src/elements/list.rs +++ /dev/null @@ -1,2985 +0,0 @@ -//! A list element that can be used to render a large number of differently sized elements -//! efficiently. Clients of this API need to ensure that elements outside of the scrolled -//! area do not change their height for this element to function correctly. If your elements -//! do change height, notify the list element via [`ListState::splice`] or [`ListState::reset`]. -//! In order to minimize re-renders, this element's state is stored intrusively -//! on your own views, so that your code can coordinate directly with the list element's cached state. -//! -//! If all of your elements are the same height, see [`crate::UniformList`] for a simpler API - -use crate::{ - AnyElement, App, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges, Element, EntityId, - FocusHandle, GlobalElementId, Hitbox, HitboxBehavior, InspectorElementId, IntoElement, - Overflow, Pixels, Point, ScrollDelta, ScrollWheelEvent, Size, Style, StyleRefinement, Styled, - Window, point, px, size, -}; -use collections::VecDeque; -use refineable::Refineable as _; -use std::{cell::RefCell, ops::Range, rc::Rc}; -use sum_tree::{Bias, Dimensions, SumTree}; - -type RenderItemFn = dyn FnMut(usize, &mut Window, &mut App) -> AnyElement + 'static; - -/// Construct a new list element -pub fn list( - state: ListState, - render_item: impl FnMut(usize, &mut Window, &mut App) -> AnyElement + 'static, -) -> List { - List { - state, - render_item: Box::new(render_item), - style: StyleRefinement::default(), - sizing_behavior: ListSizingBehavior::default(), - } -} - -/// A list element -pub struct List { - state: ListState, - render_item: Box, - style: StyleRefinement, - sizing_behavior: ListSizingBehavior, -} - -impl List { - /// Set the sizing behavior for the list. - pub fn with_sizing_behavior(mut self, behavior: ListSizingBehavior) -> Self { - self.sizing_behavior = behavior; - self - } -} - -/// The list state that views must hold on behalf of the list element. -#[derive(Clone)] -pub struct ListState(Rc>); - -impl std::fmt::Debug for ListState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("ListState") - } -} - -struct StateInner { - last_layout_bounds: Option>, - last_padding: Option>, - items: SumTree, - logical_scroll_top: Option, - alignment: ListAlignment, - overdraw: Pixels, - reset: bool, - #[allow(clippy::type_complexity)] - scroll_handler: Option>, - scrollbar_drag_start_height: Option, - measuring_behavior: ListMeasuringBehavior, - pending_scroll: Option, - follow_state: FollowState, -} - -/// Deferred scroll adjustment applied after the scroll-top item has been remeasured. -/// -/// An absolute pending scroll preserves the same pixel offset into the item, which keeps -/// visible text stable while content is appended to or removed from that item. A -/// proportional pending scroll preserves the same fractional position within the item, -/// which is useful when the whole list is being resized and each item scales similarly. -#[derive(Clone)] -enum PendingScroll { - /// Preserve the same pixel offset into the item after it is remeasured. - Absolute { item_ix: usize, offset: Pixels }, - /// Preserve the same fractional offset into the item after it is remeasured. - Proportional(PendingScrollFraction), -} - -/// Keeps track of a fractional scroll position within an item for restoration -/// after remeasurement. -#[derive(Clone)] -struct PendingScrollFraction { - /// The index of the item to scroll within. - item_ix: usize, - /// Fractional offset (0.0 to 1.0) within the item's height. - fraction: f32, -} - -/// Determines how remeasurement preserves the scroll position when the scroll-top item -/// changes height. -enum ScrollAnchor { - /// Preserve the same pixel offset into the scroll-top item. - Absolute, - /// Preserve the same fractional position within the scroll-top item. - Proportional, -} - -/// Controls whether the list automatically follows new content at the end. -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub enum FollowMode { - /// Normal scrolling — no automatic following. - #[default] - Normal, - /// The list should auto-scroll along with the tail, when scrolled to bottom. - Tail, -} - -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -enum FollowState { - #[default] - Normal, - Tail { - is_following: bool, - }, -} - -impl FollowState { - fn is_following(&self) -> bool { - matches!(self, FollowState::Tail { is_following: true }) - } - - fn has_stopped_following(&self) -> bool { - matches!( - self, - FollowState::Tail { - is_following: false - } - ) - } - - fn start_following(&mut self) { - if let FollowState::Tail { - is_following: false, - } = self - { - *self = FollowState::Tail { is_following: true }; - } - } - - fn stop_following(&mut self) { - if let FollowState::Tail { is_following: true } = self { - *self = FollowState::Tail { - is_following: false, - }; - } - } -} - -/// Whether the list is scrolling from top to bottom or bottom to top. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum ListAlignment { - /// The list is scrolling from top to bottom, like most lists. - Top, - /// The list is scrolling from bottom to top, like a chat log. - Bottom, -} - -/// A scroll event that has been converted to be in terms of the list's items. -pub struct ListScrollEvent { - /// The range of items currently visible in the list, after applying the scroll event. - pub visible_range: Range, - - /// The number of items that are currently visible in the list, after applying the scroll event. - pub count: usize, - - /// Whether the list has been scrolled. - pub is_scrolled: bool, - - /// Whether the list is currently in follow-tail mode (auto-scrolling to end). - pub is_following_tail: bool, -} - -/// The sizing behavior to apply during layout. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum ListSizingBehavior { - /// The list should calculate its size based on the size of its items. - Infer, - /// The list should not calculate a fixed size. - #[default] - Auto, -} - -/// The measuring behavior to apply during layout. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum ListMeasuringBehavior { - /// Measure all items in the list. - /// Note: This can be expensive for the first frame in a large list. - Measure(bool), - /// Only measure visible items - #[default] - Visible, -} - -impl ListMeasuringBehavior { - fn reset(&mut self) { - match self { - ListMeasuringBehavior::Measure(has_measured) => *has_measured = false, - ListMeasuringBehavior::Visible => {} - } - } -} - -/// The horizontal sizing behavior to apply during layout. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum ListHorizontalSizingBehavior { - /// List items' width can never exceed the width of the list. - #[default] - FitList, - /// List items' width may go over the width of the list, if any item is wider. - Unconstrained, -} - -struct LayoutItemsResponse { - max_item_width: Pixels, - scroll_top: ListOffset, - item_layouts: VecDeque, -} - -struct ItemLayout { - index: usize, - element: AnyElement, - size: Size, -} - -/// Frame state used by the [List] element after layout. -pub struct ListPrepaintState { - hitbox: Hitbox, - layout: LayoutItemsResponse, -} - -#[derive(Clone)] -enum ListItem { - Unmeasured { - size_hint: Option>, - focus_handle: Option, - }, - Measured { - size: Size, - focus_handle: Option, - }, -} - -impl ListItem { - fn size(&self) -> Option> { - if let ListItem::Measured { size, .. } = self { - Some(*size) - } else { - None - } - } - - fn size_hint(&self) -> Option> { - match self { - ListItem::Measured { size, .. } => Some(*size), - ListItem::Unmeasured { size_hint, .. } => *size_hint, - } - } - - fn focus_handle(&self) -> Option { - match self { - ListItem::Unmeasured { focus_handle, .. } | ListItem::Measured { focus_handle, .. } => { - focus_handle.clone() - } - } - } - - fn contains_focused(&self, window: &Window, cx: &App) -> bool { - match self { - ListItem::Unmeasured { focus_handle, .. } | ListItem::Measured { focus_handle, .. } => { - focus_handle - .as_ref() - .is_some_and(|handle| handle.contains_focused(window, cx)) - } - } - } -} - -#[derive(Clone, Debug, Default, PartialEq)] -struct ListItemSummary { - count: usize, - rendered_count: usize, - unrendered_count: usize, - height: Pixels, - has_focus_handles: bool, - has_unknown_height: bool, -} - -#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] -struct Count(usize); - -#[derive(Clone, Debug, Default)] -struct Height(Pixels); - -impl ListState { - /// Construct a new list state, for storage on a view. - /// - /// The overdraw parameter controls how much extra space is rendered - /// above and below the visible area. Elements within this area will - /// be measured even though they are not visible. This can help ensure - /// that the list doesn't flicker or pop in when scrolling. - pub fn new(item_count: usize, alignment: ListAlignment, overdraw: Pixels) -> Self { - let this = Self(Rc::new(RefCell::new(StateInner { - last_layout_bounds: None, - last_padding: None, - items: SumTree::default(), - logical_scroll_top: None, - alignment, - overdraw, - scroll_handler: None, - reset: false, - scrollbar_drag_start_height: None, - measuring_behavior: ListMeasuringBehavior::default(), - pending_scroll: None, - follow_state: FollowState::default(), - }))); - this.splice(0..0, item_count); - this - } - - /// Set the list to measure all items in the list in the first layout phase. - /// - /// This is useful for ensuring that the scrollbar size is correct instead of based on only rendered elements. - pub fn measure_all(self) -> Self { - self.0.borrow_mut().measuring_behavior = ListMeasuringBehavior::Measure(false); - self - } - - /// Pre-populate every unmeasured item with a uniform height hint so the scrollbar thumb - /// is correctly sized from the first frame, without measuring all items up front. - /// - /// As items are actually rendered their real heights replace the hint, so the scrollbar - /// converges to the exact size over time. This is a cheaper alternative to [`Self::measure_all`] - /// for lists where items have roughly uniform heights (e.g. table rows). - pub fn with_uniform_item_height(self, height: Pixels) -> Self { - self.apply_uniform_item_height(height); - self - } - - /// Reset this instantiation of the list state. - /// - /// Note that this will cause scroll events to be dropped until the next paint. - pub fn reset(&self, element_count: usize) { - let old_count = { - let state = &mut *self.0.borrow_mut(); - state.reset = true; - state.measuring_behavior.reset(); - state.logical_scroll_top = None; - state.pending_scroll = None; - state.scrollbar_drag_start_height = None; - state.items.summary().count - }; - - self.splice(0..old_count, element_count); - } - - /// Reset the list to `element_count` items, pre-populating every item with a - /// uniform height hint so the scrollbar thumb is correctly sized from the first - /// frame even for off-screen items. - pub fn reset_with_uniform_height(&self, element_count: usize, height: Pixels) { - self.reset(element_count); - self.apply_uniform_item_height(height); - } - - fn apply_uniform_item_height(&self, height: Pixels) { - let size_hint = Size { - width: px(0.), - height, - }; - let mut state = self.0.borrow_mut(); - let new_items = state - .items - .iter() - .map(|item| ListItem::Unmeasured { - size_hint: Some(item.size_hint().unwrap_or(size_hint)), - focus_handle: item.focus_handle(), - }) - .collect::>(); - let mut tree = SumTree::default(); - tree.extend(new_items, ()); - state.items = tree; - } - - /// Remeasure all items while preserving proportional scroll position. - /// - /// Use this when item heights may have changed (e.g., font size changes) - /// but the number and identity of items remains the same. - pub fn remeasure(&self) { - let count = self.item_count(); - self.remeasure_items_with_scroll_anchor(0..count, ScrollAnchor::Proportional); - } - - /// Mark items in `range` as needing remeasurement while preserving - /// the current scroll position. Unlike [`Self::splice`], this does - /// not change the number of items or blow away `logical_scroll_top`. - /// - /// Use this when an item's content has changed and its rendered - /// height may be different (e.g., streaming text, tool results - /// loading), but the item itself still exists at the same index. - pub fn remeasure_items(&self, range: Range) { - self.remeasure_items_with_scroll_anchor(range, ScrollAnchor::Absolute); - } - - fn remeasure_items_with_scroll_anchor(&self, range: Range, scroll_anchor: ScrollAnchor) { - let state = &mut *self.0.borrow_mut(); - - if let Some(scroll_top) = state.logical_scroll_top { - if range.contains(&scroll_top.item_ix) { - state.pending_scroll = match scroll_anchor { - ScrollAnchor::Absolute => Some(PendingScroll::Absolute { - item_ix: scroll_top.item_ix, - offset: scroll_top.offset_in_item, - }), - ScrollAnchor::Proportional => { - // If the scroll-top item falls within the remeasured range, - // store a fractional offset so the layout can restore the - // proportional scroll position after the item is re-rendered - // at its new height. - let mut cursor = state.items.cursor::(()); - cursor.seek(&Count(scroll_top.item_ix), Bias::Right); - - cursor - .item() - .and_then(|item| { - item.size().map(|size| { - let fraction = if size.height.0 > 0.0 { - (scroll_top.offset_in_item.0 / size.height.0) - .clamp(0.0, 1.0) - } else { - 0.0 - }; - - PendingScroll::Proportional(PendingScrollFraction { - item_ix: scroll_top.item_ix, - fraction, - }) - }) - }) - .or_else(|| state.pending_scroll.clone()) - } - }; - } - } - - // Rebuild the tree, replacing items in the range with - // Unmeasured copies that keep their focus handles. - let new_items = { - let mut cursor = state.items.cursor::(()); - let mut new_items = cursor.slice(&Count(range.start), Bias::Right); - let invalidated = cursor.slice(&Count(range.end), Bias::Right); - new_items.extend( - invalidated.iter().map(|item| ListItem::Unmeasured { - size_hint: item.size_hint(), - focus_handle: item.focus_handle(), - }), - (), - ); - new_items.append(cursor.suffix(), ()); - new_items - }; - state.items = new_items; - state.measuring_behavior.reset(); - } - - /// The number of items in this list. - pub fn item_count(&self) -> usize { - self.0.borrow().items.summary().count - } - - /// Whether the list is scrolled to the end, or `None` if the list is - /// not scrollable or the total content height is not yet known. - pub fn is_scrolled_to_end(&self) -> Option { - let state = self.0.borrow(); - let bounds = state.last_layout_bounds?; - let summary = state.items.summary(); - if summary.has_unknown_height { - return None; - } - let padding = state.last_padding.unwrap_or_default(); - let content_height = summary.height + padding.top + padding.bottom; - let scroll_max = (content_height - bounds.size.height).max(px(0.)); - if scroll_max <= px(0.) { - return None; - } - let scroll_top = state.scroll_top(&state.logical_scroll_top()); - Some(scroll_top >= scroll_max) - } - - /// Inform the list state that the items in `old_range` have been replaced - /// by `count` new items that must be recalculated. - pub fn splice(&self, old_range: Range, count: usize) { - self.splice_focusable(old_range, (0..count).map(|_| None)) - } - - /// Register with the list state that the items in `old_range` have been replaced - /// by new items. As opposed to [`Self::splice`], this method allows an iterator of optional focus handles - /// to be supplied to properly integrate with items in the list that can be focused. If a focused item - /// is scrolled out of view, the list will continue to render it to allow keyboard interaction. - pub fn splice_focusable( - &self, - old_range: Range, - focus_handles: impl IntoIterator>, - ) { - let state = &mut *self.0.borrow_mut(); - - let mut old_items = state.items.cursor::(()); - let mut new_items = old_items.slice(&Count(old_range.start), Bias::Right); - old_items.seek_forward(&Count(old_range.end), Bias::Right); - - let mut spliced_count = 0; - new_items.extend( - focus_handles.into_iter().map(|focus_handle| { - spliced_count += 1; - ListItem::Unmeasured { - size_hint: None, - focus_handle, - } - }), - (), - ); - new_items.append(old_items.suffix(), ()); - drop(old_items); - state.items = new_items; - - 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) + spliced_count; - } - } - } - - /// Set a handler that will be called when the list is scrolled. - pub fn set_scroll_handler( - &self, - handler: impl FnMut(&ListScrollEvent, &mut Window, &mut App) + 'static, - ) { - self.0.borrow_mut().scroll_handler = Some(Box::new(handler)) - } - - /// Get the current scroll offset, in terms of the list's items. - pub fn logical_scroll_top(&self) -> ListOffset { - self.0.borrow().logical_scroll_top() - } - - /// Scroll the list by the given offset - pub fn scroll_by(&self, distance: Pixels) { - if distance == px(0.) { - return; - } - - let current_offset = self.logical_scroll_top(); - let state = &mut *self.0.borrow_mut(); - - if distance < px(0.) { - state.follow_state.stop_following(); - } - - let mut cursor = state.items.cursor::(()); - cursor.seek(&Count(current_offset.item_ix), Bias::Right); - - let start_pixel_offset = cursor.start().height + current_offset.offset_in_item; - let new_pixel_offset = (start_pixel_offset + distance).max(px(0.)); - if new_pixel_offset > start_pixel_offset { - cursor.seek_forward(&Height(new_pixel_offset), Bias::Right); - } else { - cursor.seek(&Height(new_pixel_offset), Bias::Right); - } - - let scroll_top = ListOffset { - item_ix: cursor.start().count, - offset_in_item: new_pixel_offset - cursor.start().height, - }; - drop(cursor); - state.rebase_pending_scroll(scroll_top); - state.logical_scroll_top = Some(scroll_top); - } - - /// Scroll the list to the very end (past the last item). - /// - /// Unlike [`scroll_to_reveal_item`], this uses the total item count as the - /// anchor, so the list's layout pass will walk backwards from the end and - /// always show the bottom of the last item — even when that item is still - /// growing (e.g. during streaming). - pub fn scroll_to_end(&self) { - let state = &mut *self.0.borrow_mut(); - let item_count = state.items.summary().count; - state.pending_scroll = None; - state.logical_scroll_top = Some(ListOffset { - item_ix: item_count, - offset_in_item: px(0.), - }); - } - - /// Set the follow mode for the list. In `Tail` mode, the list - /// will auto-scroll to the end and re-engage after the user - /// scrolls back to the bottom. In `Normal` mode, no automatic - /// following occurs. - pub fn set_follow_mode(&self, mode: FollowMode) { - let state = &mut *self.0.borrow_mut(); - - match mode { - FollowMode::Normal => { - state.follow_state = FollowState::Normal; - } - FollowMode::Tail => { - state.follow_state = FollowState::Tail { is_following: true }; - if matches!(mode, FollowMode::Tail) { - let item_count = state.items.summary().count; - state.logical_scroll_top = Some(ListOffset { - item_ix: item_count, - offset_in_item: px(0.), - }); - } - } - } - } - - /// Pause tail-following, freezing the list at its current scroll - /// position. Unlike [`Self::set_follow_mode`] with [`FollowMode::Normal`], - /// this keeps the list in `Tail` mode, so it will resume following - /// automatically once the view returns to the bottom. No-op when the list - /// isn't currently following. - /// - /// Useful when something other than the user grows an item (e.g. zooming a - /// diagram) and the current position should stay put rather than snapping - /// to the end. - pub fn pause_following_tail(&self) { - self.0.borrow_mut().follow_state.stop_following(); - } - - /// Returns whether the list is currently actively following the - /// tail (snapping to the end on each layout). - pub fn is_following_tail(&self) -> bool { - matches!( - self.0.borrow().follow_state, - FollowState::Tail { is_following: true } - ) - } - - /// Scroll the list to the given offset - 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.); - } - - if scroll_top.item_ix < item_count { - state.follow_state.stop_following(); - } - - state.rebase_pending_scroll(scroll_top); - state.logical_scroll_top = Some(scroll_top); - } - - /// Scroll the list to the given item, such that the item is fully visible. - 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); - let padding = state.last_padding.unwrap_or_default(); - - 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::(()); - cursor.seek(&Count(ix + 1), Bias::Right); - let bottom = cursor.start().height + padding.top; - let goal_top = px(0.).max(bottom - height + padding.bottom); - - 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.rebase_pending_scroll(scroll_top); - state.logical_scroll_top = Some(scroll_top); - } - - /// Get the bounds for the given item in window coordinates, if it's - /// been rendered. - pub fn bounds_for_item(&self, ix: usize) -> Option> { - 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::>(()); - 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::Measured { size, .. }) = cursor.item() { - let &Dimensions(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 + size.height), - )); - } - } - None - } - - /// Call this method when the user starts dragging the scrollbar. - /// - /// This will prevent the height reported to the scrollbar from changing during the drag - /// as items in the overdraw get measured, and help offset scroll position changes accordingly. - pub fn scrollbar_drag_started(&self) { - let mut state = self.0.borrow_mut(); - state.scrollbar_drag_start_height = Some(state.items.summary().height); - } - - /// Called when the user stops dragging the scrollbar. - /// - /// See `scrollbar_drag_started`. - pub fn scrollbar_drag_ended(&self) { - self.0.borrow_mut().scrollbar_drag_start_height.take(); - } - - /// Returns `true` if the scrollbar is currently being dragged. - /// - /// This is set between [`scrollbar_drag_started`](Self::scrollbar_drag_started) - /// and [`scrollbar_drag_ended`](Self::scrollbar_drag_ended) calls. Useful for - /// consumers that need to distinguish scrollbar drags from wheel/trackpad scrolls, - /// e.g. to suppress auto-scroll behavior during manual positioning. - pub fn is_scrollbar_dragging(&self) -> bool { - self.0.borrow().scrollbar_drag_start_height.is_some() - } - - /// Set the offset from the scrollbar - pub fn set_offset_from_scrollbar(&self, point: Point) { - self.0.borrow_mut().set_offset_from_scrollbar(point); - } - - /// Returns the maximum scroll offset according to the items we have measured. - /// This value remains constant while dragging to prevent the scrollbar from moving away unexpectedly. - pub fn max_offset_for_scrollbar(&self) -> Point { - let state = self.0.borrow(); - point(Pixels::ZERO, state.max_scroll_offset()) - } - - /// Returns the current scroll offset adjusted for the scrollbar. - /// - /// The returned offset has a negative `y` component representing - /// how far the content has scrolled. - pub fn scroll_px_offset_for_scrollbar(&self) -> Point { - let state = &self.0.borrow(); - - if state.logical_scroll_top.is_none() && state.alignment == ListAlignment::Bottom { - return Point::new(px(0.), -state.max_scroll_offset()); - } - - let logical_scroll_top = state.logical_scroll_top(); - - let mut cursor = state.items.cursor::(()); - let summary: ListItemSummary = - cursor.summary(&Count(logical_scroll_top.item_ix), Bias::Right); - let offset = summary.height + logical_scroll_top.offset_in_item; - - Point::new(px(0.), -offset) - } - - /// Return the bounds of the viewport in pixels. - pub fn viewport_bounds(&self) -> Bounds { - self.0.borrow().last_layout_bounds.unwrap_or_default() - } - - /// Returns whether the item is entirely above the viewport, or `None` if - /// the list has not measured enough layout to know. - /// - /// A zero-height viewport still yields a definitive answer: callers may - /// size sibling UI based on this query (potentially squeezing the list - /// itself to zero height), so returning `None` in that case would make - /// the answer oscillate from frame to frame. - pub fn item_is_above_viewport(&self, ix: usize) -> Option { - let viewport_bounds = self.0.borrow().last_layout_bounds?; - - let scroll_top = self.logical_scroll_top(); - if ix < scroll_top.item_ix { - // Rows before the logical scroll top have no item bounds, but - // their position relative to the viewport is known from scroll state. - return Some(true); - } - - let item_bounds = self.bounds_for_item(ix)?; - Some(item_bounds.bottom() <= viewport_bounds.top()) - } - - /// Returns whether the item is entirely below the viewport, or `None` if - /// the list has not measured enough layout to know. - /// - /// See [`Self::item_is_above_viewport`] for why a zero-height viewport - /// still yields a definitive answer. - pub fn item_is_below_viewport(&self, ix: usize) -> Option { - let viewport_bounds = self.0.borrow().last_layout_bounds?; - - let scroll_top = self.logical_scroll_top(); - if ix < scroll_top.item_ix { - // Rows before the logical scroll top have no item bounds, but - // their position relative to the viewport is known from scroll state. - return Some(false); - } - - let item_bounds = self.bounds_for_item(ix)?; - Some(item_bounds.top() >= viewport_bounds.bottom()) - } -} - -impl StateInner { - /// Re-anchor a pending scroll adjustment from a remeasure onto a newly set - /// scroll position, so it clamps to the remeasured item's new height on - /// the next layout instead of reverting the scroll. - fn rebase_pending_scroll(&mut self, scroll_top: ListOffset) { - let Some(pending) = self.pending_scroll.take() else { - return; - }; - if scroll_top.item_ix >= self.items.summary().count { - return; - } - - self.pending_scroll = match pending { - PendingScroll::Absolute { .. } => Some(PendingScroll::Absolute { - item_ix: scroll_top.item_ix, - offset: scroll_top.offset_in_item, - }), - PendingScroll::Proportional(_) => { - let mut cursor = self.items.cursor::(()); - cursor.seek(&Count(scroll_top.item_ix), Bias::Right); - cursor - .item() - .and_then(|item| item.size_hint()) - .filter(|size| size.height.0 > 0.0) - .map(|size| { - PendingScroll::Proportional(PendingScrollFraction { - item_ix: scroll_top.item_ix, - fraction: (scroll_top.offset_in_item.0 / size.height.0).clamp(0.0, 1.0), - }) - }) - } - }; - } - - fn max_scroll_offset(&self) -> Pixels { - let bounds = self.last_layout_bounds.unwrap_or_default(); - let height = self - .scrollbar_drag_start_height - .unwrap_or_else(|| self.items.summary().height); - (height - bounds.size.height).max(px(0.)) - } - - fn visible_range( - items: &SumTree, - height: Pixels, - scroll_top: &ListOffset, - ) -> Range { - let mut cursor = items.cursor::(()); - 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, - current_view: EntityId, - window: &mut Window, - cx: &mut App, - ) { - // Drop scroll events after a reset, since we can't calculate - // the new logical scroll top without the item heights - if self.reset { - return; - } - - let padding = self.last_padding.unwrap_or_default(); - let scroll_max = - (self.items.summary().height + padding.top + padding.bottom - 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.pending_scroll = None; - self.logical_scroll_top = None; - } else { - let (start, ..) = - self.items - .find::((), &Height(new_scroll_top), Bias::Right); - let scroll_top = ListOffset { - item_ix: start.count, - offset_in_item: new_scroll_top - start.height, - }; - // The user's scroll supersedes the position stashed by a - // remeasure; re-anchor the pending adjustment so it doesn't revert - // this scroll on the next layout. - self.rebase_pending_scroll(scroll_top); - self.logical_scroll_top = Some(scroll_top); - } - - if delta.y > px(0.) { - self.follow_state.stop_following(); - } - - if let Some(handler) = self.scroll_handler.as_mut() { - let visible_range = Self::visible_range(&self.items, height, scroll_top); - handler( - &ListScrollEvent { - visible_range, - count: self.items.summary().count, - is_scrolled: self.logical_scroll_top.is_some(), - is_following_tail: matches!( - self.follow_state, - FollowState::Tail { is_following: true } - ), - }, - window, - cx, - ); - } - - cx.notify(current_view); - } - - 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 (start, ..) = self.items.find::( - (), - &Count(logical_scroll_top.item_ix), - Bias::Right, - ); - start.height + logical_scroll_top.offset_in_item - } - - fn layout_all_items( - &mut self, - available_width: Pixels, - render_item: &mut RenderItemFn, - window: &mut Window, - cx: &mut App, - ) { - match &mut self.measuring_behavior { - ListMeasuringBehavior::Visible => { - return; - } - ListMeasuringBehavior::Measure(has_measured) => { - if *has_measured { - return; - } - *has_measured = true; - } - } - - let mut cursor = self.items.cursor::(()); - let available_item_space = size( - AvailableSpace::Definite(available_width), - AvailableSpace::MinContent, - ); - - let mut measured_items = Vec::default(); - - for (ix, item) in cursor.enumerate() { - let size = item.size().unwrap_or_else(|| { - let mut element = render_item(ix, window, cx); - element.layout_as_root(available_item_space, window, cx) - }); - - measured_items.push(ListItem::Measured { - size, - focus_handle: item.focus_handle(), - }); - } - - self.items = SumTree::from_iter(measured_items, ()); - } - - fn layout_items( - &mut self, - available_width: Option, - available_height: Pixels, - padding: &Edges, - render_item: &mut RenderItemFn, - window: &mut Window, - cx: &mut App, - ) -> LayoutItemsResponse { - let old_items = self.items.clone(); - let mut measured_items = VecDeque::new(); - let mut item_layouts = VecDeque::new(); - let mut rendered_height = padding.top; - let mut max_item_width = px(0.); - let mut scroll_top = self.logical_scroll_top(); - - if self.follow_state.is_following() { - scroll_top = ListOffset { - item_ix: self.items.summary().count, - offset_in_item: px(0.), - }; - self.logical_scroll_top = Some(scroll_top); - } - - let mut rendered_focused_item = false; - - let available_item_space = size( - available_width.map_or(AvailableSpace::MaxContent, |width| { - AvailableSpace::Definite(width) - }), - AvailableSpace::MinContent, - ); - - let mut cursor = old_items.cursor::(()); - - // Render items after the scroll top, including those in the trailing overdraw - 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 >= available_height + self.overdraw { - break; - } - - // Use the previously cached height and focus handle if available - let mut size = item.size(); - - // If we're within the visible area or the height wasn't cached, render and measure the item's element - if visible_height < available_height || size.is_none() { - let item_index = scroll_top.item_ix + ix; - let mut element = render_item(item_index, window, cx); - let element_size = element.layout_as_root(available_item_space, window, cx); - size = Some(element_size); - - // If there's a pending scroll adjustment for the scroll-top - // item, apply it. - if ix == 0 { - if let Some(pending_scroll) = self.pending_scroll.take() { - match pending_scroll { - PendingScroll::Absolute { item_ix, offset } - if item_ix == scroll_top.item_ix => - { - scroll_top.offset_in_item = offset.min(element_size.height); - self.logical_scroll_top = Some(scroll_top); - } - PendingScroll::Proportional(pending_scroll) - if pending_scroll.item_ix == scroll_top.item_ix => - { - // Ensuring proportional scroll position is - // maintained after re-measuring. - scroll_top.offset_in_item = - Pixels(pending_scroll.fraction * element_size.height.0); - self.logical_scroll_top = Some(scroll_top); - } - _ => {} - } - } - } - - if visible_height < available_height { - item_layouts.push_back(ItemLayout { - index: item_index, - element, - size: element_size, - }); - if item.contains_focused(window, cx) { - rendered_focused_item = true; - } - } - } - - let size = size.unwrap(); - rendered_height += size.height; - max_item_width = max_item_width.max(size.width); - measured_items.push_back(ListItem::Measured { - size, - focus_handle: item.focus_handle(), - }); - } - rendered_height += padding.bottom; - - // 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 < available_height { - while rendered_height < available_height { - cursor.prev(); - if let Some(item) = cursor.item() { - let item_index = cursor.start().0; - let mut element = render_item(item_index, window, cx); - let element_size = element.layout_as_root(available_item_space, window, cx); - let focus_handle = item.focus_handle(); - rendered_height += element_size.height; - measured_items.push_front(ListItem::Measured { - size: element_size, - focus_handle, - }); - item_layouts.push_front(ItemLayout { - index: item_index, - element, - size: element_size, - }); - if item.contains_focused(window, cx) { - rendered_focused_item = true; - } - } else { - break; - } - } - - scroll_top = ListOffset { - item_ix: cursor.start().0, - offset_in_item: rendered_height - available_height, - }; - - match self.alignment { - ListAlignment::Top => { - scroll_top.offset_in_item = scroll_top.offset_in_item.max(px(0.)); - self.logical_scroll_top = Some(scroll_top); - } - ListAlignment::Bottom => { - scroll_top = ListOffset { - item_ix: cursor.start().0, - offset_in_item: rendered_height - available_height, - }; - self.logical_scroll_top = None; - } - }; - } - - // Measure items in the leading overdraw - let mut leading_overdraw = scroll_top.offset_in_item; - while leading_overdraw < self.overdraw { - cursor.prev(); - if let Some(item) = cursor.item() { - let size = if let ListItem::Measured { size, .. } = item { - *size - } else { - let mut element = render_item(cursor.start().0, window, cx); - element.layout_as_root(available_item_space, window, cx) - }; - - leading_overdraw += size.height; - measured_items.push_front(ListItem::Measured { - size, - focus_handle: item.focus_handle(), - }); - } else { - break; - } - } - - let measured_range = cursor.start().0..(cursor.start().0 + measured_items.len()); - let mut cursor = old_items.cursor::(()); - 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(), ()); - self.items = new_items; - - // If follow_tail mode is on but the user scrolled away - // (is_following is false), check whether the current scroll - // position has returned to the bottom. - if self.follow_state.has_stopped_following() { - let padding = self.last_padding.unwrap_or_default(); - let total_height = self.items.summary().height + padding.top + padding.bottom; - let scroll_offset = self.scroll_top(&scroll_top); - if scroll_offset + available_height >= total_height - px(1.0) { - self.follow_state.start_following(); - } - } - - // If none of the visible items are focused, check if an off-screen item is focused - // and include it to be rendered after the visible items so keyboard interaction continues - // to work for it. - if !rendered_focused_item { - let mut cursor = self - .items - .filter::<_, Count>((), |summary| summary.has_focus_handles); - cursor.next(); - while let Some(item) = cursor.item() { - if item.contains_focused(window, cx) { - let item_index = cursor.start().0; - let mut element = render_item(cursor.start().0, window, cx); - let size = element.layout_as_root(available_item_space, window, cx); - item_layouts.push_back(ItemLayout { - index: item_index, - element, - size, - }); - break; - } - cursor.next(); - } - } - - LayoutItemsResponse { - max_item_width, - scroll_top, - item_layouts, - } - } - - fn prepaint_items( - &mut self, - bounds: Bounds, - padding: Edges, - autoscroll: bool, - render_item: &mut RenderItemFn, - window: &mut Window, - cx: &mut App, - ) -> Result { - window.transact(|window| { - match self.measuring_behavior { - ListMeasuringBehavior::Measure(has_measured) if !has_measured => { - self.layout_all_items(bounds.size.width, render_item, window, cx); - } - _ => {} - } - - let mut layout_response = self.layout_items( - Some(bounds.size.width), - bounds.size.height, - &padding, - render_item, - window, - cx, - ); - - // Avoid honoring autoscroll requests from elements other than our children. - window.take_autoscroll(); - - // Only paint the visible items, if there is actually any space for them (taking padding into account) - if bounds.size.height > padding.top + padding.bottom { - let mut item_origin = bounds.origin + Point::new(px(0.), padding.top); - item_origin.y -= layout_response.scroll_top.offset_in_item; - for item in &mut layout_response.item_layouts { - window.with_content_mask( - Some(ContentMask { - bounds, - ..Default::default() - }), - |window| { - item.element.prepaint_at(item_origin, window, cx); - }, - ); - - if let Some(autoscroll_bounds) = window.take_autoscroll() - && autoscroll - { - if autoscroll_bounds.top() < bounds.top() { - let mut item_ix = item.index; - let mut offset_in_item = autoscroll_bounds.top() - item_origin.y; - - // The requested top can sit above this item's own - // top. Walk into earlier items so the offset stays - // non-negative and no blank space appears above the - // list. - if offset_in_item < Pixels::ZERO { - let mut cursor = self.items.cursor::(()); - cursor.seek(&Count(item_ix), Bias::Right); - while offset_in_item < Pixels::ZERO { - cursor.prev(); - let Some(prev_item) = cursor.item() else { - offset_in_item = Pixels::ZERO; - break; - }; - let size = prev_item.size().unwrap_or_else(|| { - let mut element = render_item(cursor.start().0, window, cx); - let item_available_size = size( - bounds.size.width.into(), - AvailableSpace::MinContent, - ); - element.layout_as_root(item_available_size, window, cx) - }); - item_ix = cursor.start().0; - offset_in_item += size.height; - } - } - - return Err(ListOffset { - item_ix, - offset_in_item, - }); - } else if autoscroll_bounds.bottom() > bounds.bottom() { - let mut cursor = self.items.cursor::(()); - cursor.seek(&Count(item.index), Bias::Right); - let mut height = bounds.size.height - padding.top - padding.bottom; - - // Account for the height of the element down until the autoscroll bottom. - height -= autoscroll_bounds.bottom() - item_origin.y; - - // Keep decreasing the scroll top until we fill all the available space. - while height > Pixels::ZERO { - cursor.prev(); - let Some(item) = cursor.item() else { break }; - - let size = item.size().unwrap_or_else(|| { - let mut item = render_item(cursor.start().0, window, cx); - let item_available_size = - size(bounds.size.width.into(), AvailableSpace::MinContent); - item.layout_as_root(item_available_size, window, cx) - }); - height -= size.height; - } - - return Err(ListOffset { - item_ix: cursor.start().0, - offset_in_item: if height < Pixels::ZERO { - -height - } else { - Pixels::ZERO - }, - }); - } - } - - item_origin.y += item.size.height; - } - } else { - layout_response.item_layouts.clear(); - } - - Ok(layout_response) - }) - } - - // Scrollbar support - - fn set_offset_from_scrollbar(&mut self, point: Point) { - let Some(bounds) = self.last_layout_bounds else { - return; - }; - let height = bounds.size.height; - - let padding = self.last_padding.unwrap_or_default(); - // Scrollbar drag positions are computed from the content height - // captured at drag start, so map them back using the same height. - let content_height = self - .scrollbar_drag_start_height - .unwrap_or_else(|| self.items.summary().height); - let scroll_max = (content_height + padding.top + padding.bottom - height).max(px(0.)); - let new_scroll_top = (-point.y).max(px(0.)).min(scroll_max); - - // If content grew during the drag, the frozen bottom is below the - // live bottom. Treat dragging to the frozen end as resuming tail follow. - let dragged_to_end = - scroll_max > px(0.) && new_scroll_top >= (scroll_max - px(1.0)).max(px(0.)); - if dragged_to_end && matches!(self.follow_state, FollowState::Tail { .. }) { - self.follow_state = FollowState::Tail { is_following: true }; - let item_count = self.items.summary().count; - self.pending_scroll = None; - self.logical_scroll_top = Some(ListOffset { - item_ix: item_count, - offset_in_item: px(0.), - }); - return; - } - - self.follow_state.stop_following(); - - if self.alignment == ListAlignment::Bottom && new_scroll_top == scroll_max { - self.pending_scroll = None; - self.logical_scroll_top = None; - } else { - let (start, _, _) = - self.items - .find::((), &Height(new_scroll_top), Bias::Right); - - let scroll_top = ListOffset { - item_ix: start.count, - offset_in_item: new_scroll_top - start.height, - }; - self.rebase_pending_scroll(scroll_top); - self.logical_scroll_top = Some(scroll_top); - } - } -} - -impl std::fmt::Debug for ListItem { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Unmeasured { .. } => write!(f, "Unrendered"), - Self::Measured { size, .. } => f.debug_struct("Rendered").field("size", size).finish(), - } - } -} - -/// An offset into the list's items, in terms of the item index and the number -/// of pixels off the top left of the item. -#[derive(Debug, Clone, Copy, Default)] -pub struct ListOffset { - /// The index of an item in the list - pub item_ix: usize, - /// The number of pixels to offset from the item index. - pub offset_in_item: Pixels, -} - -impl Element for List { - type RequestLayoutState = (); - type PrepaintState = ListPrepaintState; - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (crate::LayoutId, Self::RequestLayoutState) { - let layout_id = match self.sizing_behavior { - ListSizingBehavior::Infer => { - let mut style = Style::default(); - style.overflow.y = Overflow::Scroll; - style.refine(&self.style); - window.with_text_style(style.text_style().cloned(), |window| { - let state = &mut *self.state.0.borrow_mut(); - - let available_height = if let Some(last_bounds) = state.last_layout_bounds { - last_bounds.size.height - } else { - // If we don't have the last layout bounds (first render), - // we might just use the overdraw value as the available height to layout enough items. - state.overdraw - }; - let padding = style.padding.to_pixels( - state.last_layout_bounds.unwrap_or_default().size.into(), - window.rem_size(), - ); - - let layout_response = state.layout_items( - None, - available_height, - &padding, - &mut self.render_item, - window, - cx, - ); - let max_element_width = layout_response.max_item_width; - - let summary = state.items.summary(); - let total_height = summary.height; - - window.request_measured_layout( - style, - move |known_dimensions, available_space, _window, _cx| { - let width = - known_dimensions - .width - .unwrap_or(match available_space.width { - AvailableSpace::Definite(x) => x, - AvailableSpace::MinContent | AvailableSpace::MaxContent => { - max_element_width - } - }); - let height = match available_space.height { - AvailableSpace::Definite(height) => total_height.min(height), - AvailableSpace::MinContent | AvailableSpace::MaxContent => { - total_height - } - }; - size(width, height) - }, - ) - }) - } - ListSizingBehavior::Auto => { - let mut style = Style::default(); - style.refine(&self.style); - window.with_text_style(style.text_style().cloned(), |window| { - window.request_layout(style, None, cx) - }) - } - }; - (layout_id, ()) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - _: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> ListPrepaintState { - let state = &mut *self.state.0.borrow_mut(); - state.reset = false; - - let mut style = Style::default(); - style.refine(&self.style); - - let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal); - - // If the width of the list has changed, invalidate all cached item heights - if state - .last_layout_bounds - .is_none_or(|last_bounds| last_bounds.size.width != bounds.size.width) - { - let new_items = SumTree::from_iter( - state.items.iter().map(|item| ListItem::Unmeasured { - size_hint: None, - focus_handle: item.focus_handle(), - }), - (), - ); - - state.items = new_items; - state.measuring_behavior.reset(); - } - - let padding = style - .padding - .to_pixels(bounds.size.into(), window.rem_size()); - let layout = - match state.prepaint_items(bounds, padding, true, &mut self.render_item, window, cx) { - Ok(layout) => layout, - Err(autoscroll_request) => { - state.logical_scroll_top = Some(autoscroll_request); - state - .prepaint_items(bounds, padding, false, &mut self.render_item, window, cx) - .unwrap() - } - }; - - state.last_layout_bounds = Some(bounds); - state.last_padding = Some(padding); - ListPrepaintState { hitbox, layout } - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - _: &mut Self::RequestLayoutState, - prepaint: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - let current_view = window.current_view(); - - // Register the scroll listener before painting children so that, in - // the bubble phase (which runs in reverse registration order), - // children's scroll-wheel handlers run first and can stop propagation - // to prevent the list from scrolling. This matches the ordering of - // div-based scroll containers. - let list_state = self.state.clone(); - let height = bounds.size.height; - let scroll_top = prepaint.layout.scroll_top; - let hitbox_id = prepaint.hitbox.id; - let mut accumulated_scroll_delta = ScrollDelta::default(); - window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| { - if phase == DispatchPhase::Bubble && hitbox_id.should_handle_scroll(window) { - accumulated_scroll_delta = accumulated_scroll_delta.coalesce(event.delta); - let pixel_delta = accumulated_scroll_delta.pixel_delta(px(20.)); - list_state.0.borrow_mut().scroll( - &scroll_top, - height, - pixel_delta, - current_view, - window, - cx, - ) - } - }); - - window.with_content_mask( - Some(ContentMask { - bounds, - ..Default::default() - }), - |window| { - for item in &mut prepaint.layout.item_layouts { - item.element.paint(window, cx); - } - }, - ); - } -} - -impl IntoElement for List { - type Element = Self; - - 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::Unmeasured { - size_hint, - focus_handle, - } => ListItemSummary { - count: 1, - rendered_count: 0, - unrendered_count: 1, - height: if let Some(size) = size_hint { - size.height - } else { - px(0.) - }, - has_focus_handles: focus_handle.is_some(), - has_unknown_height: size_hint.is_none(), - }, - ListItem::Measured { - size, focus_handle, .. - } => ListItemSummary { - count: 1, - rendered_count: 1, - unrendered_count: 0, - height: size.height, - has_focus_handles: focus_handle.is_some(), - has_unknown_height: false, - }, - } - } -} - -impl sum_tree::ContextLessSummary for ListItemSummary { - fn zero() -> Self { - Default::default() - } - - 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; - self.has_focus_handles |= summary.has_focus_handles; - self.has_unknown_height |= summary.has_unknown_height; - } -} - -impl<'a> sum_tree::Dimension<'a, ListItemSummary> for Count { - fn zero(_cx: ()) -> Self { - Default::default() - } - - fn add_summary(&mut self, summary: &'a ListItemSummary, _: ()) { - self.0 += summary.count; - } -} - -impl<'a> sum_tree::Dimension<'a, ListItemSummary> for Height { - fn zero(_cx: ()) -> Self { - Default::default() - } - - fn add_summary(&mut self, summary: &'a ListItemSummary, _: ()) { - self.0 += summary.height; - } -} - -impl sum_tree::SeekTarget<'_, ListItemSummary, ListItemSummary> for Count { - fn cmp(&self, other: &ListItemSummary, _: ()) -> std::cmp::Ordering { - self.0.partial_cmp(&other.count).unwrap() - } -} - -impl sum_tree::SeekTarget<'_, ListItemSummary, ListItemSummary> for Height { - fn cmp(&self, other: &ListItemSummary, _: ()) -> std::cmp::Ordering { - self.0.partial_cmp(&other.height).unwrap() - } -} - -#[cfg(test)] -mod test { - - use gpui::{ScrollDelta, ScrollWheelEvent}; - use std::cell::Cell; - use std::rc::Rc; - - use crate::{ - self as gpui, AppContext, Bounds, Context, Element, FollowMode, InteractiveElement, - IntoElement, ListState, Render, Styled, TestAppContext, Window, canvas, div, list, point, - px, size, - }; - - #[gpui::test] - fn test_autoscroll_above_item_top_renders_items_above(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let state = ListState::new(5, crate::ListAlignment::Top, px(10.)); - state.scroll_to(gpui::ListOffset { - item_ix: 2, - offset_in_item: px(0.), - }); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |ix, _, _| { - if ix == 2 { - // Request an autoscroll whose top sits 30px above item 2's - // own top, mimicking a scroll-margin overshoot. - canvas( - |bounds, window, _| { - window.request_autoscroll(Bounds::from_corners( - point(bounds.left(), bounds.top() - px(30.)), - point(bounds.right(), bounds.top() + px(5.)), - )); - }, - |_, _, _, _| {}, - ) - .h(px(20.)) - .w_full() - .into_any() - } else { - div().h(px(20.)).w_full().into_any() - } - }) - .w_full() - .h_full() - } - } - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(60.)), |_, cx| { - cx.new(|_| TestView(state.clone())).into_any_element() - }); - - // 30px above item 2's top, with 20px items, lands 10px into item 0. - let scroll_top = state.logical_scroll_top(); - assert!( - scroll_top.offset_in_item >= px(0.), - "offset_in_item must never be negative (would leave blank space above), got {:?}", - scroll_top.offset_in_item, - ); - assert_eq!(scroll_top.item_ix, 0); - assert_eq!(scroll_top.offset_in_item, px(10.)); - } - - #[gpui::test] - fn test_reset_after_paint_before_scroll(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let state = ListState::new(5, crate::ListAlignment::Top, px(10.)); - - // Ensure that the list is scrolled to the top - state.scroll_to(gpui::ListOffset { - item_ix: 0, - offset_in_item: px(0.0), - }); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(10.)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - // Paint - cx.draw(point(px(0.), px(0.)), size(px(100.), px(20.)), |_, cx| { - cx.new(|_| TestView(state.clone())).into_any_element() - }); - - // Reset - state.reset(5); - - // And then receive a scroll event _before_ the next paint - cx.simulate_event(ScrollWheelEvent { - position: point(px(1.), px(1.)), - delta: ScrollDelta::Pixels(point(px(0.), px(-500.))), - ..Default::default() - }); - - // Scroll position should stay at the top of the list - assert_eq!(state.logical_scroll_top().item_ix, 0); - assert_eq!(state.logical_scroll_top().offset_in_item, px(0.)); - } - - #[gpui::test] - fn test_scroll_by_positive_and_negative_distance(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let state = ListState::new(5, crate::ListAlignment::Top, px(10.)); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(20.)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - // Paint - cx.draw(point(px(0.), px(0.)), size(px(100.), px(100.)), |_, cx| { - cx.new(|_| TestView(state.clone())).into_any_element() - }); - - // Test positive distance: start at item 1, move down 30px - state.scroll_by(px(30.)); - - // Should move to item 2 - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 1); - assert_eq!(offset.offset_in_item, px(10.)); - - // Test negative distance: start at item 2, move up 30px - state.scroll_by(px(-30.)); - - // Should move back to item 1 - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 0); - assert_eq!(offset.offset_in_item, px(0.)); - - // Test zero distance - state.scroll_by(px(0.)); - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 0); - assert_eq!(offset.offset_in_item, px(0.)); - } - - #[gpui::test] - fn test_child_scroll_handler_can_stop_list_scroll(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let state = ListState::new(5, crate::ListAlignment::Top, px(10.)); - let child_saw_event = Rc::new(Cell::new(false)); - - struct TestView { - state: ListState, - child_saw_event: Rc>, - } - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - let child_saw_event = self.child_saw_event.clone(); - list(self.state.clone(), move |_, _, _| { - let child_saw_event = child_saw_event.clone(); - div() - .h(px(20.)) - .w_full() - .on_scroll_wheel(move |_, _, cx| { - child_saw_event.set(true); - cx.stop_propagation(); - }) - .into_any() - }) - .w_full() - .h_full() - } - } - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(100.)), |_, cx| { - cx.new(|_| TestView { - state: state.clone(), - child_saw_event: child_saw_event.clone(), - }) - .into_any_element() - }); - - cx.simulate_event(ScrollWheelEvent { - position: point(px(50.), px(10.)), - delta: ScrollDelta::Pixels(point(px(0.), px(-30.))), - ..Default::default() - }); - - assert!( - child_saw_event.get(), - "the child's scroll-wheel handler should run" - ); - // The child stopped propagation, so the list must not have scrolled. - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 0); - assert_eq!(offset.offset_in_item, px(0.)); - } - - struct TestListView(ListState); - impl Render for TestListView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(20.)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - #[gpui::test] - fn test_item_viewport_queries_return_none_before_layout(_cx: &mut TestAppContext) { - let state = ListState::new(5, crate::ListAlignment::Top, px(10.)).measure_all(); - - assert_eq!(state.item_is_above_viewport(0), None); - assert_eq!(state.item_is_below_viewport(0), None); - } - - #[gpui::test] - fn test_item_viewport_queries_before_logical_scroll_top(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let state = ListState::new(5, crate::ListAlignment::Top, px(10.)).measure_all(); - - state.scroll_to(gpui::ListOffset { - item_ix: 2, - offset_in_item: px(0.), - }); - cx.draw(point(px(0.), px(0.)), size(px(100.), px(20.)), |_, cx| { - cx.new(|_| TestListView(state.clone())).into_any_element() - }); - - assert_eq!(state.item_is_above_viewport(1), Some(true)); - assert_eq!(state.item_is_below_viewport(1), Some(false)); - } - - #[gpui::test] - fn test_item_viewport_queries_measured_item_inside_viewport(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let state = ListState::new(5, crate::ListAlignment::Top, px(10.)).measure_all(); - - state.scroll_to(gpui::ListOffset { - item_ix: 2, - offset_in_item: px(0.), - }); - cx.draw(point(px(0.), px(0.)), size(px(100.), px(20.)), |_, cx| { - cx.new(|_| TestListView(state.clone())).into_any_element() - }); - - assert_eq!(state.item_is_above_viewport(2), Some(false)); - assert_eq!(state.item_is_below_viewport(2), Some(false)); - } - - #[gpui::test] - fn test_item_viewport_queries_measured_item_above_viewport(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let state = ListState::new(5, crate::ListAlignment::Top, px(10.)).measure_all(); - - state.scroll_to(gpui::ListOffset { - item_ix: 2, - offset_in_item: px(20.), - }); - cx.draw(point(px(0.), px(0.)), size(px(100.), px(20.)), |_, cx| { - cx.new(|_| TestListView(state.clone())).into_any_element() - }); - - assert_eq!(state.item_is_above_viewport(2), Some(true)); - assert_eq!(state.item_is_below_viewport(2), Some(false)); - } - - #[gpui::test] - fn test_item_viewport_queries_measured_item_below_viewport(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let state = ListState::new(5, crate::ListAlignment::Top, px(10.)).measure_all(); - - state.scroll_to(gpui::ListOffset { - item_ix: 2, - offset_in_item: px(0.), - }); - cx.draw(point(px(0.), px(0.)), size(px(100.), px(20.)), |_, cx| { - cx.new(|_| TestListView(state.clone())).into_any_element() - }); - - assert_eq!(state.item_is_above_viewport(3), Some(false)); - assert_eq!(state.item_is_below_viewport(3), Some(true)); - } - - #[gpui::test] - fn test_item_viewport_queries_remain_stable_with_zero_height_viewport(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let state = ListState::new(5, crate::ListAlignment::Top, px(10.)).measure_all(); - - state.scroll_to(gpui::ListOffset { - item_ix: 2, - offset_in_item: px(0.), - }); - cx.draw(point(px(0.), px(0.)), size(px(100.), px(20.)), |_, cx| { - cx.new(|_| TestListView(state.clone())).into_any_element() - }); - - assert_eq!(state.item_is_above_viewport(3), Some(false)); - assert_eq!(state.item_is_below_viewport(3), Some(true)); - - // Squeeze the list to zero height, e.g. because a sibling element - // (sized based on the queries above) consumed all the space. The - // answers must remain definitive rather than becoming `None`, - // otherwise the sibling's size can oscillate between frames. - cx.draw(point(px(0.), px(0.)), size(px(100.), px(0.)), |_, cx| { - cx.new(|_| TestListView(state.clone())).into_any_element() - }); - - assert_eq!(state.item_is_above_viewport(1), Some(true)); - assert_eq!(state.item_is_below_viewport(1), Some(false)); - assert_eq!(state.item_is_above_viewport(3), Some(false)); - assert_eq!(state.item_is_below_viewport(3), Some(true)); - } - - #[gpui::test] - fn test_item_viewport_queries_after_scroll_to_end_before_layout(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let state = ListState::new(5, crate::ListAlignment::Top, px(10.)).measure_all(); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(20.)), |_, cx| { - cx.new(|_| TestListView(state.clone())).into_any_element() - }); - - state.scroll_to_end(); - - assert_eq!(state.logical_scroll_top().item_ix, state.item_count()); - assert_eq!(state.item_is_above_viewport(0), Some(true)); - assert_eq!(state.item_is_below_viewport(0), Some(false)); - } - - #[gpui::test] - fn test_measure_all_after_width_change(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let state = ListState::new(10, crate::ListAlignment::Top, px(0.)).measure_all(); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(50.)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let view = cx.update(|_, cx| cx.new(|_| TestView(state.clone()))); - - // First draw at width 100: all 10 items measured (total 500px). - // Viewport is 200px, so max scroll offset should be 300px. - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - assert_eq!(state.max_offset_for_scrollbar().y, px(300.)); - - // Second draw at a different width: items get invalidated. - // Without the fix, max_offset would drop because unmeasured items - // contribute 0 height. - cx.draw(point(px(0.), px(0.)), size(px(200.), px(200.)), |_, _| { - view.into_any_element() - }); - assert_eq!(state.max_offset_for_scrollbar().y, px(300.)); - } - - #[gpui::test] - fn test_remeasure(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - // Create a list with 10 items, each 100px tall. We'll keep a reference - // to the item height so we can later change the height and assert how - // `ListState` handles it. - let item_height = Rc::new(Cell::new(100usize)); - let state = ListState::new(10, crate::ListAlignment::Top, px(10.)); - - struct TestView { - state: ListState, - item_height: Rc>, - } - - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - let height = self.item_height.get(); - list(self.state.clone(), move |_, _, _| { - div().h(px(height as f32)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let state_clone = state.clone(); - let item_height_clone = item_height.clone(); - let view = cx.update(|_, cx| { - cx.new(|_| TestView { - state: state_clone, - item_height: item_height_clone, - }) - }); - - // Simulate scrolling 40px inside the element with index 2. Since the - // original item height is 100px, this equates to 40% inside the item. - state.scroll_to(gpui::ListOffset { - item_ix: 2, - offset_in_item: px(40.), - }); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 2); - assert_eq!(offset.offset_in_item, px(40.)); - - // Update the `item_height` to be 50px instead of 100px so we can assert - // that the scroll position is proportionally preserved, that is, - // instead of 40px from the top of item 2, it should be 20px, since the - // item's height has been halved. - item_height.set(50); - state.remeasure(); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.into_any_element() - }); - - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 2); - assert_eq!(offset.offset_in_item, px(20.)); - } - - #[gpui::test] - fn test_remeasure_item_preserves_scroll_offset(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let item_height = Rc::new(Cell::new(100usize)); - let state = ListState::new(20, crate::ListAlignment::Top, px(10.)); - - struct TestView { - state: ListState, - item_height: Rc>, - } - - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - let height = self.item_height.get(); - list(self.state.clone(), move |index, _, _| { - let height = if index == 5 { height } else { 100 }; - div().h(px(height as f32)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let state_clone = state.clone(); - let item_height_clone = item_height.clone(); - let view = cx.update(|_, cx| { - cx.new(|_| TestView { - state: state_clone, - item_height: item_height_clone, - }) - }); - - state.scroll_to(gpui::ListOffset { - item_ix: 5, - offset_in_item: px(40.), - }); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - - item_height.set(200); - state.remeasure_items(5..6); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.into_any_element() - }); - - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 5); - assert_eq!(offset.offset_in_item, px(40.)); - } - - #[gpui::test] - fn test_remeasure_then_scroll_does_not_revert_scroll_position(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let state = ListState::new(20, crate::ListAlignment::Top, px(10.)); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(100.)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let view = { - let state = state.clone(); - cx.update(|_, cx| cx.new(|_| TestView(state))) - }; - - state.scroll_to(gpui::ListOffset { - item_ix: 5, - offset_in_item: px(40.), - }); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - - state.remeasure_items(5..6); - - cx.simulate_event(ScrollWheelEvent { - position: point(px(50.), px(100.)), - delta: ScrollDelta::Pixels(point(px(0.), px(-30.))), - ..Default::default() - }); - - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 5); - assert_eq!(offset.offset_in_item, px(70.)); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.into_any_element() - }); - - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 5); - assert_eq!( - offset.offset_in_item, - px(70.), - "scrolling after a remeasure should not be reverted by the stale pending scroll" - ); - } - - #[gpui::test] - fn test_scroll_after_remeasure_clamps_to_shrunk_item_height(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let item_height = Rc::new(Cell::new(100usize)); - let state = ListState::new(20, crate::ListAlignment::Top, px(10.)); - - struct TestView { - state: ListState, - item_height: Rc>, - } - - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - let height = self.item_height.get(); - list(self.state.clone(), move |index, _, _| { - let height = if index == 5 { height } else { 100 }; - div().h(px(height as f32)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let view = { - let state = state.clone(); - let item_height = item_height.clone(); - cx.update(|_, cx| cx.new(|_| TestView { state, item_height })) - }; - - state.scroll_to(gpui::ListOffset { - item_ix: 5, - offset_in_item: px(40.), - }); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - - // Item 5 shrinks from 100px to 50px and is remeasured... - item_height.set(50); - state.remeasure_items(5..6); - - // ...and then the user scrolls down by 30px before the next frame, - // landing at offset 70. - cx.simulate_event(ScrollWheelEvent { - position: point(px(50.), px(100.)), - delta: ScrollDelta::Pixels(point(px(0.), px(-30.))), - ..Default::default() - }); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.into_any_element() - }); - - // The rebased pending scroll clamps the user's offset to the item's - // new height instead of leaving it pointing past the end of the item. - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 5); - assert_eq!(offset.offset_in_item, px(50.)); - } - - #[gpui::test] - fn test_follow_tail_stays_at_bottom_as_items_grow(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - // 10 items, each 50px tall → 500px total content, 200px viewport. - // With follow-tail on, the list should always show the bottom. - let item_height = Rc::new(Cell::new(50usize)); - let state = ListState::new(10, crate::ListAlignment::Top, px(0.)); - - struct TestView { - state: ListState, - item_height: Rc>, - } - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - let height = self.item_height.get(); - list(self.state.clone(), move |_, _, _| { - div().h(px(height as f32)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let state_clone = state.clone(); - let item_height_clone = item_height.clone(); - let view = cx.update(|_, cx| { - cx.new(|_| TestView { - state: state_clone, - item_height: item_height_clone, - }) - }); - - state.set_follow_mode(FollowMode::Tail); - - // First paint — items are 50px, total 500px, viewport 200px. - // Follow-tail should anchor to the end. - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - - // The scroll should be at the bottom: the last visible items fill the - // 200px viewport from the end of 500px of content (offset 300px). - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 6); - assert_eq!(offset.offset_in_item, px(0.)); - assert!(state.is_following_tail()); - - // Simulate items growing (e.g. streaming content makes each item taller). - // 10 items × 80px = 800px total. - item_height.set(80); - state.remeasure(); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.into_any_element() - }); - - // After growth, follow-tail should have re-anchored to the new end. - // 800px total − 200px viewport = 600px offset → item 7 at offset 40px, - // but follow-tail anchors to item_count (10), and layout walks back to - // fill 200px, landing at item 7 (7 × 80 = 560, 800 − 560 = 240 > 200, - // so item 8: 8 × 80 = 640, 800 − 640 = 160 < 200 → keeps walking → - // item 7: offset = 800 − 200 = 600, item_ix = 600/80 = 7, remainder 40). - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 7); - assert_eq!(offset.offset_in_item, px(40.)); - assert!(state.is_following_tail()); - } - - #[gpui::test] - fn test_pause_following_tail_reengages_when_still_at_bottom(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - // 10 items × 50px = 500px total, 200px viewport. - let state = ListState::new(10, crate::ListAlignment::Top, px(0.)); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(50.)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let view = cx.update(|_, cx| cx.new(|_| TestView(state.clone()))); - state.set_follow_mode(FollowMode::Tail); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - assert!(state.is_following_tail()); - - // Pausing while the view is still at the bottom (e.g. a no-op zoom) - // must not strand follow-tail: the next layout has to re-engage so the - // invariant "at the bottom + new content => visible" is preserved. - state.pause_following_tail(); - assert!(!state.is_following_tail()); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.into_any_element() - }); - assert!( - state.is_following_tail(), - "pausing while at the bottom must re-engage follow-tail on the next layout" - ); - } - - #[gpui::test] - fn test_pause_following_tail_freezes_off_bottom(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - // 10 items, 200px viewport. Item height is adjustable to simulate a - // diagram block growing/shrinking on zoom. - let item_height = Rc::new(Cell::new(50usize)); - let state = ListState::new(10, crate::ListAlignment::Top, px(0.)); - - struct TestView { - state: ListState, - item_height: Rc>, - } - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - let height = self.item_height.get(); - list(self.state.clone(), move |_, _, _| { - div().h(px(height as f32)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let view = cx.update(|_, cx| { - cx.new(|_| TestView { - state: state.clone(), - item_height: item_height.clone(), - }) - }); - state.set_follow_mode(FollowMode::Tail); - - // At the bottom: 500px content, 200px viewport → top at item 6. - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - assert_eq!(state.logical_scroll_top().item_ix, 6); - assert!(state.is_following_tail()); - - // Pause, then grow items (a zoom-in that pushes content below the fold). - // The frozen top must stay put rather than snapping to the new end, and - // following stays paused since we're no longer at the bottom. - state.pause_following_tail(); - item_height.set(80); - state.remeasure(); - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 6); - assert_eq!(offset.offset_in_item, px(0.)); - assert!( - !state.is_following_tail(), - "a paused list must not re-engage while the frozen top is off the bottom" - ); - - // Shrink back (zoom-out) so the frozen top once again reaches the - // bottom: follow-tail must re-engage on its own. - item_height.set(50); - state.remeasure(); - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.into_any_element() - }); - assert!( - state.is_following_tail(), - "returning to the bottom must restore follow-tail" - ); - } - - #[gpui::test] - fn test_follow_tail_disengages_on_user_scroll(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - // 10 items × 50px = 500px total, 200px viewport. - let state = ListState::new(10, crate::ListAlignment::Top, px(0.)); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(50.)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - state.set_follow_mode(FollowMode::Tail); - - // Paint with follow-tail — scroll anchored to the bottom. - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, cx| { - cx.new(|_| TestView(state.clone())).into_any_element() - }); - assert!(state.is_following_tail()); - - // Simulate the user scrolling up. - // This should disengage follow-tail. - cx.simulate_event(ScrollWheelEvent { - position: point(px(50.), px(100.)), - delta: ScrollDelta::Pixels(point(px(0.), px(100.))), - ..Default::default() - }); - - assert!( - !state.is_following_tail(), - "follow-tail should disengage when the user scrolls toward the start" - ); - } - - #[gpui::test] - fn test_follow_tail_disengages_on_scrollbar_reposition(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - // 10 items × 50px = 500px total, 200px viewport. - let state = ListState::new(10, crate::ListAlignment::Top, px(0.)).measure_all(); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(50.)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let view = cx.update(|_, cx| cx.new(|_| TestView(state.clone()))); - - state.set_follow_mode(FollowMode::Tail); - - // Paint with follow-tail — scroll anchored to the bottom. - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - assert!(state.is_following_tail()); - - // Simulate the scrollbar moving the viewport to the middle. - state.set_offset_from_scrollbar(point(px(0.), px(-150.))); - - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 3); - assert_eq!(offset.offset_in_item, px(0.)); - assert!( - !state.is_following_tail(), - "follow-tail should disengage when the scrollbar manually repositions the list" - ); - - // A subsequent draw should preserve the user's manual position instead - // of snapping back to the end. - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.into_any_element() - }); - - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 3); - assert_eq!(offset.offset_in_item, px(0.)); - } - - #[gpui::test] - fn test_scrollbar_drag_with_growing_content(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - let last_item_height = Rc::new(Cell::new(50usize)); - let state = ListState::new(10, crate::ListAlignment::Top, px(0.)).measure_all(); - - struct TestView { - state: ListState, - last_item_height: Rc>, - } - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - let last_item_height = self.last_item_height.clone(); - list(self.state.clone(), move |index, _, _| { - let height = if index == 9 { - last_item_height.get() - } else { - 50 - }; - div().h(px(height as f32)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let view = cx.update(|_, cx| { - cx.new(|_| TestView { - state: state.clone(), - last_item_height: last_item_height.clone(), - }) - }); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - - state.scrollbar_drag_started(); - - state.set_offset_from_scrollbar(point(px(0.), px(-150.))); - let scrollbar_offset_before_growth = state.scroll_px_offset_for_scrollbar(); - - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 3); - assert_eq!(offset.offset_in_item, px(0.)); - - last_item_height.set(550); - state.remeasure_items(9..10); - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - - assert_eq!(state.max_offset_for_scrollbar().y, px(300.)); - assert_eq!( - state.scroll_px_offset_for_scrollbar(), - scrollbar_offset_before_growth - ); - - state.set_offset_from_scrollbar(point(px(0.), px(-150.))); - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 3); - assert_eq!(offset.offset_in_item, px(0.)); - } - - #[gpui::test] - fn test_set_follow_tail_snaps_to_bottom(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - // 10 items × 50px = 500px total, 200px viewport. - let state = ListState::new(10, crate::ListAlignment::Top, px(0.)); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(50.)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let view = cx.update(|_, cx| cx.new(|_| TestView(state.clone()))); - - // Scroll to the middle of the list (item 3). - state.scroll_to(gpui::ListOffset { - item_ix: 3, - offset_in_item: px(0.), - }); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 3); - assert_eq!(offset.offset_in_item, px(0.)); - assert!(!state.is_following_tail()); - - // Enable follow-tail — this should immediately snap the scroll anchor - // to the end, like the user just sent a prompt. - state.set_follow_mode(FollowMode::Tail); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.into_any_element() - }); - - // After paint, scroll should be at the bottom. - // 500px total − 200px viewport = 300px offset → item 6, offset 0. - let offset = state.logical_scroll_top(); - assert_eq!(offset.item_ix, 6); - assert_eq!(offset.offset_in_item, px(0.)); - assert!(state.is_following_tail()); - } - - #[gpui::test] - fn test_bottom_aligned_scrollbar_offset_at_end(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - const ITEMS: usize = 10; - const ITEM_SIZE: f32 = 50.0; - - let state = ListState::new( - ITEMS, - crate::ListAlignment::Bottom, - px(ITEMS as f32 * ITEM_SIZE), - ); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(ITEM_SIZE)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(100.)), |_, cx| { - cx.new(|_| TestView(state.clone())).into_any_element() - }); - - // Bottom-aligned lists start pinned to the end: logical_scroll_top returns - // item_ix == item_count, meaning no explicit scroll position has been set. - assert_eq!(state.logical_scroll_top().item_ix, ITEMS); - - let max_offset = state.max_offset_for_scrollbar(); - let scroll_offset = state.scroll_px_offset_for_scrollbar(); - - assert_eq!( - -scroll_offset.y, max_offset.y, - "scrollbar offset ({}) should equal max offset ({}) when list is pinned to bottom", - -scroll_offset.y, max_offset.y, - ); - } - - /// When the user scrolls away from the bottom during follow_tail, - /// follow_tail suspends. If they scroll back to the bottom, the - /// next paint should re-engage follow_tail using fresh measurements. - #[gpui::test] - fn test_follow_tail_reengages_when_scrolled_back_to_bottom(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - // 10 items × 50px = 500px total, 200px viewport. - let state = ListState::new(10, crate::ListAlignment::Top, px(0.)); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(50.)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let view = cx.update(|_, cx| cx.new(|_| TestView(state.clone()))); - - state.set_follow_mode(FollowMode::Tail); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - assert!(state.is_following_tail()); - - // Scroll up — follow_tail should suspend (not fully disengage). - cx.simulate_event(ScrollWheelEvent { - position: point(px(50.), px(100.)), - delta: ScrollDelta::Pixels(point(px(0.), px(50.))), - ..Default::default() - }); - assert!(!state.is_following_tail()); - - // Scroll back down to the bottom. - cx.simulate_event(ScrollWheelEvent { - position: point(px(50.), px(100.)), - delta: ScrollDelta::Pixels(point(px(0.), px(-10000.))), - ..Default::default() - }); - - // After a paint, follow_tail should re-engage because the - // layout confirmed we're at the true bottom. - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - assert!( - state.is_following_tail(), - "follow_tail should re-engage after scrolling back to the bottom" - ); - } - - /// When an item is spliced to unmeasured (0px) while follow_tail - /// is suspended, the re-engagement check should still work correctly - #[gpui::test] - fn test_follow_tail_reengagement_not_fooled_by_unmeasured_items(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - // 20 items × 50px = 1000px total, 200px viewport, 1000px - // overdraw so all items get measured during the follow_tail - // paint (matching realistic production settings). - let state = ListState::new(20, crate::ListAlignment::Top, px(1000.)); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(50.)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let view = cx.update(|_, cx| cx.new(|_| TestView(state.clone()))); - - state.set_follow_mode(FollowMode::Tail); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - assert!(state.is_following_tail()); - - // Scroll up a meaningful amount — suspends follow_tail. - // 20 items × 50px = 1000px. viewport 200px. scroll_max = 800px. - // Scrolling up 200px puts us at 600px, clearly not at bottom. - cx.simulate_event(ScrollWheelEvent { - position: point(px(50.), px(100.)), - delta: ScrollDelta::Pixels(point(px(0.), px(200.))), - ..Default::default() - }); - assert!(!state.is_following_tail()); - - // Invalidate the last item (simulates EntryUpdated calling - // remeasure_items). This makes items.summary().height - // temporarily wrong (0px for the invalidated item). - state.remeasure_items(19..20); - - // Paint — layout re-measures the invalidated item with its true - // height. The re-engagement check uses these fresh measurements. - // Since we scrolled 200px up from the 800px max, we're at - // ~600px — NOT at the bottom, so follow_tail should NOT - // re-engage. - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - assert!( - !state.is_following_tail(), - "follow_tail should not falsely re-engage due to an unmeasured item \ - reducing items.summary().height" - ); - } - - #[gpui::test] - fn test_follow_tail_reengages_after_scrollbar_disengagement(cx: &mut TestAppContext) { - let cx = cx.add_empty_window(); - - // 10 items × 50px = 500px total, 200px viewport, scroll_max = 300px. - let state = ListState::new(10, crate::ListAlignment::Top, px(0.)).measure_all(); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(50.)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let view = cx.update(|_, cx| cx.new(|_| TestView(state.clone()))); - - state.set_follow_mode(FollowMode::Tail); - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - assert!(state.is_following_tail()); - - // Drag the scrollbar up to the middle — follow_tail should suspend. - state.set_offset_from_scrollbar(point(px(0.), px(-150.))); - assert!(!state.is_following_tail()); - - // Drag the scrollbar back to the bottom — follow_tail should re-engage - // on the next paint. - state.set_offset_from_scrollbar(point(px(0.), px(-300.))); - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.into_any_element() - }); - assert!( - state.is_following_tail(), - "follow_tail should re-engage after scrolling back to the bottom via the scrollbar" - ); - } - - #[gpui::test] - fn test_follow_tail_reengages_after_scrollbar_drag_to_bottom_while_growing( - cx: &mut TestAppContext, - ) { - let cx = cx.add_empty_window(); - - let state = ListState::new(10, crate::ListAlignment::Top, px(0.)).measure_all(); - - struct TestView(ListState); - impl Render for TestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - list(self.0.clone(), |_, _, _| { - div().h(px(50.)).w_full().into_any() - }) - .w_full() - .h_full() - } - } - - let view = cx.update(|_, cx| cx.new(|_| TestView(state.clone()))); - - state.set_follow_mode(FollowMode::Tail); - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - assert!(state.is_following_tail()); - - state.scrollbar_drag_started(); - - state.splice(10..10, 10); - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.clone().into_any_element() - }); - - state.set_offset_from_scrollbar(point(px(0.), px(-300.))); - state.scrollbar_drag_ended(); - - cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { - view.into_any_element() - }); - - assert!( - state.is_following_tail(), - "follow_tail should re-engage when the user drags the scrollbar to \ - the bottom of its track, even when content has grown during the drag \ - (so frozen_bottom < live_bottom)" - ); - } -} diff --git a/crates/gpui_pre/src/elements/mod.rs b/crates/gpui_pre/src/elements/mod.rs deleted file mode 100644 index 8a2a1b70..00000000 --- a/crates/gpui_pre/src/elements/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -mod anchored; -mod animation; -mod canvas; -mod container_query; -mod deferred; -mod div; -mod image_cache; -mod img; -mod list; -mod surface; -mod svg; -mod text; -mod uniform_list; - -pub use anchored::*; -pub use animation::*; -pub use canvas::*; -pub use container_query::*; -pub use deferred::*; -pub use div::*; -pub use image_cache::*; -pub use img::*; -pub use list::*; -pub use surface::*; -pub use svg::*; -pub use text::*; -pub use uniform_list::*; diff --git a/crates/gpui_pre/src/elements/surface.rs b/crates/gpui_pre/src/elements/surface.rs deleted file mode 100644 index ac1c247b..00000000 --- a/crates/gpui_pre/src/elements/surface.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::{ - App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, LayoutId, - ObjectFit, Pixels, Style, StyleRefinement, Styled, Window, -}; -#[cfg(target_os = "macos")] -use core_video::pixel_buffer::CVPixelBuffer; -use refineable::Refineable; - -/// A source of a surface's content. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SurfaceSource { - /// A macOS image buffer from CoreVideo - #[cfg(target_os = "macos")] - Surface(CVPixelBuffer), -} - -#[cfg(target_os = "macos")] -impl From for SurfaceSource { - fn from(value: CVPixelBuffer) -> Self { - SurfaceSource::Surface(value) - } -} - -/// A surface element. -pub struct Surface { - source: SurfaceSource, - object_fit: ObjectFit, - style: StyleRefinement, -} - -/// Create a new surface element. -#[cfg(target_os = "macos")] -pub fn surface(source: impl Into) -> Surface { - Surface { - source: source.into(), - object_fit: ObjectFit::Contain, - style: Default::default(), - } -} - -impl Surface { - /// Set the object fit for the image. - pub fn object_fit(mut self, object_fit: ObjectFit) -> Self { - self.object_fit = object_fit; - self - } -} - -impl Element for Surface { - type RequestLayoutState = (); - type PrepaintState = (); - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _global_id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - let mut style = Style::default(); - style.refine(&self.style); - let layout_id = window.request_layout(style, [], cx); - (layout_id, ()) - } - - fn prepaint( - &mut self, - _global_id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - _window: &mut Window, - _cx: &mut App, - ) -> Self::PrepaintState { - } - - fn paint( - &mut self, - _global_id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - #[cfg_attr(not(target_os = "macos"), allow(unused_variables))] bounds: Bounds, - _: &mut Self::RequestLayoutState, - _: &mut Self::PrepaintState, - #[cfg_attr(not(target_os = "macos"), allow(unused_variables))] window: &mut Window, - _: &mut App, - ) { - match &self.source { - #[cfg(target_os = "macos")] - SurfaceSource::Surface(surface) => { - let size = crate::size(surface.get_width().into(), surface.get_height().into()); - let new_bounds = self.object_fit.get_bounds(bounds, size); - // TODO: Add support for corner_radii - window.paint_surface(new_bounds, surface.clone()); - } - #[allow(unreachable_patterns)] - _ => {} - } - } -} - -impl IntoElement for Surface { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -impl Styled for Surface { - fn style(&mut self) -> &mut StyleRefinement { - &mut self.style - } -} diff --git a/crates/gpui_pre/src/elements/svg.rs b/crates/gpui_pre/src/elements/svg.rs deleted file mode 100644 index 176072f1..00000000 --- a/crates/gpui_pre/src/elements/svg.rs +++ /dev/null @@ -1,303 +0,0 @@ -use std::{ - fs, - hash::{Hash, Hasher}, - path::Path, - sync::Arc, -}; - -use crate::{ - App, Asset, Bounds, Element, GlobalElementId, Hitbox, InspectorElementId, InteractiveElement, - Interactivity, IntoElement, LayoutId, Pixels, Point, Radians, SharedString, Size, - StyleRefinement, Styled, TransformationMatrix, Window, point, px, radians, size, -}; -use gpui_util::ResultExt; - -/// An SVG element. -pub struct Svg { - interactivity: Interactivity, - transformation: Option, - path: Option, - external_path: Option, - data: Option>, - data_path: Option, -} - -/// Create a new SVG element. -#[track_caller] -pub fn svg() -> Svg { - Svg { - interactivity: Interactivity::new(), - transformation: None, - path: None, - external_path: None, - data: None, - data_path: None, - } -} - -impl Svg { - /// Set the path to the SVG file for this element. - pub fn path(mut self, path: impl Into) -> Self { - self.path = Some(path.into()); - self - } - - /// Set the path to the SVG file for this element. - pub fn external_path(mut self, path: impl Into) -> Self { - self.external_path = Some(path.into()); - self - } - - /// Set the raw SVG data for this element. - /// The SVG will be rendered directly from the provided bytes. - pub fn data(mut self, data: &[u8]) -> Self { - // Generate a unique deterministic path based on the data hash for caching - let mut hasher = std::collections::hash_map::DefaultHasher::new(); - data.hash(&mut hasher); - let hash = hasher.finish(); - let path = SharedString::from(format!("__binary_svg__{}", hash)); - self.data = Some(Arc::from(data)); - self.data_path = Some(path); - self - } - - /// Transform the SVG element with the given transformation. - /// Note that this won't effect the hitbox or layout of the element, only the rendering. - pub fn with_transformation(mut self, transformation: Transformation) -> Self { - self.transformation = Some(transformation); - self - } -} - -impl Element for Svg { - type RequestLayoutState = (); - type PrepaintState = Option; - - fn id(&self) -> Option { - self.interactivity.element_id.clone() - } - - fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { - self.interactivity.source_location() - } - - fn request_layout( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - let layout_id = self.interactivity.request_layout( - global_id, - inspector_id, - window, - cx, - |style, window, cx| window.request_layout(style, None, cx), - ); - (layout_id, ()) - } - - fn prepaint( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Option { - self.interactivity.prepaint( - global_id, - inspector_id, - bounds, - bounds.size, - window, - cx, - |_, _, hitbox, _, _| hitbox, - ) - } - - fn paint( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - hitbox: &mut Option, - window: &mut Window, - cx: &mut App, - ) where - Self: Sized, - { - self.interactivity.paint( - global_id, - inspector_id, - bounds, - hitbox.as_ref(), - window, - cx, - |style, window, cx| { - let transformation = self - .transformation - .as_ref() - .map(|transformation| { - transformation.into_matrix(bounds.center(), window.scale_factor()) - }) - .unwrap_or_default(); - - if let Some((data, path)) = self.data.as_ref().zip(self.data_path.as_ref()) { - if let Some(color) = style.text.color { - window - .paint_svg( - bounds, - path.clone(), - Some(&**data), - transformation, - color, - cx, - ) - .log_err(); - } - } else if let Some((path, color)) = - self.external_path.as_ref().zip(style.text.color) - { - let Some(bytes) = window - .use_asset::(path, cx) - .and_then(|asset| asset.log_err()) - else { - return; - }; - - window - .paint_svg( - bounds, - path.clone(), - Some(&bytes), - transformation, - color, - cx, - ) - .log_err(); - } else if let Some((path, color)) = self.path.as_ref().zip(style.text.color) { - window - .paint_svg(bounds, path.clone(), None, transformation, color, cx) - .log_err(); - } - }, - ) - } -} - -impl IntoElement for Svg { - type Element = Self; - - 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 - } -} - -/// A transformation to apply to an SVG element. -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Transformation { - scale: Size, - translate: Point, - rotate: Radians, -} - -impl Default for Transformation { - fn default() -> Self { - Self { - scale: size(1.0, 1.0), - translate: point(px(0.0), px(0.0)), - rotate: radians(0.0), - } - } -} - -impl Transformation { - /// Create a new Transformation with the specified scale along each axis. - pub fn scale(scale: Size) -> Self { - Self { - scale, - translate: point(px(0.0), px(0.0)), - rotate: radians(0.0), - } - } - - /// Create a new Transformation with the specified translation. - pub fn translate(translate: Point) -> Self { - Self { - scale: size(1.0, 1.0), - translate, - rotate: radians(0.0), - } - } - - /// Create a new Transformation with the specified rotation in radians. - pub fn rotate(rotate: impl Into) -> Self { - let rotate = rotate.into(); - Self { - scale: size(1.0, 1.0), - translate: point(px(0.0), px(0.0)), - rotate, - } - } - - /// Update the scaling factor of this transformation. - pub fn with_scaling(mut self, scale: Size) -> Self { - self.scale = scale; - self - } - - /// Update the translation value of this transformation. - pub fn with_translation(mut self, translate: Point) -> Self { - self.translate = translate; - self - } - - /// Update the rotation angle of this transformation. - pub fn with_rotation(mut self, rotate: impl Into) -> Self { - self.rotate = rotate.into(); - self - } - - fn into_matrix(self, center: Point, scale_factor: f32) -> TransformationMatrix { - //Note: if you read this as a sequence of matrix multiplications, start from the bottom - TransformationMatrix::unit() - .translate(center.scale(scale_factor) + self.translate.scale(scale_factor)) - .rotate(self.rotate) - .scale(self.scale) - .translate(center.scale(-scale_factor)) - } -} - -enum SvgAsset {} - -impl Asset for SvgAsset { - type Source = SharedString; - type Output = Result, Arc>; - - fn load( - source: Self::Source, - _cx: &mut App, - ) -> impl Future + Send + 'static { - async move { - let bytes = fs::read(Path::new(source.as_ref())).map_err(|e| Arc::new(e))?; - let bytes = Arc::from(bytes); - Ok(bytes) - } - } -} diff --git a/crates/gpui_pre/src/elements/text.rs b/crates/gpui_pre/src/elements/text.rs deleted file mode 100644 index 3470ac94..00000000 --- a/crates/gpui_pre/src/elements/text.rs +++ /dev/null @@ -1,1313 +0,0 @@ -use crate::{ - ActiveTooltip, AnyView, App, Bounds, DispatchPhase, Element, ElementId, GlobalElementId, - HighlightStyle, Hitbox, HitboxBehavior, InspectorElementId, IntoElement, LayoutId, - MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextOverflow, - TextRun, TextStyle, TooltipId, TruncateFrom, WhiteSpace, Window, WrappedLine, - WrappedLineLayout, register_tooltip_mouse_handlers, set_tooltip_on_window, -}; -use anyhow::Context as _; -use gpui_util::ResultExt; -use itertools::Itertools; -use smallvec::SmallVec; -use std::{ - borrow::Cow, - cell::{Cell, RefCell}, - mem, - ops::{Deref, DerefMut, Range}, - rc::Rc, - sync::Arc, -}; - -/// An [`Element`] that renders text. -/// -/// In general, [`Text`] objects should be created via the [`text`] macro: -/// ```rust -/// # use gpui::*; -/// # fn render() -> impl IntoElement { -/// div().child(text!("hello")) -/// # } -/// ``` -/// ## IDs and Accessibility -/// -/// [`Text`] elements have an ID. This ID is primarily used to produce nodes in -/// the accessibility tree, which allows the text to be visible to screen -/// readers and other assistive technologies. -/// -/// This ID is stable across frames. If the same text, with the same ID, is -/// present in two consecutive frames, no updates are reported to the screen -/// reader. If the text changes, but the ID stays the same, then the screen -/// reader will be notified that a text node's content has changed. **However**, -/// if the ID changes, then the screen reader will be notified that a node has -/// been removed, and a new node has been added. -/// -/// When using the [`text`] macro, each invocation of the macro will get a -/// unique ID, derived from its position in the source code (filename, line, and -/// column). For example: -/// ```rust -/// # use gpui::*; -/// let x = text!("hello"); -/// let y = text!("hello"); -/// // not equal, because different `text!` invocations produced them -/// assert_ne!(x.id(), y.id()); -/// -/// fn make_text(s: &str) -> Text { text!(s) } -/// let x = make_text("hello"); -/// let y = make_text("hello"); -/// // equal, because the same `text!` invocation produced them -/// assert_eq!(x.id(), y.id()); -/// ``` -/// When the contents of an invocation of [`text`] do not change, this -/// distinction is less relevant (with the caveat that you still need to take -/// care to ensure that duplicate IDs do not appear). -/// -/// However, when a [`text`] invocation's argument *does* change, you should -/// consider whether this change should be reported as a node "updating its -/// contents", or an old node being destroyed and a new node being created. -#[derive(Debug, Clone)] -pub struct Text { - id: Option, - text: SharedString, -} - -impl Text { - /// Create a new [`Text`] element with a specific ID. - /// - /// If you want a unique ID to be assigned automatically, use the [`text`] - /// macro. The docs for [`Text`] have more detail about choosing IDs. - #[inline] - pub const fn new(id: ElementId, text: SharedString) -> Self { - Self { id: Some(id), text } - } - - /// Create a new [`Text`] element that is inaccessible to screen readers. - /// - /// In order for text to be accessible to screen readers, it must have an ID - /// provided. If you want text to be accessible, either use [`text`] to have - /// an ID automatically assigned, or use [`Text::new`] to manually assign an - /// ID. - /// - /// This function is intended for use inside custom UI components, where - /// accessible properties may be set on parent containers. - #[inline] - pub const fn new_inaccessible(text: SharedString) -> Self { - Self { id: None, text } - } - - /// The ID of this [`Text`] element. - #[inline] - pub const fn id(&self) -> Option<&ElementId> { - self.id.as_ref() - } - - /// Produce a new [`Text`] with the given `id`. - pub fn with_id(mut self, id: impl Into) -> Self { - self.id = Some(id.into()); - self - } - - /// The text that this [`Text`] element will display. - #[inline] - pub const fn text(&self) -> &SharedString { - &self.text - } -} - -impl Deref for Text { - type Target = SharedString; - fn deref(&self) -> &Self::Target { - &self.text - } -} - -impl DerefMut for Text { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.text - } -} - -/// Trivial hash function for the location information produced by the [`text`] -/// macro. Not covered by semver guarantees. Performance is not particularly -/// significant because it's only used on small strings in const contexts. -#[doc(hidden)] -pub const fn __hash_text_macro_location_unstable_do_not_use(s: &'static str) -> u64 { - const BASIS: u64 = 0xcbf29ce484222325; - const PRIME: u64 = 0x100000001b3; - - let bytes = s.as_bytes(); - let mut hash = BASIS; - let mut i = 0; - while i < bytes.len() { - hash ^= bytes[i] as u64; - hash = hash.wrapping_mul(PRIME); - i += 1; - } - hash -} - -/// Create a new [`Text`] element. -/// -/// ```rust -/// # use gpui::*; -/// let a = text!("hello"); -/// let b = text!(id = "farewell-message", "hello"); -/// -/// ``` -/// -/// Text created with this macro is *accessible*. The macro generates an ID -/// based on the source location. See the docs for [`Text`] for a more in-depth -/// explanation of the significance of the ID of a [`Text`] element. -#[macro_export] -macro_rules! text { - (id = $id:expr, $text:expr) => {{ $crate::Text::new($id.into(), $text.into()) }}; - ($text:expr) => {{ - const ID: &'static str = concat!(file!(), "/", line!(), ":", column!()); - const HASH: u64 = $crate::__hash_text_macro_location_unstable_do_not_use(ID); - $crate::Text::new($crate::ElementId::Integer(HASH), $text.into()) - }}; -} - -impl IntoElement for Text { - type Element = Self; - #[inline] - fn into_element(self) -> Self::Element { - self - } -} - -impl Element for Text { - type RequestLayoutState = TextLayout; - type PrepaintState = (); - - fn id(&self) -> Option { - self.id.clone() - } - - fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { - None - } - - fn a11y_role(&self) -> Option { - if self.id.is_some() { - Some(accesskit::Role::Label) - } else { - None - } - } - - fn write_a11y_info(&self, node: &mut accesskit::Node) { - node.set_value(self.text.to_string()); - } - - fn request_layout( - &mut self, - id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - ::request_layout(&mut self.text, id, inspector_id, window, cx) - } - - fn prepaint( - &mut self, - id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - request_layout: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Self::PrepaintState { - ::prepaint( - &mut self.text, - id, - inspector_id, - bounds, - request_layout, - window, - cx, - ) - } - - fn paint( - &mut self, - id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - request_layout: &mut Self::RequestLayoutState, - prepaint: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - ::paint( - &mut self.text, - id, - inspector_id, - bounds, - request_layout, - prepaint, - window, - cx, - ); - } -} - -impl Element for &'static str { - type RequestLayoutState = TextLayout; - type PrepaintState = (); - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - let mut state = TextLayout::default(); - let layout_id = state.layout(SharedString::from(*self), None, window, cx); - (layout_id, state) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - text_layout: &mut Self::RequestLayoutState, - _window: &mut Window, - _cx: &mut App, - ) { - text_layout.prepaint(bounds, self) - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - text_layout: &mut TextLayout, - _: &mut (), - window: &mut Window, - cx: &mut App, - ) { - text_layout.paint(self, window, cx) - } -} - -impl IntoElement for &'static str { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -impl IntoElement for String { - type Element = SharedString; - - fn into_element(self) -> Self::Element { - self.into() - } -} - -impl IntoElement for Cow<'static, str> { - type Element = SharedString; - - fn into_element(self) -> Self::Element { - self.into() - } -} - -impl Element for SharedString { - type RequestLayoutState = TextLayout; - type PrepaintState = (); - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - let mut state = TextLayout::default(); - let layout_id = state.layout(self.clone(), None, window, cx); - (layout_id, state) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - text_layout: &mut Self::RequestLayoutState, - _window: &mut Window, - _cx: &mut App, - ) { - text_layout.prepaint(bounds, self.as_ref()) - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - text_layout: &mut Self::RequestLayoutState, - _: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - text_layout.paint(self.as_ref(), window, cx) - } -} - -impl IntoElement for SharedString { - type Element = Self; - - 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>, - delayed_highlights: Option, HighlightStyle)>>, - delayed_font_family_overrides: Option, SharedString)>>, - layout: TextLayout, -} - -impl StyledText { - /// Construct a new styled text element from the given string. - pub fn new(text: impl Into) -> Self { - StyledText { - text: text.into(), - runs: None, - delayed_highlights: None, - delayed_font_family_overrides: None, - layout: TextLayout::default(), - } - } - - /// Get the layout for this element. This can be used to map indices to pixels and vice versa. - pub fn layout(&self) -> &TextLayout { - &self.layout - } - - /// Set the styling attributes for the given text, as well as - /// as any ranges of text that have had their style customized. - pub fn with_default_highlights( - mut self, - default_style: &TextStyle, - highlights: impl IntoIterator, HighlightStyle)>, - ) -> Self { - debug_assert!( - self.delayed_highlights.is_none(), - "Can't use `with_default_highlights` and `with_highlights`" - ); - let runs = Self::compute_runs(&self.text, default_style, highlights); - self.with_runs(runs) - } - - /// Set the styling attributes for the given text, as well as - /// as any ranges of text that have had their style customized. - pub fn with_highlights( - mut self, - highlights: impl IntoIterator, HighlightStyle)>, - ) -> Self { - debug_assert!( - self.runs.is_none(), - "Can't use `with_highlights` and `with_default_highlights`" - ); - self.delayed_highlights = Some( - highlights - .into_iter() - .inspect(|(run, _)| { - debug_assert!(self.text.is_char_boundary(run.start)); - debug_assert!(self.text.is_char_boundary(run.end)); - }) - .collect::>(), - ); - self - } - - fn compute_runs( - text: &str, - default_style: &TextStyle, - highlights: impl IntoIterator, HighlightStyle)>, - ) -> Vec { - let mut runs = Vec::new(); - let mut ix = 0; - for (range, highlight) in highlights { - if ix < range.start { - debug_assert!(text.is_char_boundary(range.start)); - runs.push(default_style.clone().to_run(range.start - ix)); - } - debug_assert!(text.is_char_boundary(range.end)); - runs.push( - default_style - .clone() - .highlight(highlight) - .to_run(range.len()), - ); - ix = range.end; - } - if ix < text.len() { - runs.push(default_style.to_run(text.len() - ix)); - } - runs - } - - /// Override the font family for specific byte ranges of the text. - /// - /// This is resolved lazily at layout time, so the overrides are applied - /// on top of the inherited text style from the parent element. - /// Can be combined with [`with_highlights`](Self::with_highlights). - /// - /// The overrides must be sorted by range start and non-overlapping. - /// Each override range must fall on character boundaries. - pub fn with_font_family_overrides( - mut self, - overrides: impl IntoIterator, SharedString)>, - ) -> Self { - self.delayed_font_family_overrides = Some( - overrides - .into_iter() - .inspect(|(range, _)| { - debug_assert!(self.text.is_char_boundary(range.start)); - debug_assert!(self.text.is_char_boundary(range.end)); - }) - .collect(), - ); - self - } - - fn apply_font_family_overrides( - runs: &mut [TextRun], - overrides: &[(Range, SharedString)], - ) { - let mut byte_offset = 0; - let mut override_idx = 0; - for run in runs.iter_mut() { - let run_end = byte_offset + run.len; - while override_idx < overrides.len() && overrides[override_idx].0.end <= byte_offset { - override_idx += 1; - } - if override_idx < overrides.len() { - let (ref range, ref family) = overrides[override_idx]; - if byte_offset >= range.start && run_end <= range.end { - run.font.family = family.clone(); - } - } - byte_offset = run_end; - } - } - - /// Set the text runs for this piece of text. - pub fn with_runs(mut self, runs: Vec) -> Self { - let mut text = &*self.text; - for run in &runs { - text = text.get(run.len..).unwrap_or_else(|| { - #[cfg(debug_assertions)] - panic!("invalid text run. Text: '{text}', run: {run:?}"); - #[cfg(not(debug_assertions))] - panic!("invalid text run"); - }); - } - assert!(text.is_empty(), "invalid text run"); - self.runs = Some(runs); - self - } -} - -impl Element for StyledText { - type RequestLayoutState = (); - type PrepaintState = (); - - fn id(&self) -> Option { - None - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - let font_family_overrides = self.delayed_font_family_overrides.take(); - let mut runs = self.runs.take().or_else(|| { - self.delayed_highlights.take().map(|delayed_highlights| { - Self::compute_runs(&self.text, &window.text_style(), delayed_highlights) - }) - }); - - if let Some(ref overrides) = font_family_overrides { - let runs = - runs.get_or_insert_with(|| vec![window.text_style().to_run(self.text.len())]); - Self::apply_font_family_overrides(runs, overrides); - } - - let layout_id = self.layout.layout(self.text.clone(), runs, window, cx); - (layout_id, ()) - } - - fn prepaint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - _: &mut Self::RequestLayoutState, - _window: &mut Window, - _cx: &mut App, - ) { - self.layout.prepaint(bounds, &self.text) - } - - fn paint( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - _: &mut Self::RequestLayoutState, - _: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - self.layout.paint(&self.text, window, cx) - } -} - -impl IntoElement for StyledText { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -/// The Layout for TextElement. This can be used to map indices to pixels and vice versa. -#[derive(Default, Clone)] -pub struct TextLayout(Rc>>); - -struct TextLayoutInner { - len: usize, - lines: SmallVec<[WrappedLine; 1]>, - line_height: Pixels, - wrap_width: Option, - truncate_width: Option, - size: Option>, - bounds: Option>, -} - -impl TextLayout { - fn layout( - &self, - text: SharedString, - runs: Option>, - window: &mut Window, - _: &mut App, - ) -> LayoutId { - let text_style = window.text_style(); - let font_size = text_style.font_size.to_pixels(window.rem_size()); - let line_height = window.pixel_snap( - text_style - .line_height - .to_pixels(font_size.into(), window.rem_size()), - ); - - let runs = if let Some(runs) = runs { - runs - } else { - vec![text_style.to_run(text.len())] - }; - window.request_measured_layout(Default::default(), { - let element_state = self.clone(); - - move |known_dimensions, available_space, window, 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 - }; - - let (truncate_width, truncation_affix, truncate_from) = - if let Some(text_overflow) = text_style.text_overflow.clone() { - let width = known_dimensions.width.or(match available_space.width { - crate::AvailableSpace::Definite(x) => match text_style.line_clamp { - Some(max_lines) => Some(x * max_lines), - None => Some(x), - }, - _ => None, - }); - - match text_overflow { - TextOverflow::Truncate(s) => (width, s, TruncateFrom::End), - TextOverflow::TruncateStart(s) => (width, s, TruncateFrom::Start), - TextOverflow::TruncateMiddle(s) => (width, s, TruncateFrom::Middle), - } - } else { - (None, "".into(), TruncateFrom::End) - }; - - // Only use cached layout if: - // 1. We have a cached size - // 2. wrap_width matches (or both are None) - // 3. truncate_width is None (if truncate_width is Some, we need to re-layout - // because the previous layout may have been computed without truncation) - // 4. the cached layout was not truncated (a truncated layout answers an - // unconstrained probe with the truncated size, which poisons intrinsic - // sizing with whatever width some earlier measure pass happened to use) - if let Some(text_layout) = element_state.0.borrow().as_ref() - && let Some(size) = text_layout.size - && (wrap_width.is_none() || wrap_width == text_layout.wrap_width) - && truncate_width.is_none() - && text_layout.truncate_width.is_none() - { - return size; - } - - let mut line_wrapper = cx.text_system().line_wrapper(text_style.font(), font_size); - let (text, runs) = if let Some(truncate_width) = truncate_width { - if let Some(max_lines) = text_style.line_clamp - && let Some(wrap_width) = wrap_width - { - line_wrapper.truncate_wrapped_line( - text.clone(), - wrap_width, - max_lines, - &truncation_affix, - &runs, - truncate_from, - ) - } else if let Some(unclipped) = window - .text_system() - .shape_text(text.clone(), font_size, &runs, None, None) - .log_err() - && unclipped - .iter() - .all(|line| line.size(line_height).width <= truncate_width) - { - // The truncation decision below sums per-character advances, - // which overestimates the shaped width (no kerning), truncating - // text that fits exactly in its measured width. Skip truncation - // whenever the honestly-shaped text fits; the shaping result - // comes from the line layout cache when the same text was - // already measured untruncated this frame. - (text.clone(), Cow::Borrowed(&*runs)) - } else { - line_wrapper.truncate_line( - text.clone(), - truncate_width, - &truncation_affix, - &runs, - truncate_from, - ) - } - } else { - (text.clone(), Cow::Borrowed(&*runs)) - }; - let len = text.len(); - - let Some(lines) = window - .text_system() - .shape_text( - text, - font_size, - &runs, - wrap_width, // Wrap if we know the width. - text_style.line_clamp, // Limit the number of lines if line_clamp is set. - ) - .log_err() - else { - element_state.0.borrow_mut().replace(TextLayoutInner { - lines: Default::default(), - len: 0, - line_height, - wrap_width, - truncate_width, - size: Some(Size::default()), - bounds: None, - }); - return Size::default(); - }; - - let mut size: Size = 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.0.borrow_mut().replace(TextLayoutInner { - lines, - len, - line_height, - wrap_width, - truncate_width, - size: Some(size), - bounds: None, - }); - - size - } - }) - } - - fn prepaint(&self, bounds: Bounds, text: &str) { - let mut element_state = self.0.borrow_mut(); - let element_state = element_state - .as_mut() - .with_context(|| format!("measurement has not been performed on {text}")) - .unwrap(); - element_state.bounds = Some(bounds); - } - - fn paint(&self, text: &str, window: &mut Window, cx: &mut App) { - let element_state = self.0.borrow(); - let element_state = element_state - .as_ref() - .with_context(|| format!("measurement has not been performed on {text}")) - .unwrap(); - let bounds = element_state - .bounds - .with_context(|| format!("prepaint has not been performed on {text}")) - .unwrap(); - - let line_height = element_state.line_height; - let mut line_origin = bounds.origin; - let text_style = window.text_style(); - for line in &element_state.lines { - line.paint_background( - line_origin, - line_height, - text_style.text_align, - Some(bounds), - window, - cx, - ) - .log_err(); - line.paint( - line_origin, - line_height, - text_style.text_align, - Some(bounds), - window, - cx, - ) - .log_err(); - line_origin.y += line.size(line_height).height; - } - } - - /// Get the byte index into the input of the pixel position. - pub fn index_for_position(&self, mut position: Point) -> Result { - let element_state = self.0.borrow(); - let element_state = element_state - .as_ref() - .expect("measurement has not been performed"); - let bounds = element_state - .bounds - .expect("prepaint has not been performed"); - - if position.y < bounds.top() { - return Err(0); - } - - 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; - match line.index_for_position(position_within_line, line_height) { - Ok(index_within_line) => return Ok(line_start_ix + index_within_line), - Err(index_within_line) => return Err(line_start_ix + index_within_line), - } - } - } - - Err(line_start_ix.saturating_sub(1)) - } - - /// Get the pixel position for the given byte index. - pub fn position_for_index(&self, index: usize) -> Option> { - let element_state = self.0.borrow(); - let element_state = element_state - .as_ref() - .expect("measurement has not been performed"); - let bounds = element_state - .bounds - .expect("prepaint 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_end_ix = line_start_ix + line.len(); - if index < line_start_ix { - break; - } else if index > line_end_ix { - line_origin.y += line.size(line_height).height; - line_start_ix = line_end_ix + 1; - continue; - } else { - let ix_within_line = index - line_start_ix; - return Some(line_origin + line.position_for_index(ix_within_line, line_height)?); - } - } - - None - } - - /// Retrieve the layout for the line containing the given byte index. - pub fn line_layout_for_index(&self, index: usize) -> Option> { - let element_state = self.0.borrow(); - let element_state = element_state - .as_ref() - .expect("measurement has not been performed"); - let mut line_start_ix = 0; - - for line in &element_state.lines { - let line_end_ix = line_start_ix + line.len(); - if index < line_start_ix { - break; - } else if index > line_end_ix { - line_start_ix = line_end_ix + 1; - continue; - } else { - return Some(line.layout.clone()); - } - } - - None - } - - /// Retrieve all line layouts in source order. - pub fn line_layouts(&self) -> SmallVec<[Arc; 1]> { - self.0 - .borrow() - .as_ref() - .expect("measurement has not been performed") - .lines - .iter() - .map(|line| line.layout.clone()) - .collect() - } - - /// The bounds of this layout. - pub fn bounds(&self) -> Bounds { - self.0.borrow().as_ref().unwrap().bounds.unwrap() - } - - /// The line height for this layout. - pub fn line_height(&self) -> Pixels { - self.0.borrow().as_ref().unwrap().line_height - } - - /// The UTF-8 length of the underlying text. - pub fn len(&self) -> usize { - self.0.borrow().as_ref().unwrap().len - } - - /// The text for this layout. - pub fn text(&self) -> String { - self.0 - .borrow() - .as_ref() - .unwrap() - .lines - .iter() - .map(|s| &s.text) - .join("\n") - } - - /// The text for this layout (with soft-wraps as newlines) - pub fn wrapped_text(&self) -> String { - let mut accumulator = String::new(); - - for wrapped in self.0.borrow().as_ref().unwrap().lines.iter() { - let mut seen = 0; - for boundary in wrapped.layout.wrap_boundaries.iter() { - let index = wrapped.layout.unwrapped_layout.runs[boundary.run_ix].glyphs - [boundary.glyph_ix] - .index; - - accumulator.push_str(&wrapped.text[seen..index]); - accumulator.push('\n'); - seen = index; - } - accumulator.push_str(&wrapped.text[seen..]); - accumulator.push('\n'); - } - // Remove trailing newline - accumulator.pop(); - accumulator - } -} - -/// A text element that can be interacted with. -pub struct InteractiveText { - element_id: ElementId, - text: StyledText, - click_listener: - Option], InteractiveTextClickEvent, &mut Window, &mut App)>>, - hover_listener: Option, MouseMoveEvent, &mut Window, &mut App)>>, - tooltip_builder: Option Option>>, - tooltip_id: Option, - clickable_ranges: Vec>, -} - -struct InteractiveTextClickEvent { - mouse_down_index: usize, - mouse_up_index: usize, -} - -#[doc(hidden)] -#[derive(Default)] -pub struct InteractiveTextState { - mouse_down_index: Rc>>, - hovered_index: Rc>>, - active_tooltip: Rc>>, -} - -/// InteractiveTest is a wrapper around StyledText that adds mouse interactions. -impl InteractiveText { - /// Creates a new InteractiveText from the given text. - pub fn new(id: impl Into, text: StyledText) -> Self { - Self { - element_id: id.into(), - text, - click_listener: None, - hover_listener: None, - tooltip_builder: None, - tooltip_id: None, - clickable_ranges: Vec::new(), - } - } - - /// on_click is called when the user clicks on one of the given ranges, passing the index of - /// the clicked range. - pub fn on_click( - mut self, - ranges: Vec>, - listener: impl Fn(usize, &mut Window, &mut App) + 'static, - ) -> Self { - self.click_listener = Some(Box::new(move |ranges, event, window, 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, window, cx); - } - } - })); - self.clickable_ranges = ranges; - self - } - - /// on_hover is called when the mouse moves over a character within the text, passing the - /// index of the hovered character, or None if the mouse leaves the text. - pub fn on_hover( - mut self, - listener: impl Fn(Option, MouseMoveEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.hover_listener = Some(Box::new(listener)); - self - } - - /// tooltip lets you specify a tooltip for a given character index in the string. - pub fn tooltip( - mut self, - builder: impl Fn(usize, &mut Window, &mut App) -> Option + 'static, - ) -> Self { - self.tooltip_builder = Some(Rc::new(builder)); - self - } -} - -impl Element for InteractiveText { - type RequestLayoutState = (); - type PrepaintState = Hitbox; - - fn id(&self) -> Option { - Some(self.element_id.clone()) - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn a11y_role(&self) -> Option { - Some(accesskit::Role::Label) - } - - fn write_a11y_info(&self, node: &mut accesskit::Node) { - node.set_value(self.text.text.to_string()); - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - self.text.request_layout(None, inspector_id, window, cx) - } - - fn prepaint( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - state: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Hitbox { - window.with_optional_element_state::( - global_id, - |interactive_state, window| { - let mut interactive_state = interactive_state - .map(|interactive_state| interactive_state.unwrap_or_default()); - - if let Some(interactive_state) = interactive_state.as_mut() { - if self.tooltip_builder.is_some() { - self.tooltip_id = - set_tooltip_on_window(&interactive_state.active_tooltip, window); - } else { - // If there is no longer a tooltip builder, remove the active tooltip. - interactive_state.active_tooltip.take(); - } - } - - self.text - .prepaint(None, inspector_id, bounds, state, window, cx); - let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal); - (hitbox, interactive_state) - }, - ) - } - - fn paint( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - _: &mut Self::RequestLayoutState, - hitbox: &mut Hitbox, - window: &mut Window, - cx: &mut App, - ) { - let current_view = window.current_view(); - let text_layout = self.text.layout().clone(); - window.with_element_state::( - global_id.unwrap(), - |interactive_state, window| { - let mut interactive_state = interactive_state.unwrap_or_default(); - if let Some(click_listener) = self.click_listener.take() { - let mouse_position = window.mouse_position(); - if let Ok(ix) = text_layout.index_for_position(mouse_position) - && self - .clickable_ranges - .iter() - .any(|range| range.contains(&ix)) - { - window.set_cursor_style(crate::CursorStyle::PointingHand, hitbox) - } - - let text_layout = text_layout.clone(); - let mouse_down = interactive_state.mouse_down_index.clone(); - if let Some(mouse_down_index) = mouse_down.get() { - let hitbox = hitbox.clone(); - let clickable_ranges = mem::take(&mut self.clickable_ranges); - window.on_mouse_event( - move |event: &MouseUpEvent, phase, window: &mut Window, cx| { - if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { - if let Ok(mouse_up_index) = - text_layout.index_for_position(event.position) - { - click_listener( - &clickable_ranges, - InteractiveTextClickEvent { - mouse_down_index, - mouse_up_index, - }, - window, - cx, - ) - } - - mouse_down.take(); - window.refresh(); - } - }, - ); - } else { - let hitbox = hitbox.clone(); - window.on_mouse_event(move |event: &MouseDownEvent, phase, window, _| { - if phase == DispatchPhase::Bubble - && hitbox.is_hovered(window) - && let Ok(mouse_down_index) = - text_layout.index_for_position(event.position) - { - mouse_down.set(Some(mouse_down_index)); - window.refresh(); - } - }); - } - } - - window.on_mouse_event({ - let mut hover_listener = self.hover_listener.take(); - let hitbox = hitbox.clone(); - let text_layout = text_layout.clone(); - let hovered_index = interactive_state.hovered_index.clone(); - move |event: &MouseMoveEvent, phase, window, cx| { - if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) { - let current = hovered_index.get(); - let updated = text_layout.index_for_position(event.position).ok(); - if current != updated { - hovered_index.set(updated); - if let Some(hover_listener) = hover_listener.as_ref() { - hover_listener(updated, event.clone(), window, cx); - } - cx.notify(current_view); - } - } - } - }); - - if let Some(tooltip_builder) = self.tooltip_builder.clone() { - let active_tooltip = interactive_state.active_tooltip.clone(); - let build_tooltip = Rc::new({ - let tooltip_is_hoverable = false; - let text_layout = text_layout.clone(); - move |window: &mut Window, cx: &mut App| { - text_layout - .index_for_position(window.mouse_position()) - .ok() - .and_then(|position| tooltip_builder(position, window, cx)) - .map(|view| (view, tooltip_is_hoverable)) - } - }); - - // Use bounds instead of testing hitbox since this is called during prepaint. - let check_is_hovered_during_prepaint = Rc::new({ - let source_bounds = hitbox.bounds; - let text_layout = text_layout.clone(); - let pending_mouse_down = interactive_state.mouse_down_index.clone(); - move |window: &Window| { - text_layout - .index_for_position(window.mouse_position()) - .is_ok() - && source_bounds.contains(&window.mouse_position()) - && pending_mouse_down.get().is_none() - } - }); - - let check_is_hovered = Rc::new({ - let hitbox = hitbox.clone(); - let text_layout = text_layout.clone(); - let pending_mouse_down = interactive_state.mouse_down_index.clone(); - move |window: &Window| { - text_layout - .index_for_position(window.mouse_position()) - .is_ok() - && hitbox.is_hovered(window) - && pending_mouse_down.get().is_none() - } - }); - - register_tooltip_mouse_handlers( - &active_tooltip, - self.tooltip_id, - build_tooltip, - check_is_hovered, - check_is_hovered_during_prepaint, - None, - window, - ); - } - - self.text - .paint(None, inspector_id, bounds, &mut (), &mut (), window, cx); - - ((), interactive_state) - }, - ); - } -} - -impl IntoElement for InteractiveText { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_into_element_for() { - use crate::{ParentElement as _, SharedString, div}; - use std::borrow::Cow; - - let _ = div().child("static str"); - let _ = div().child("String".to_string()); - let _ = div().child(Cow::Borrowed("Cow")); - let _ = div().child(SharedString::from("SharedString")); - } - - #[test] - fn text_macro_id() { - // one call to `text!` = one id - fn make_text_stable_id(happy: bool) -> Text { - text!(if happy { "happy" } else { "sad" }) - } - - // two calls to `text!` = two ids - fn make_text_unstable_id(happy: bool) -> Text { - if happy { text!("happy") } else { text!("sad") } - } - - assert_eq!(make_text_stable_id(false).id, make_text_stable_id(true).id); - assert_ne!( - make_text_unstable_id(false).id, - make_text_unstable_id(true).id - ); - } -} diff --git a/crates/gpui_pre/src/elements/uniform_list.rs b/crates/gpui_pre/src/elements/uniform_list.rs deleted file mode 100644 index 308fbfec..00000000 --- a/crates/gpui_pre/src/elements/uniform_list.rs +++ /dev/null @@ -1,868 +0,0 @@ -//! A scrollable list of elements with uniform height, optimized for large lists. -//! Rather than use the full taffy layout system, uniform_list simply measures -//! the first element and then lays out all remaining elements in a line based on that -//! measurement. This is much faster than the full layout system, but only works for -//! elements with uniform height. - -use crate::{ - AnyElement, App, AvailableSpace, Bounds, ContentMask, Element, ElementId, Entity, - GlobalElementId, Hitbox, InspectorElementId, InteractiveElement, Interactivity, IntoElement, - IsZero, LayoutId, ListSizingBehavior, Overflow, Pixels, Point, ScrollHandle, Size, - StyleRefinement, Styled, Window, point, px, size, -}; -use smallvec::SmallVec; -use std::{cell::RefCell, cmp, ops::Range, rc::Rc}; - -use super::ListHorizontalSizingBehavior; - -/// 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( - id: impl Into, - item_count: usize, - f: impl 'static + Fn(Range, &mut Window, &mut App) -> Vec, -) -> UniformList -where - R: IntoElement, -{ - let id = id.into(); - let mut base_style = StyleRefinement::default(); - base_style.overflow.y = Some(Overflow::Scroll); - - let render_range = move |range: Range, window: &mut Window, cx: &mut App| { - f(range, window, cx) - .into_iter() - .map(|component| component.into_any_element()) - .collect() - }; - - UniformList { - item_count, - item_to_measure_index: 0, - render_items: Box::new(render_range), - decorations: Vec::new(), - interactivity: Interactivity { - element_id: Some(id), - base_style: Box::new(base_style), - ..Interactivity::new() - }, - scroll_handle: None, - sizing_behavior: ListSizingBehavior::default(), - horizontal_sizing_behavior: ListHorizontalSizingBehavior::default(), - } -} - -/// A list element for efficiently laying out and displaying a list of uniform-height elements. -pub struct UniformList { - item_count: usize, - item_to_measure_index: usize, - render_items: Box< - dyn for<'a> Fn(Range, &'a mut Window, &'a mut App) -> SmallVec<[AnyElement; 64]>, - >, - decorations: Vec>, - interactivity: Interactivity, - scroll_handle: Option, - sizing_behavior: ListSizingBehavior, - horizontal_sizing_behavior: ListHorizontalSizingBehavior, -} - -/// Frame state used by the [UniformList]. -pub struct UniformListFrameState { - items: SmallVec<[AnyElement; 32]>, - decorations: SmallVec<[AnyElement; 2]>, -} - -/// A handle for controlling the scroll position of a uniform list. -/// This should be stored in your view and passed to the uniform_list on each frame. -#[derive(Clone, Debug, Default)] -pub struct UniformListScrollHandle(pub Rc>); - -/// Where to place the element scrolled to. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum ScrollStrategy { - /// Place the element at the top of the list's viewport. - Top, - /// Attempt to place the element in the middle of the list's viewport. - /// May not be possible if there's not enough list items above the item scrolled to: - /// in this case, the element will be placed at the closest possible position. - Center, - /// Attempt to place the element at the bottom of the list's viewport. - /// May not be possible if there's not enough list items above the item scrolled to: - /// in this case, the element will be placed at the closest possible position. - Bottom, - /// If the element is not visible attempt to place it at: - /// - The top of the list's viewport if the target element is above currently visible elements. - /// - The bottom of the list's viewport if the target element is above currently visible elements. - Nearest, -} - -#[derive(Clone, Copy, Debug)] -#[allow(missing_docs)] -pub struct DeferredScrollToItem { - /// The item index to scroll to - pub item_index: usize, - /// The scroll strategy to use - pub strategy: ScrollStrategy, - /// The offset in number of items - pub offset: usize, - pub scroll_strict: bool, -} - -#[derive(Clone, Debug, Default)] -#[allow(missing_docs)] -pub struct UniformListScrollState { - pub base_handle: ScrollHandle, - pub deferred_scroll_to_item: Option, - /// Size of the item, captured during last layout. - pub last_item_size: Option, - /// Whether the list was vertically flipped during last layout. - pub y_flipped: bool, -} - -#[derive(Copy, Clone, Debug, Default)] -/// The size of the item and its contents. -pub struct ItemSize { - /// The size of the item. - pub item: Size, - /// The size of the item's contents, which may be larger than the item itself, - /// if the item was bounded by a parent element. - pub contents: Size, -} - -impl UniformListScrollHandle { - /// Create a new scroll handle to bind to a uniform list. - pub fn new() -> Self { - Self(Rc::new(RefCell::new(UniformListScrollState { - base_handle: ScrollHandle::new(), - deferred_scroll_to_item: None, - last_item_size: None, - y_flipped: false, - }))) - } - - /// Scroll the list so that the given item index is visible. - /// - /// This uses non-strict scrolling: if the item is already fully visible, no scrolling occurs. - /// If the item is out of view, it scrolls the minimum amount to bring it into view according - /// to the strategy. - pub fn scroll_to_item(&self, ix: usize, strategy: ScrollStrategy) { - self.0.borrow_mut().deferred_scroll_to_item = Some(DeferredScrollToItem { - item_index: ix, - strategy, - offset: 0, - scroll_strict: false, - }); - } - - /// Scroll the list so that the given item index is at scroll strategy position. - /// - /// This uses strict scrolling: the item will always be scrolled to match the strategy position, - /// even if it's already visible. Use this when you need precise positioning. - pub fn scroll_to_item_strict(&self, ix: usize, strategy: ScrollStrategy) { - self.0.borrow_mut().deferred_scroll_to_item = Some(DeferredScrollToItem { - item_index: ix, - strategy, - offset: 0, - scroll_strict: true, - }); - } - - /// Scroll the list to the given item index with an offset in number of items. - /// - /// This uses non-strict scrolling: if the item is already visible within the offset region, - /// no scrolling occurs. - /// - /// The offset parameter shrinks the effective viewport by the specified number of items - /// from the corresponding edge, then applies the scroll strategy within that reduced viewport: - /// - `ScrollStrategy::Top`: Shrinks from top, positions item at the new top - /// - `ScrollStrategy::Center`: Shrinks from top, centers item in the reduced viewport - /// - `ScrollStrategy::Bottom`: Shrinks from bottom, positions item at the new bottom - pub fn scroll_to_item_with_offset(&self, ix: usize, strategy: ScrollStrategy, offset: usize) { - self.0.borrow_mut().deferred_scroll_to_item = Some(DeferredScrollToItem { - item_index: ix, - strategy, - offset, - scroll_strict: false, - }); - } - - /// Scroll the list so that the given item index is at the exact scroll strategy position with an offset. - /// - /// This uses strict scrolling: the item will always be scrolled to match the strategy position, - /// even if it's already visible. - /// - /// The offset parameter shrinks the effective viewport by the specified number of items - /// from the corresponding edge, then applies the scroll strategy within that reduced viewport: - /// - `ScrollStrategy::Top`: Shrinks from top, positions item at the new top - /// - `ScrollStrategy::Center`: Shrinks from top, centers item in the reduced viewport - /// - `ScrollStrategy::Bottom`: Shrinks from bottom, positions item at the new bottom - pub fn scroll_to_item_strict_with_offset( - &self, - ix: usize, - strategy: ScrollStrategy, - offset: usize, - ) { - self.0.borrow_mut().deferred_scroll_to_item = Some(DeferredScrollToItem { - item_index: ix, - strategy, - offset, - scroll_strict: true, - }); - } - - /// Check if the list is flipped vertically. - pub fn y_flipped(&self) -> bool { - self.0.borrow().y_flipped - } - - /// Get the index of the topmost visible child. - #[cfg(any(test, feature = "test-support"))] - pub fn logical_scroll_top_index(&self) -> usize { - let this = self.0.borrow(); - this.deferred_scroll_to_item - .as_ref() - .map(|deferred| deferred.item_index) - .unwrap_or_else(|| this.base_handle.logical_scroll_top().0) - } - - /// Checks if the list can be scrolled vertically. - pub fn is_scrollable(&self) -> bool { - if let Some(size) = self.0.borrow().last_item_size { - size.contents.height > size.item.height - } else { - false - } - } - - /// Whether the list is scrolled to the end, or `None` if the list is - /// not scrollable. - pub fn is_scrolled_to_end(&self) -> Option { - let state = self.0.borrow(); - let max_offset = state.base_handle.max_offset(); - if max_offset.y <= px(0.) { - return None; - } - let offset = state.base_handle.offset(); - Some(-offset.y >= max_offset.y) - } - - /// Scroll to the bottom of the list. - pub fn scroll_to_bottom(&self) { - self.scroll_to_item(usize::MAX, ScrollStrategy::Bottom); - } -} - -impl Styled for UniformList { - fn style(&mut self) -> &mut StyleRefinement { - &mut self.interactivity.base_style - } -} - -impl Element for UniformList { - type RequestLayoutState = UniformListFrameState; - type PrepaintState = Option; - - fn id(&self) -> Option { - self.interactivity.element_id.clone() - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - let max_items = self.item_count; - let item_size = self.measure_item(None, window, cx); - let layout_id = self.interactivity.request_layout( - global_id, - inspector_id, - window, - cx, - |style, window, cx| match self.sizing_behavior { - ListSizingBehavior::Infer => { - window.with_text_style(style.text_style().cloned(), |window| { - window.request_measured_layout( - style, - move |known_dimensions, available_space, _window, _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) - }, - ) - }) - } - ListSizingBehavior::Auto => window - .with_text_style(style.text_style().cloned(), |window| { - window.request_layout(style, None, cx) - }), - }, - ); - - ( - layout_id, - UniformListFrameState { - items: SmallVec::new(), - decorations: SmallVec::new(), - }, - ) - } - - fn prepaint( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - frame_state: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Option { - let style = self - .interactivity - .compute_style(global_id, None, window, cx); - let border = style.border_widths.to_pixels(window.rem_size()); - let padding = style - .padding - .to_pixels(bounds.size.into(), window.rem_size()); - - let padded_bounds = Bounds::from_corners( - bounds.origin + point(border.left + padding.left, border.top + padding.top), - bounds.bottom_right() - - point(border.right + padding.right, border.bottom + padding.bottom), - ); - - let can_scroll_horizontally = matches!( - self.horizontal_sizing_behavior, - ListHorizontalSizingBehavior::Unconstrained - ); - - let longest_item_size = self.measure_item(None, window, cx); - let content_width = if can_scroll_horizontally { - padded_bounds.size.width.max(longest_item_size.width) - } else { - padded_bounds.size.width - }; - let content_size = Size { - width: content_width, - height: longest_item_size.height * self.item_count, - }; - - let shared_scroll_offset = self.interactivity.scroll_offset.clone().unwrap(); - let item_height = longest_item_size.height; - let shared_scroll_to_item = self.scroll_handle.as_mut().and_then(|handle| { - let mut handle = handle.0.borrow_mut(); - handle.last_item_size = Some(ItemSize { - item: padded_bounds.size, - contents: content_size, - }); - handle.deferred_scroll_to_item.take() - }); - - self.interactivity.prepaint( - global_id, - inspector_id, - bounds, - content_size, - window, - cx, - |_style, mut scroll_offset, hitbox, window, cx| { - let y_flipped = if let Some(scroll_handle) = &self.scroll_handle { - let scroll_state = scroll_handle.0.borrow(); - scroll_state.y_flipped - } else { - false - }; - - if self.item_count > 0 { - let content_height = item_height * self.item_count; - - let is_scrolled_vertically = !scroll_offset.y.is_zero(); - let max_scroll_offset = padded_bounds.size.height - content_height; - - if is_scrolled_vertically && scroll_offset.y < max_scroll_offset { - shared_scroll_offset.borrow_mut().y = max_scroll_offset; - scroll_offset.y = max_scroll_offset; - } - - let content_width = content_size.width + padding.left + padding.right; - let is_scrolled_horizontally = - can_scroll_horizontally && !scroll_offset.x.is_zero(); - if is_scrolled_horizontally && content_width <= padded_bounds.size.width { - shared_scroll_offset.borrow_mut().x = Pixels::ZERO; - scroll_offset.x = Pixels::ZERO; - } - - if let Some(DeferredScrollToItem { - mut item_index, - mut strategy, - offset, - scroll_strict, - }) = shared_scroll_to_item - { - if y_flipped { - item_index = self.item_count.saturating_sub(item_index + 1); - } - let list_height = padded_bounds.size.height; - let mut updated_scroll_offset = shared_scroll_offset.borrow_mut(); - let item_top = item_height * item_index; - let item_bottom = item_top + item_height; - let scroll_top = -updated_scroll_offset.y; - let offset_pixels = item_height * offset; - - // is the selected item above/below currently visible items - let is_above = item_top < scroll_top + offset_pixels; - let is_below = item_bottom > scroll_top + list_height; - - if scroll_strict || is_above || is_below { - if strategy == ScrollStrategy::Nearest { - if is_above { - strategy = ScrollStrategy::Top; - } else if is_below { - strategy = ScrollStrategy::Bottom; - } - } - - let max_scroll_offset = - (content_height - list_height).max(Pixels::ZERO); - match strategy { - ScrollStrategy::Top => { - updated_scroll_offset.y = -(item_top - offset_pixels) - .clamp(Pixels::ZERO, max_scroll_offset); - } - ScrollStrategy::Center => { - let item_center = item_top + item_height / 2.0; - - let viewport_height = list_height - offset_pixels; - let viewport_center = offset_pixels + viewport_height / 2.0; - let target_scroll_top = item_center - viewport_center; - updated_scroll_offset.y = - -target_scroll_top.clamp(Pixels::ZERO, max_scroll_offset); - } - ScrollStrategy::Bottom => { - updated_scroll_offset.y = -(item_bottom - list_height) - .clamp(Pixels::ZERO, max_scroll_offset); - } - ScrollStrategy::Nearest => { - // Nearest, but the item is visible -> no scroll is required - } - } - } - scroll_offset = *updated_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 items = if y_flipped { - let flipped_range = self.item_count.saturating_sub(visible_range.end) - ..self.item_count.saturating_sub(visible_range.start); - let mut items = (self.render_items)(flipped_range, window, cx); - items.reverse(); - items - } else { - (self.render_items)(visible_range.clone(), window, cx) - }; - - let content_mask = ContentMask { - bounds, - ..Default::default() - }; - window.with_content_mask(Some(content_mask), |window| { - for (mut item, ix) in items.into_iter().zip(visible_range.clone()) { - let item_origin = padded_bounds.origin - + scroll_offset - + point(Pixels::ZERO, item_height * ix); - - let available_width = if can_scroll_horizontally { - padded_bounds.size.width + scroll_offset.x.abs() - } else { - padded_bounds.size.width - }; - let available_space = size( - AvailableSpace::Definite(available_width), - AvailableSpace::Definite(item_height), - ); - item.layout_as_root(available_space, window, cx); - item.prepaint_at(item_origin, window, cx); - frame_state.items.push(item); - } - - let bounds = - Bounds::new(padded_bounds.origin + scroll_offset, padded_bounds.size); - for decoration in &self.decorations { - let mut decoration = decoration.as_ref().compute( - visible_range.clone(), - bounds, - scroll_offset, - item_height, - self.item_count, - window, - cx, - ); - let available_space = size( - AvailableSpace::Definite(bounds.size.width), - AvailableSpace::Definite(bounds.size.height), - ); - decoration.layout_as_root(available_space, window, cx); - decoration.prepaint_at(bounds.origin, window, cx); - frame_state.decorations.push(decoration); - } - }); - } - - hitbox - }, - ) - } - - fn paint( - &mut self, - global_id: Option<&GlobalElementId>, - inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - request_layout: &mut Self::RequestLayoutState, - hitbox: &mut Option, - window: &mut Window, - cx: &mut App, - ) { - self.interactivity.paint( - global_id, - inspector_id, - bounds, - hitbox.as_ref(), - window, - cx, - |_, window, cx| { - for item in &mut request_layout.items { - item.paint(window, cx); - } - for decoration in &mut request_layout.decorations { - decoration.paint(window, cx); - } - }, - ) - } -} - -impl IntoElement for UniformList { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -/// A decoration for a [`UniformList`]. This can be used for various things, -/// such as rendering indent guides, or other visual effects. -pub trait UniformListDecoration { - /// Compute the decoration element, given the visible range of list items, - /// the bounds of the list, and the height of each item. - fn compute( - &self, - visible_range: Range, - bounds: Bounds, - scroll_offset: Point, - item_height: Pixels, - item_count: usize, - window: &mut Window, - cx: &mut App, - ) -> AnyElement; -} - -impl UniformListDecoration for Entity { - fn compute( - &self, - visible_range: Range, - bounds: Bounds, - scroll_offset: Point, - item_height: Pixels, - item_count: usize, - window: &mut Window, - cx: &mut App, - ) -> AnyElement { - self.update(cx, |inner, cx| { - inner.compute( - visible_range, - bounds, - scroll_offset, - item_height, - item_count, - window, - cx, - ) - }) - } -} - -impl UniformList { - /// Selects a specific list item for measurement. - pub fn with_width_from_item(mut self, item_index: Option) -> Self { - self.item_to_measure_index = item_index.unwrap_or(0); - self - } - - /// Sets the sizing behavior, similar to the `List` element. - pub fn with_sizing_behavior(mut self, behavior: ListSizingBehavior) -> Self { - self.sizing_behavior = behavior; - self - } - - /// Sets the horizontal sizing behavior, controlling the way list items laid out horizontally. - /// With [`ListHorizontalSizingBehavior::Unconstrained`] behavior, every item and the list itself will - /// have the size of the widest item and lay out pushing the `end_slot` to the right end. - pub fn with_horizontal_sizing_behavior( - mut self, - behavior: ListHorizontalSizingBehavior, - ) -> Self { - self.horizontal_sizing_behavior = behavior; - match behavior { - ListHorizontalSizingBehavior::FitList => { - self.interactivity.base_style.overflow.x = None; - } - ListHorizontalSizingBehavior::Unconstrained => { - self.interactivity.base_style.overflow.x = Some(Overflow::Scroll); - } - } - self - } - - /// Adds a decoration element to the list. - pub fn with_decoration(mut self, decoration: impl UniformListDecoration + 'static) -> Self { - self.decorations.push(Box::new(decoration)); - self - } - - fn measure_item( - &self, - list_width: Option, - window: &mut Window, - cx: &mut App, - ) -> Size { - 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, window, cx); - let Some(mut item_to_measure) = items.pop() else { - return Size::default(); - }; - let available_space = size( - list_width.map_or(AvailableSpace::MaxContent, |width| { - AvailableSpace::Definite(width) - }), - AvailableSpace::MinContent, - ); - item_to_measure.layout_as_root(available_space, window, cx) - } - - /// Track and render scroll state of this list with reference to the given scroll handle. - pub fn track_scroll(mut self, handle: &UniformListScrollHandle) -> Self { - self.interactivity.tracked_scroll_handle = Some(handle.0.borrow().base_handle.clone()); - self.scroll_handle = Some(handle.clone()); - self - } - - /// Sets whether the list is flipped vertically, such that item 0 appears at the bottom. - pub fn y_flipped(mut self, y_flipped: bool) -> Self { - if let Some(ref scroll_handle) = self.scroll_handle { - let mut scroll_state = scroll_handle.0.borrow_mut(); - let mut base_handle = &scroll_state.base_handle; - let offset = base_handle.offset(); - match scroll_state.last_item_size { - Some(last_size) if scroll_state.y_flipped != y_flipped => { - let new_y_offset = - -(offset.y + last_size.contents.height - last_size.item.height); - base_handle.set_offset(point(offset.x, new_y_offset)); - scroll_state.y_flipped = y_flipped; - } - // Handle case where list is initially flipped. - None if y_flipped => { - base_handle.set_offset(point(offset.x, Pixels::MIN)); - scroll_state.y_flipped = y_flipped; - } - _ => {} - } - } - self - } -} - -impl InteractiveElement for UniformList { - fn interactivity(&mut self) -> &mut crate::Interactivity { - &mut self.interactivity - } -} - -#[cfg(test)] -mod test { - use crate::TestAppContext; - - #[gpui::test] - fn test_scroll_strategy_nearest(cx: &mut TestAppContext) { - use crate::{ - Context, FocusHandle, ScrollStrategy, UniformListScrollHandle, Window, div, prelude::*, - px, uniform_list, - }; - use std::ops::Range; - - actions!(example, [SelectNext, SelectPrev]); - - struct TestView { - index: usize, - length: usize, - scroll_handle: UniformListScrollHandle, - focus_handle: FocusHandle, - visible_range: Range, - } - - impl TestView { - pub fn select_next( - &mut self, - _: &SelectNext, - window: &mut Window, - _: &mut Context, - ) { - if self.index + 1 == self.length { - self.index = 0 - } else { - self.index += 1; - } - self.scroll_handle - .scroll_to_item(self.index, ScrollStrategy::Nearest); - window.refresh(); - } - - pub fn select_previous( - &mut self, - _: &SelectPrev, - window: &mut Window, - _: &mut Context, - ) { - if self.index == 0 { - self.index = self.length - 1 - } else { - self.index -= 1; - } - self.scroll_handle - .scroll_to_item(self.index, ScrollStrategy::Nearest); - window.refresh(); - } - } - - impl Render for TestView { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .id("list-example") - .track_focus(&self.focus_handle) - .on_action(cx.listener(Self::select_next)) - .on_action(cx.listener(Self::select_previous)) - .size_full() - .child( - uniform_list( - "entries", - self.length, - cx.processor(|this, range: Range, _window, _cx| { - this.visible_range = range.clone(); - range - .map(|ix| div().id(ix).h(px(20.0)).child(format!("Item {ix}"))) - .collect() - }), - ) - .track_scroll(&self.scroll_handle) - .h(px(200.0)), - ) - } - } - - let (view, cx) = cx.add_window_view(|window, cx| { - let focus_handle = cx.focus_handle(); - window.focus(&focus_handle, cx); - TestView { - scroll_handle: UniformListScrollHandle::new(), - index: 0, - focus_handle, - length: 47, - visible_range: 0..0, - } - }); - - // 10 out of 47 items are visible - - // First 9 times selecting next item does not scroll - for ix in 1..10 { - cx.dispatch_action(SelectNext); - view.read_with(cx, |view, _| { - assert_eq!(view.index, ix); - assert_eq!(view.visible_range, 0..10); - }) - } - - // Now each time the list scrolls down by 1 - for ix in 10..47 { - cx.dispatch_action(SelectNext); - view.read_with(cx, |view, _| { - assert_eq!(view.index, ix); - assert_eq!(view.visible_range, ix - 9..ix + 1); - }) - } - - // After the last item we move back to the start - cx.dispatch_action(SelectNext); - view.read_with(cx, |view, _| { - assert_eq!(view.index, 0); - assert_eq!(view.visible_range, 0..10); - }); - - // Return to the last element - cx.dispatch_action(SelectPrev); - view.read_with(cx, |view, _| { - assert_eq!(view.index, 46); - assert_eq!(view.visible_range, 37..47); - }); - - // First 9 times selecting previous does not scroll - for ix in (37..46).rev() { - cx.dispatch_action(SelectPrev); - view.read_with(cx, |view, _| { - assert_eq!(view.index, ix); - assert_eq!(view.visible_range, 37..47); - }) - } - - // Now each time the list scrolls up by 1 - for ix in (0..37).rev() { - cx.dispatch_action(SelectPrev); - view.read_with(cx, |view, _| { - assert_eq!(view.index, ix); - assert_eq!(view.visible_range, ix..ix + 10); - }) - } - } -} diff --git a/crates/gpui_pre/src/executor.rs b/crates/gpui_pre/src/executor.rs deleted file mode 100644 index 444bb071..00000000 --- a/crates/gpui_pre/src/executor.rs +++ /dev/null @@ -1,588 +0,0 @@ -use crate::{App, PlatformDispatcher, PlatformScheduler}; -#[cfg(not(target_family = "wasm"))] -use futures::channel::mpsc; -use futures::prelude::*; -use gpui_util::{TryFutureExt, TryFutureExtBacktrace}; -use scheduler::Instant; -use scheduler::Scheduler; -use std::{future::Future, marker::PhantomData, rc::Rc, sync::Arc, time::Duration}; -#[cfg(not(target_family = "wasm"))] -use std::{mem, pin::Pin}; - -pub use scheduler::{ - DedicatedExecutor, FallibleTask, LocalExecutor as SchedulerLocalExecutor, Priority, Task, -}; - -/// A pointer to the executor that is currently running, -/// for spawning background tasks. -#[derive(Clone)] -pub struct BackgroundExecutor { - inner: scheduler::BackgroundExecutor, - dispatcher: Arc, -} - -/// A pointer to the executor that is currently running, -/// for spawning tasks on the main thread. -#[derive(Clone)] -pub struct ForegroundExecutor { - inner: scheduler::LocalExecutor, - dispatcher: Arc, - #[cfg(feature = "profiler")] - foreground_runnables: Option, - not_send: PhantomData>, -} - -/// Extension trait for `Task>` that adds `detach_and_log_err` with an `&App` context. -/// -/// This trait is automatically implemented for all `Task>` types. -pub trait TaskExt { - /// Run the task to completion in the background and log any errors that occur. - fn detach_and_log_err(self, cx: &App); - /// Like [`Self::detach_and_log_err`], but uses `{:?}` formatting on failure so `anyhow::Error` - /// values emit their full backtrace. Prefer `detach_and_log_err` unless a backtrace is wanted. - fn detach_and_log_err_with_backtrace(self, cx: &App); -} - -impl TaskExt for Task> -where - T: 'static, - E: 'static + std::fmt::Display + std::fmt::Debug, -{ - #[track_caller] - fn detach_and_log_err(self, cx: &App) { - let location = core::panic::Location::caller(); - cx.foreground_executor() - .spawn(self.log_tracked_err(*location)) - .detach(); - } - - #[track_caller] - fn detach_and_log_err_with_backtrace(self, cx: &App) { - let location = *core::panic::Location::caller(); - cx.foreground_executor() - .spawn(self.log_tracked_err_with_backtrace(location)) - .detach(); - } -} - -impl BackgroundExecutor { - /// Creates a new BackgroundExecutor from the given PlatformDispatcher. - pub fn new(dispatcher: Arc) -> Self { - #[cfg(any(test, feature = "test-support"))] - let scheduler: Arc = if let Some(test_dispatcher) = dispatcher.as_test() { - test_dispatcher.scheduler().clone() - } else { - Arc::new(PlatformScheduler::new(dispatcher.clone())) - }; - - #[cfg(not(any(test, feature = "test-support")))] - let scheduler: Arc = Arc::new(PlatformScheduler::new(dispatcher.clone())); - - Self { - inner: scheduler::BackgroundExecutor::new(scheduler), - dispatcher, - } - } - - /// Returns the underlying scheduler::BackgroundExecutor. - /// - /// This is used by Ex to pass the executor to thread/worktree code. - pub fn scheduler_executor(&self) -> scheduler::BackgroundExecutor { - self.inner.clone() - } - - /// Spawn a closure on a fresh session pinned to its own [`SchedulerLocalExecutor`]. - /// The closure runs on a new OS thread under the platform scheduler, or on - /// the test scheduler's loop in tests. - /// - /// Prefer this over [`Self::spawn`] for futures whose polls need more stack - /// than shared background threads guarantee. Dedicated threads get the - /// standard library's default 2 MiB, while `spawn` polls futures on - /// whatever threads the platform dispatcher provides — on macOS those are - /// GCD workers whose stacks are fixed at 512 KiB by the kernel (see `PTH_DEFAULT_STACKSIZE` in - /// ), - /// the tightest background-stack budget of any platform. - #[track_caller] - pub fn spawn_dedicated(&self, f: F) -> Task - where - F: FnOnce(SchedulerLocalExecutor) -> Fut + Send + 'static, - Fut: Future + 'static, - Fut::Output: Send + Sync + 'static, - { - self.inner.spawn_dedicated(f) - } - - /// Enqueues the given future to be run to completion on a background thread. - #[track_caller] - pub fn spawn(&self, future: impl Future + Send + 'static) -> Task - where - R: Send + 'static, - { - self.spawn_with_priority(Priority::default(), future.boxed()) - } - - /// Enqueues the given future to be run to completion on a background thread with the given priority. - /// - /// When `Priority::RealtimeAudio` is used, the task runs on a dedicated thread with - /// realtime scheduling priority, suitable for audio processing. - #[track_caller] - pub fn spawn_with_priority( - &self, - priority: Priority, - future: impl Future + Send + 'static, - ) -> Task - where - R: Send + 'static, - { - if priority == Priority::RealtimeAudio { - self.inner.spawn_realtime(future) - } else { - self.inner.spawn_with_priority(priority, future) - } - } - - /// Runs background tasks that may borrow from their environment and waits for all of them to complete. - /// - /// Dropping the returned future cancels its tasks and synchronously waits for their futures to - /// be destroyed before returning. - #[cfg(not(target_family = "wasm"))] - pub async fn scoped<'scope, F>(&self, scheduler: F) - where - F: FnOnce(&mut Scope<'scope>), - { - let mut scope = Scope::new(self.clone(), Priority::default()); - (scheduler)(&mut scope); - let spawned = mem::take(&mut scope.futures) - .into_iter() - .map(|f| self.spawn_with_priority(scope.priority, f)) - .collect::>(); - for task in spawned { - task.await; - } - } - - /// Runs prioritized background tasks that may borrow from their environment and waits for all - /// of them to complete. - /// - /// Dropping the returned future cancels its tasks and synchronously waits for their futures to - /// be destroyed before returning. - #[cfg(not(target_family = "wasm"))] - pub async fn scoped_priority<'scope, F>(&self, priority: Priority, scheduler: F) - where - F: FnOnce(&mut Scope<'scope>), - { - let mut scope = Scope::new(self.clone(), priority); - (scheduler)(&mut scope); - let spawned = mem::take(&mut scope.futures) - .into_iter() - .map(|f| self.spawn_with_priority(scope.priority, f)) - .collect::>(); - for task in spawned { - task.await; - } - } - - /// Get the current time. - /// - /// Calling this instead of `std::time::Instant::now` allows the use - /// of fake timers in tests. - pub fn now(&self) -> Instant { - self.inner.scheduler().clock().now() - } - - /// Returns a task that will complete after the given duration. - /// Depending on other concurrent tasks the elapsed duration may be longer - /// than requested. - #[track_caller] - pub fn timer(&self, duration: Duration) -> Task<()> { - if duration.is_zero() { - return Task::ready(()); - } - self.spawn(self.inner.scheduler().timer(duration)) - } - - /// In tests, run an arbitrary number of tasks (determined by the SEED environment variable) - #[cfg(any(test, feature = "test-support"))] - pub fn simulate_random_delay(&self) -> impl Future + use<> { - self.dispatcher.as_test().unwrap().simulate_random_delay() - } - - /// In tests, move time forward. This does not run any tasks, but does make `timer`s ready. - #[cfg(any(test, feature = "test-support"))] - pub fn advance_clock(&self, duration: Duration) { - self.dispatcher.as_test().unwrap().advance_clock(duration) - } - - /// In tests, run one task. - #[cfg(any(test, feature = "test-support"))] - pub fn tick(&self) -> bool { - self.dispatcher.as_test().unwrap().scheduler().tick() - } - - /// In tests, run tasks until the scheduler would park. - /// - /// Under the scheduler-backed test dispatcher, `tick()` will not advance the clock, so a pending - /// timer can keep `has_pending_tasks()` true even after all currently-runnable tasks have been - /// drained. To preserve the historical semantics that tests relied on (drain all work that can - /// make progress), we advance the clock to the next timer when no runnable tasks remain. - #[cfg(any(test, feature = "test-support"))] - pub fn run_until_parked(&self) { - let scheduler = self.dispatcher.as_test().unwrap().scheduler(); - scheduler.run(); - } - - /// In tests, prevents `run_until_parked` from panicking if there are outstanding tasks. - #[cfg(any(test, feature = "test-support"))] - pub fn allow_parking(&self) { - self.dispatcher - .as_test() - .unwrap() - .scheduler() - .allow_parking(); - - if std::env::var("GPUI_RUN_UNTIL_PARKED_LOG").ok().as_deref() == Some("1") { - log::warn!("[gpui::executor] allow_parking: enabled"); - } - } - - /// Sets the range of ticks to run before timing out in block_on. - #[cfg(any(test, feature = "test-support"))] - pub fn set_block_on_ticks(&self, range: std::ops::RangeInclusive) { - self.dispatcher - .as_test() - .unwrap() - .scheduler() - .set_timeout_ticks(range); - } - - /// Undoes the effect of [`Self::allow_parking`]. - #[cfg(any(test, feature = "test-support"))] - pub fn forbid_parking(&self) { - self.dispatcher - .as_test() - .unwrap() - .scheduler() - .forbid_parking(); - } - - /// In tests, returns the rng used by the dispatcher. - #[cfg(any(test, feature = "test-support"))] - pub fn rng(&self) -> scheduler::SharedRng { - self.dispatcher.as_test().unwrap().scheduler().rng() - } - - /// How many CPUs are available to the dispatcher. - pub fn num_cpus(&self) -> usize { - #[cfg(any(test, feature = "test-support"))] - if let Some(test) = self.dispatcher.as_test() { - return test.num_cpus_override().unwrap_or(4); - } - num_cpus::get() - } - - /// Override the number of CPUs reported by this executor in tests. - /// Panics if not called on a test executor. - #[cfg(any(test, feature = "test-support"))] - pub fn set_num_cpus(&self, count: usize) { - self.dispatcher - .as_test() - .expect("set_num_cpus can only be called on a test executor") - .set_num_cpus(count); - } - - /// Whether we're on the main thread. - pub fn is_main_thread(&self) -> bool { - self.dispatcher.is_main_thread() - } - - #[doc(hidden)] - pub fn dispatcher(&self) -> &Arc { - &self.dispatcher - } -} - -impl ForegroundExecutor { - /// Creates a new ForegroundExecutor from the given PlatformDispatcher. - pub fn new(dispatcher: Arc) -> Self { - #[cfg(any(test, feature = "test-support"))] - let (scheduler, session_id): (Arc, _) = - if let Some(test_dispatcher) = dispatcher.as_test() { - ( - test_dispatcher.scheduler().clone(), - test_dispatcher.session_id(), - ) - } else { - let platform_scheduler = Arc::new(PlatformScheduler::new(dispatcher.clone())); - let inner = platform_scheduler.foreground_executor(); - return Self { - inner, - dispatcher, - #[cfg(feature = "profiler")] - foreground_runnables: Some(platform_scheduler.foreground_runnable_counter()), - not_send: PhantomData, - }; - }; - - #[cfg(not(any(test, feature = "test-support")))] - let platform_scheduler = Arc::new(PlatformScheduler::new(dispatcher.clone())); - #[cfg(not(any(test, feature = "test-support")))] - let inner = platform_scheduler.foreground_executor(); - #[cfg(all(not(any(test, feature = "test-support")), feature = "profiler"))] - let foreground_runnables = Some(platform_scheduler.foreground_runnable_counter()); - - #[cfg(any(test, feature = "test-support"))] - let inner = { - let scheduler_for_dispatch = Arc::downgrade(&scheduler); - scheduler::LocalExecutor::new(session_id, scheduler, move |runnable| { - if let Some(scheduler) = scheduler_for_dispatch.upgrade() { - scheduler.schedule_local(session_id, runnable); - } - }) - }; - - #[cfg(all(any(test, feature = "test-support"), feature = "profiler"))] - // The deterministic test scheduler does not invoke GPUI's task profiler - // hooks, so an increment here would have no matching decrement. - let foreground_runnables = None; - - Self { - inner, - dispatcher, - #[cfg(feature = "profiler")] - foreground_runnables, - not_send: PhantomData, - } - } - - /// Enqueues the given Task to run on the main thread. - #[track_caller] - pub fn spawn(&self, future: impl Future + 'static) -> Task - where - R: 'static, - { - self.inner.spawn(future.boxed_local()) - } - - /// Enqueues the given Task to run on the main thread with the given priority. - #[track_caller] - pub fn spawn_with_priority( - &self, - _priority: Priority, - future: impl Future + 'static, - ) -> Task - where - R: 'static, - { - // Priority is ignored for foreground tasks - they run in order on the main thread - self.inner.spawn(future) - } - - /// On platforms with dedicated support, enqueues the given future to run - /// on the main thread during platform idle time. Without a `timeout`, - /// polls may be deferred indefinitely while the platform stays busy; - /// with one, a poll still waiting after that long runs as ordinary main-thread work. - /// Each poll occupies part of one idle slice, so long synchronous stretches - /// should bound themselves against [`Self::idle_time_remaining`] and yield. - /// - /// On platforms without dedicated support, schedules the given future to run - /// with a low priority, ignoring `timeout`. - #[track_caller] - pub fn spawn_when_idle( - &self, - timeout: Option, - future: impl Future + 'static, - ) -> Task - where - R: 'static, - { - let dispatcher = self.dispatcher.clone(); - #[cfg(feature = "profiler")] - let foreground_runnables = self.foreground_runnables.clone(); - self.inner - .spawn_with_dispatch(future.boxed_local(), move |runnable| { - #[cfg(feature = "profiler")] - if let Some(foreground_runnables) = &foreground_runnables { - foreground_runnables.queued(); - } - dispatcher.dispatch_on_main_thread_when_idle(runnable, timeout); - }) - } - - /// The time remaining in the current idle slice, when called from a task - /// spawned via [`Self::spawn_when_idle`] on a platform that meters idle - /// time. `None` when idle time is unmetered (or the caller is not inside - /// an idle slice); work that must bound itself should then fall back to a - /// budget of its own. - pub fn idle_time_remaining(&self) -> Option { - self.dispatcher.idle_time_remaining() - } - - /// Used by the test harness to run an async test in a synchronous fashion. - #[cfg(all(not(target_family = "wasm"), any(test, feature = "test-support")))] - #[track_caller] - pub fn block_test(&self, future: impl Future) -> R { - use std::cell::Cell; - - let scheduler = self.inner.scheduler(); - - let output = Cell::new(None); - let future = async { - output.set(Some(future.await)); - }; - let mut future = std::pin::pin!(future); - - // In async GPUI tests, we must allow foreground tasks scheduled by the test itself - // (which are associated with the test session) to make progress while we block. - // Otherwise, awaiting futures that depend on same-session foreground work can deadlock. - scheduler.block(None, future.as_mut(), None); - - output.take().expect("block_test future did not complete") - } - - /// Block the current thread until the given future resolves. - /// Consider using `block_with_timeout` instead. - #[cfg(not(target_family = "wasm"))] - pub fn block_on(&self, future: impl Future) -> R { - self.inner.block_on(future) - } - - /// Block the current thread until the given future resolves or the timeout elapses. - #[cfg(not(target_family = "wasm"))] - pub fn block_with_timeout>( - &self, - duration: Duration, - future: Fut, - ) -> Result + use> { - self.inner.block_with_timeout(duration, future) - } - - #[doc(hidden)] - pub fn dispatcher(&self) -> &Arc { - &self.dispatcher - } - - #[doc(hidden)] - pub fn scheduler_executor(&self) -> SchedulerLocalExecutor { - self.inner.clone() - } -} - -/// Scope manages a set of tasks that are enqueued and waited on together. See [`BackgroundExecutor::scoped`]. -#[cfg(not(target_family = "wasm"))] -pub struct Scope<'a> { - executor: BackgroundExecutor, - priority: Priority, - futures: Vec + Send + 'static>>>, - tx: Option>, - rx: mpsc::Receiver<()>, - lifetime: PhantomData<&'a ()>, -} - -#[cfg(not(target_family = "wasm"))] -impl<'a> Scope<'a> { - fn new(executor: BackgroundExecutor, priority: Priority) -> Self { - let (tx, rx) = mpsc::channel(1); - Self { - executor, - priority, - tx: Some(tx), - rx, - futures: Default::default(), - lifetime: PhantomData, - } - } - - /// How many CPUs are available to the dispatcher. - pub fn num_cpus(&self) -> usize { - self.executor.num_cpus() - } - - /// Spawn a future into this scope. - #[track_caller] - pub fn spawn(&mut self, f: F) - where - F: Future + 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 + Send + 'a>>, - Pin + Send + 'static>>, - >(Box::pin(async move { - f.await; - drop(tx); - })) - }; - self.futures.push(f); - } -} - -#[cfg(not(target_family = "wasm"))] -impl Drop for Scope<'_> { - fn drop(&mut self) { - self.tx.take().unwrap(); - - // Wait until the channel is closed, which means that all of the spawned - // futures have resolved. - let future = async { - self.rx.next().await; - }; - let mut future = std::pin::pin!(future); - self.executor - .inner - .scheduler() - .block(None, future.as_mut(), None); - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{App, TestDispatcher, TestPlatform}; - use std::cell::RefCell; - - /// Helper to create test infrastructure. - /// Returns (dispatcher, background_executor, app). - fn create_test_app() -> (TestDispatcher, BackgroundExecutor, Rc) { - let dispatcher = TestDispatcher::new(0); - let arc_dispatcher = Arc::new(dispatcher.clone()); - let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(arc_dispatcher); - - let platform = TestPlatform::new(background_executor.clone(), foreground_executor); - let asset_source = Arc::new(()); - let http_client = http_client::FakeHttpClient::with_404_response(); - - let app = App::new_app(platform, asset_source, http_client); - (dispatcher, background_executor, app) - } - - #[test] - fn sanity_test_tasks_run() { - let (dispatcher, _background_executor, app) = create_test_app(); - let foreground_executor = app.borrow().foreground_executor.clone(); - - let task_ran = Rc::new(RefCell::new(false)); - - foreground_executor - .spawn({ - let task_ran = Rc::clone(&task_ran); - async move { - *task_ran.borrow_mut() = true; - } - }) - .detach(); - - // Run dispatcher while app is still alive - dispatcher.run_until_parked(); - - // Task should have run - assert!( - *task_ran.borrow(), - "Task should run normally when app is alive" - ); - } -} diff --git a/crates/gpui_pre/src/geometry.rs b/crates/gpui_pre/src/geometry.rs deleted file mode 100644 index 05dafbf9..00000000 --- a/crates/gpui_pre/src/geometry.rs +++ /dev/null @@ -1,4008 +0,0 @@ -//! The GPUI geometry module is a collection of types and traits that -//! can be used to describe common units, concepts, and the relationships -//! between them. - -use anyhow::{Context as _, anyhow}; -use core::fmt::Debug; -use derive_more::{Add, AddAssign, Div, DivAssign, Mul, Neg, Sub, SubAssign}; -use refineable::Refineable; -use schemars::{JsonSchema, json_schema}; -use serde::{Deserialize, Deserializer, Serialize, Serializer, de}; -use std::borrow::Cow; -use std::ops::{AddAssign, Range}; -use std::{ - cmp::{self, PartialOrd}, - fmt::{self, Display}, - hash::Hash, - ops::{Add, Div, Mul, MulAssign, Neg, Sub}, -}; -use taffy::prelude::{TaffyGridLine, TaffyGridSpan}; - -use crate::{App, DisplayId}; - -/// Axis in a 2D cartesian space. -#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] -pub enum Axis { - /// The y axis, or up and down - Vertical, - /// The x axis, or left and right - Horizontal, -} - -impl Axis { - /// Swap this axis to the opposite axis. - pub fn invert(self) -> Self { - match self { - Axis::Vertical => Axis::Horizontal, - Axis::Horizontal => Axis::Vertical, - } - } -} - -/// A trait for accessing the given unit along a certain axis. -pub trait Along { - /// The unit associated with this type - type Unit; - - /// Returns the unit along the given axis. - fn along(&self, axis: Axis) -> Self::Unit; - - /// Applies the given function to the unit along the given axis and returns a new value. - fn apply_along(&self, axis: Axis, f: impl FnOnce(Self::Unit) -> Self::Unit) -> Self; -} - -/// Describes a location in a 2D cartesian 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 gpui::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, - Serialize, - Deserialize, - JsonSchema, - Hash, - Neg, -)] -#[refineable(Debug, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub struct Point { - /// The x coordinate of the point. - pub x: T, - /// The y coordinate of the point. - pub y: T, -} - -/// Constructs a new `Point` 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` with the specified coordinates. -/// -/// # Examples -/// -/// ``` -/// use gpui::point; -/// let p = point(10, 20); -/// assert_eq!(p.x, 10); -/// assert_eq!(p.y, 20); -/// ``` -pub const fn point(x: T, y: T) -> Point { - Point { x, y } -} - -impl Point { - /// 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 - /// - /// ``` - /// use gpui::Point; - /// 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` by applying the given function to both coordinates. - /// - /// This method allows for converting a `Point` to a `Point` 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 gpui::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 }); - /// ``` - #[must_use] - pub fn map(&self, f: impl Fn(T) -> U) -> Point { - Point { - x: f(self.x.clone()), - y: f(self.y.clone()), - } - } -} - -impl Along for Point { - 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 { - 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 { - /// 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 gpui::{Point, Pixels, ScaledPixels}; - /// let p = Point { x: Pixels::from(10.0), y: Pixels::from(20.0) }; - /// let scaled_p = p.scale(1.5); - /// assert_eq!(scaled_p, Point { x: ScaledPixels::from(15.0), y: ScaledPixels::from(30.0) }); - /// ``` - pub fn scale(&self, factor: f32) -> Point { - 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 gpui::{Pixels, Point}; - /// let p = Point { x: Pixels::from(3.0), y: Pixels::from(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 Point -where - T: Sub + Clone + Debug + Default + PartialEq, -{ - /// Get the position of this point, relative to the given origin - pub fn relative_to(&self, origin: &Point) -> Point { - point( - self.x.clone() - origin.x.clone(), - self.y.clone() - origin.y.clone(), - ) - } -} - -impl Mul for Point -where - T: Mul + Clone + Debug + Default + PartialEq, - Rhs: Clone + Debug, -{ - type Output = Point; - - fn mul(self, rhs: Rhs) -> Self::Output { - Point { - x: self.x * rhs.clone(), - y: self.y * rhs, - } - } -} - -impl MulAssign for Point -where - T: Mul + Clone + Debug + Default + PartialEq, - S: Clone, -{ - fn mul_assign(&mut self, rhs: S) { - self.x = self.x.clone() * rhs.clone(); - self.y = self.y.clone() * rhs; - } -} - -impl Div for Point -where - T: Div + Clone + Debug + Default + PartialEq, - S: Clone, -{ - type Output = Self; - - fn div(self, rhs: S) -> Self::Output { - Self { - x: self.x / rhs.clone(), - y: self.y / rhs, - } - } -} - -impl Point -where - T: PartialOrd + Clone + Debug + Default + PartialEq, -{ - /// 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 gpui::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 gpui::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 gpui::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 Clone for Point { - fn clone(&self) -> Self { - Self { - x: self.x.clone(), - y: self.y.clone(), - } - } -} - -impl Display for Point { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({}, {})", self.x, self.y) - } -} - -/// 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( - Add, Clone, Copy, Default, Deserialize, Div, Hash, Neg, PartialEq, Refineable, Serialize, Sub, -)] -#[refineable(Debug, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub struct Size { - /// The width component of the size. - pub width: T, - /// The height component of the size. - pub height: T, -} - -impl Size { - /// Create a new Size, a synonym for [`size`] - pub fn new(width: T, height: T) -> Self { - size(width, height) - } -} - -/// Constructs a new `Size` with the provided width and height. -/// -/// # Arguments -/// -/// * `width` - The width component of the `Size`. -/// * `height` - The height component of the `Size`. -/// -/// # Examples -/// -/// ``` -/// use gpui::size; -/// let my_size = size(10, 20); -/// assert_eq!(my_size.width, 10); -/// assert_eq!(my_size.height, 20); -/// ``` -pub const fn size(width: T, height: T) -> Size -where - T: Clone + Debug + Default + PartialEq, -{ - Size { width, height } -} - -impl Size -where - T: Clone + Debug + Default + PartialEq, -{ - /// Applies a function to the width and height of the size, producing a new `Size`. - /// - /// This method allows for converting a `Size` to a `Size` 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 gpui::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(&self, f: impl Fn(T) -> U) -> Size - where - U: Clone + Debug + Default + PartialEq, - { - Size { - width: f(self.width.clone()), - height: f(self.height.clone()), - } - } -} - -impl Size -where - T: Clone + Debug + Default + PartialEq + Half, -{ - /// Compute the center point of the size.g - pub fn center(&self) -> Point { - Point { - x: self.width.half(), - y: self.height.half(), - } - } -} - -impl Size { - /// 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` that is proportionally larger or smaller - /// depending on the factor. - /// - /// # Arguments - /// - /// * `factor` - The scaling factor to apply to the width and height. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Size, Pixels, ScaledPixels}; - /// let size = Size { width: Pixels::from(100.0), height: Pixels::from(50.0) }; - /// let scaled_size = size.scale(2.0); - /// assert_eq!(scaled_size, Size { width: ScaledPixels::from(200.0), height: ScaledPixels::from(100.0) }); - /// ``` - pub fn scale(&self, factor: f32) -> Size { - Size { - width: self.width.scale(factor), - height: self.height.scale(factor), - } - } -} - -impl Along for Size -where - T: Clone + Debug + Default + PartialEq, -{ - 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 Size -where - T: PartialOrd + Clone + Debug + Default + PartialEq, -{ - /// 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 gpui::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() - }, - } - } - - /// Returns a new `Size` with the minimum width and height from `self` and `other`. - /// - /// # Arguments - /// - /// * `other` - A reference to another `Size` to compare with `self`. - /// - /// # Examples - /// - /// ``` - /// # use gpui::Size; - /// let size1 = Size { width: 30, height: 40 }; - /// let size2 = Size { width: 50, height: 20 }; - /// let min_size = size1.min(&size2); - /// assert_eq!(min_size, Size { width: 30, height: 20 }); - /// ``` - pub fn min(&self, other: &Self) -> Self { - Size { - width: if self.width >= other.width { - other.width.clone() - } else { - self.width.clone() - }, - height: if self.height >= other.height { - other.height.clone() - } else { - self.height.clone() - }, - } - } -} - -impl Mul for Size -where - T: Mul + Clone + Debug + Default + PartialEq, - Rhs: Clone + Debug + Default + PartialEq, -{ - type Output = Size; - - fn mul(self, rhs: Rhs) -> Self::Output { - Size { - width: self.width * rhs.clone(), - height: self.height * rhs, - } - } -} - -impl MulAssign for Size -where - T: Mul + Clone + Debug + Default + PartialEq, - S: Clone, -{ - fn mul_assign(&mut self, rhs: S) { - self.width = self.width.clone() * rhs.clone(); - self.height = self.height.clone() * rhs; - } -} - -impl Eq for Size where T: Eq + Clone + Debug + Default + PartialEq {} - -impl Debug for Size -where - T: Clone + Debug + Default + PartialEq, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Size {{ {:?} × {:?} }}", self.width, self.height) - } -} - -impl Display for Size { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} × {}", self.width, self.height) - } -} - -impl From> for Size { - fn from(point: Point) -> Self { - Self { - width: point.x, - height: point.y, - } - } -} - -impl From> for Size { - fn from(size: Size) -> Self { - Size { - width: size.width.into(), - height: size.height.into(), - } - } -} - -impl From> for Size { - fn from(size: Size) -> Self { - Size { - width: size.width.into(), - height: size.height.into(), - } - } -} - -impl Size { - /// 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` 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 { - /// 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` 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` which defines the top left corner of the rectangle, -/// and the size is represented as a `Size` which defines the width and height of the rectangle. -/// -/// # Examples -/// -/// ``` -/// # use gpui::{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, Copy, Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] -#[refineable(Debug)] -#[repr(C)] -pub struct Bounds { - /// The origin point of this area. - pub origin: Point, - /// The size of the rectangle. - pub size: Size, -} - -/// Create a bounds with the given origin and size -pub fn bounds( - origin: Point, - size: Size, -) -> Bounds { - Bounds { origin, size } -} - -impl Bounds { - /// Generate a centered bounds for the given display or primary display if none is provided - pub fn centered(display_id: Option, size: Size, cx: &App) -> Self { - let display = display_id - .and_then(|id| cx.find_display(id)) - .or_else(|| cx.primary_display()); - - display - .map(|display| Bounds::centered_at(display.bounds().center(), size)) - .unwrap_or_else(|| Bounds { - origin: point(px(0.), px(0.)), - size, - }) - } - - /// Generate maximized bounds for the given display or primary display if none is provided - pub fn maximized(display_id: Option, cx: &App) -> Self { - let display = display_id - .and_then(|id| cx.find_display(id)) - .or_else(|| cx.primary_display()); - - display - .map(|display| display.bounds()) - .unwrap_or_else(|| Bounds { - origin: point(px(0.), px(0.)), - size: size(px(1024.), px(768.)), - }) - } -} - -impl Bounds -where - T: Clone + Debug + Default + PartialEq, -{ - /// Creates a new `Bounds` with the specified origin and size. - /// - /// # Arguments - /// - /// * `origin` - A `Point` representing the origin of the bounds. - /// * `size` - A `Size` representing the size of the bounds. - /// - /// # Returns - /// - /// Returns a `Bounds` that has the given origin and size. - pub fn new(origin: Point, size: Size) -> Self { - Bounds { origin, size } - } -} - -impl Bounds -where - T: Sub + Clone + Debug + Default + PartialEq, -{ - /// Constructs a `Bounds` from two corner points: the top left and bottom right corners. - /// - /// This function calculates the origin and size of the `Bounds` based on the provided corner points. - /// The origin is set to the top left corner, and the size is determined by the difference between - /// the x and y coordinates of the bottom right and top left points. - /// - /// # Arguments - /// - /// * `top_left` - A `Point` representing the top left corner of the rectangle. - /// * `bottom_right` - A `Point` representing the bottom right corner of the rectangle. - /// - /// # Returns - /// - /// Returns a `Bounds` that encompasses the area defined by the two corner points. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point}; - /// let top_left = Point { x: 0, y: 0 }; - /// let bottom_right = Point { x: 10, y: 10 }; - /// let bounds = Bounds::from_corners(top_left, bottom_right); - /// - /// assert_eq!(bounds.origin, top_left); - /// assert_eq!(bounds.size.width, 10); - /// assert_eq!(bounds.size.height, 10); - /// ``` - pub fn from_corners(top_left: Point, bottom_right: Point) -> Self { - let origin = Point { - x: top_left.x.clone(), - y: top_left.y.clone(), - }; - let size = Size { - width: bottom_right.x - top_left.x, - height: bottom_right.y - top_left.y, - }; - Bounds { origin, size } - } -} - -impl Bounds -where - T: Sub + Half + Clone + Debug + Default + PartialEq, -{ - /// Constructs a `Bounds` from a corner point and size. The specified corner will be placed at - /// the specified origin. - pub fn from_anchor_and_size(corner: Anchor, origin: Point, size: Size) -> Bounds { - let origin = match corner { - Anchor::TopLeft => origin, - Anchor::TopRight => Point { - x: origin.x - size.width.clone(), - y: origin.y, - }, - Anchor::BottomLeft => Point { - x: origin.x, - y: origin.y - size.height.clone(), - }, - Anchor::BottomRight => Point { - x: origin.x - size.width.clone(), - y: origin.y - size.height.clone(), - }, - Anchor::TopCenter => Point { - x: origin.x - size.width.half(), - y: origin.y, - }, - Anchor::BottomCenter => Point { - x: origin.x - size.width.half(), - y: origin.y - size.height.clone(), - }, - Anchor::LeftCenter => Point { - x: origin.x, - y: origin.y - size.height.half(), - }, - Anchor::RightCenter => Point { - x: origin.x - size.width.clone(), - y: origin.y - size.height.half(), - }, - }; - - Bounds { origin, size } - } -} - -impl Bounds -where - T: Sub + Half + Clone + Debug + Default + PartialEq, -{ - /// Creates a new bounds centered at the given point. - pub fn centered_at(center: Point, size: Size) -> Self { - let origin = Point { - x: center.x - size.width.half(), - y: center.y - size.height.half(), - }; - Self::new(origin, size) - } -} - -impl Bounds -where - T: Add + Half + Clone + Debug + Default + PartialEq, -{ - /// Returns the top center point of the bounds. - pub fn top_center(&self) -> Point { - Point { - x: self.origin.x.clone() + self.size.width.half(), - y: self.origin.y.clone(), - } - } - - /// Returns the bottom center point of the bounds. - pub fn bottom_center(&self) -> Point { - Point { - x: self.origin.x.clone() + self.size.width.half(), - y: self.origin.y.clone() + self.size.height.clone(), - } - } - - /// Returns the left center point of the bounds. - pub fn left_center(&self) -> Point { - Point { - x: self.origin.x.clone(), - y: self.origin.y.clone() + self.size.height.half(), - } - } - - /// Returns the right center point of the bounds. - pub fn right_center(&self) -> Point { - Point { - x: self.origin.x.clone() + self.size.width.clone(), - y: self.origin.y.clone() + self.size.height.half(), - } - } -} - -impl Bounds -where - T: PartialOrd + Add + Clone + Debug + Default + PartialEq, -{ - /// 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 gpui::{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) -> bool { - let my_lower_right = self.bottom_right(); - let their_lower_right = other.bottom_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 - } -} - -impl Bounds -where - T: Add + Half + Clone + Debug + Default + PartialEq, -{ - /// 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` where `T` is the type of the - /// coordinate system. - /// - /// # Returns - /// - /// A `Point` representing the center of the bounds. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{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 { - Point { - x: self.origin.x.clone() + self.size.width.clone().half(), - y: self.origin.y.clone() + self.size.height.clone().half(), - } - } -} - -impl Bounds -where - T: Add + Clone + Debug + Default + PartialEq, -{ - /// Calculates the half perimeter of a rectangle defined by the bounds. - /// - /// The half perimeter is calculated as the sum of the width and the height of the rectangle. - /// This method is generic over the type `T` which must implement the `Sub` trait to allow - /// calculation of the width and height from the bounds' origin and size, as well as the `Add` trait - /// to sum the width and height for the half perimeter. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point, Size}; - /// let bounds = Bounds { - /// origin: Point { x: 0, y: 0 }, - /// size: Size { width: 10, height: 20 }, - /// }; - /// let half_perimeter = bounds.half_perimeter(); - /// assert_eq!(half_perimeter, 30); - /// ``` - pub fn half_perimeter(&self) -> T { - self.size.width.clone() + self.size.height.clone() - } -} - -impl Bounds -where - T: Add + Sub + Clone + Debug + Default + PartialEq, -{ - /// 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 gpui::{Bounds, Point, Size}; - /// let mut bounds = Bounds { - /// origin: Point { x: 10, y: 10 }, - /// size: Size { width: 10, height: 10 }, - /// }; - /// let expanded_bounds = bounds.dilate(5); - /// assert_eq!(expanded_bounds, Bounds { - /// origin: Point { x: 5, y: 5 }, - /// size: Size { width: 20, height: 20 }, - /// }); - /// ``` - #[must_use] - pub fn dilate(&self, amount: T) -> Bounds { - let double_amount = amount.clone() + amount.clone(); - Bounds { - origin: self.origin.clone() - point(amount.clone(), amount), - size: self.size.clone() + size(double_amount.clone(), double_amount), - } - } - - /// Extends the bounds different amounts in each direction. - #[must_use] - pub fn extend(&self, amount: Edges) -> Bounds { - Bounds { - origin: self.origin.clone() - point(amount.left.clone(), amount.top.clone()), - size: self.size.clone() - + size( - amount.left.clone() + amount.right.clone(), - amount.top.clone() + amount.bottom, - ), - } - } -} - -impl Bounds -where - T: Add - + Sub - + Neg - + Clone - + Debug - + Default - + PartialEq, -{ - /// Inset the bounds by a specified amount. Equivalent to `dilate` with the amount negated. - /// - /// Note that this may panic if T does not support negative values. - pub fn inset(&self, amount: T) -> Self { - self.dilate(-amount) - } -} - -impl + Sub + Clone + Debug + Default + PartialEq> - Bounds -{ - /// 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 gpui::{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 bottom_right = self - .bottom_right() - .min(&other.bottom_right()) - .max(&upper_left); - Self::from_corners(upper_left, bottom_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 gpui::{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.bottom_right().max(&other.bottom_right()); - Bounds::from_corners(top_left, bottom_right) - } -} - -impl Bounds -where - T: Add + Sub + Clone + Debug + Default + PartialEq, -{ - /// Computes the space available within outer bounds. - pub fn space_within(&self, outer: &Self) -> Edges { - Edges { - top: self.top() - outer.top(), - right: outer.right() - self.right(), - bottom: outer.bottom() - self.bottom(), - left: self.left() - outer.left(), - } - } -} - -impl Mul for Bounds -where - T: Mul + Clone + Debug + Default + PartialEq, - Point: Mul>, - Rhs: Clone + Debug + Default + PartialEq, -{ - type Output = Bounds; - - fn mul(self, rhs: Rhs) -> Self::Output { - Bounds { - origin: self.origin * rhs.clone(), - size: self.size * rhs, - } - } -} - -impl MulAssign for Bounds -where - T: Mul + Clone + Debug + Default + PartialEq, - S: Clone, -{ - fn mul_assign(&mut self, rhs: S) { - self.origin *= rhs.clone(); - self.size *= rhs; - } -} - -impl Div for Bounds -where - Size: Div>, - T: Div + Clone + Debug + Default + PartialEq, - S: Clone, -{ - type Output = Self; - - fn div(self, rhs: S) -> Self { - Self { - origin: self.origin / rhs.clone(), - size: self.size / rhs, - } - } -} - -impl Add> for Bounds -where - T: Add + Clone + Debug + Default + PartialEq, -{ - type Output = Self; - - fn add(self, rhs: Point) -> Self { - Self { - origin: self.origin + rhs, - size: self.size, - } - } -} - -impl Sub> for Bounds -where - T: Sub + Clone + Debug + Default + PartialEq, -{ - type Output = Self; - - fn sub(self, rhs: Point) -> Self { - Self { - origin: self.origin - rhs, - size: self.size, - } - } -} - -impl From> for Point { - fn from(size: Size) -> Self { - Self { - x: size.width, - y: size.height, - } - } -} - -impl Bounds -where - T: Add + Clone + Debug + Default + PartialEq, -{ - /// 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 top right corner point of the bounds. - /// - /// # Returns - /// - /// A `Point` representing the top right corner of the bounds. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point, Size}; - /// let bounds = Bounds { - /// origin: Point { x: 0, y: 0 }, - /// size: Size { width: 10, height: 20 }, - /// }; - /// let top_right = bounds.top_right(); - /// assert_eq!(top_right, Point { x: 10, y: 0 }); - /// ``` - pub fn top_right(&self) -> Point { - Point { - x: self.origin.x.clone() + self.size.width.clone(), - y: self.origin.y.clone(), - } - } - - /// Returns the bottom right corner point of the bounds. - /// - /// # Returns - /// - /// A `Point` representing the bottom right corner of the bounds. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point, Size}; - /// let bounds = Bounds { - /// origin: Point { x: 0, y: 0 }, - /// size: Size { width: 10, height: 20 }, - /// }; - /// let bottom_right = bounds.bottom_right(); - /// assert_eq!(bottom_right, Point { x: 10, y: 20 }); - /// ``` - pub fn bottom_right(&self) -> Point { - Point { - x: self.origin.x.clone() + self.size.width.clone(), - y: self.origin.y.clone() + self.size.height.clone(), - } - } - - /// Returns the bottom left corner point of the bounds. - /// - /// # Returns - /// - /// A `Point` representing the bottom left corner of the bounds. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point, Size}; - /// let bounds = Bounds { - /// origin: Point { x: 0, y: 0 }, - /// size: Size { width: 10, height: 20 }, - /// }; - /// let bottom_left = bounds.bottom_left(); - /// assert_eq!(bottom_left, Point { x: 0, y: 20 }); - /// ``` - pub fn bottom_left(&self) -> Point { - Point { - x: self.origin.x.clone(), - y: self.origin.y.clone() + self.size.height.clone(), - } - } -} - -impl Bounds -where - T: Add + Half + Clone + Debug + Default + PartialEq, -{ - /// Returns the requested corner point of the bounds. - /// - /// # Returns - /// - /// A `Point` representing the corner of the bounds requested by the parameter. - /// - /// # Examples - /// - /// ``` - /// use gpui::{Bounds, Anchor, Point, Size}; - /// let bounds = Bounds { - /// origin: Point { x: 0, y: 0 }, - /// size: Size { width: 10, height: 20 }, - /// }; - /// let bottom_left = bounds.corner(Anchor::BottomLeft); - /// assert_eq!(bottom_left, Point { x: 0, y: 20 }); - /// ``` - pub fn corner(&self, corner: Anchor) -> Point { - match corner { - Anchor::TopLeft => self.origin.clone(), - Anchor::TopRight => self.top_right(), - Anchor::BottomLeft => self.bottom_left(), - Anchor::BottomRight => self.bottom_right(), - Anchor::TopCenter => self.top_center(), - Anchor::BottomCenter => self.bottom_center(), - Anchor::LeftCenter => self.left_center(), - Anchor::RightCenter => self.right_center(), - } - } -} - -impl Bounds -where - T: Add + PartialOrd + Clone + Debug + Default + PartialEq, -{ - /// 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` that represents the point to check. - /// - /// # Returns - /// - /// Returns `true` if the point is within the bounds, `false` otherwise. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Point, Bounds, Size}; - /// 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(&inside_point)); - /// assert!(!bounds.contains(&outside_point)); - /// ``` - pub fn contains(&self, point: &Point) -> 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() - } - - /// Checks if this bounds is completely contained within another bounds. - /// - /// This method determines whether the current bounds is entirely enclosed by the given bounds. - /// A bounds is considered to be contained within another if its origin (top-left corner) and - /// its bottom-right corner are both contained within the other bounds. - /// - /// # Arguments - /// - /// * `other` - A reference to another `Bounds` that might contain this bounds. - /// - /// # Returns - /// - /// Returns `true` if this bounds is completely inside the other bounds, `false` otherwise. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point, Size}; - /// let outer_bounds = Bounds { - /// origin: Point { x: 0, y: 0 }, - /// size: Size { width: 20, height: 20 }, - /// }; - /// let inner_bounds = Bounds { - /// origin: Point { x: 5, y: 5 }, - /// size: Size { width: 10, height: 10 }, - /// }; - /// let overlapping_bounds = Bounds { - /// origin: Point { x: 15, y: 15 }, - /// size: Size { width: 10, height: 10 }, - /// }; - /// - /// assert!(inner_bounds.is_contained_within(&outer_bounds)); - /// assert!(!overlapping_bounds.is_contained_within(&outer_bounds)); - /// ``` - pub fn is_contained_within(&self, other: &Self) -> bool { - other.contains(&self.origin) && other.contains(&self.bottom_right()) - } - - /// Applies a function to the origin and size of the bounds, producing a new `Bounds`. - /// - /// This method allows for converting a `Bounds` to a `Bounds` 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` with the origin and size mapped by the provided function. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{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(&self, f: impl Fn(T) -> U) -> Bounds - where - U: Clone + Debug + Default + PartialEq, - { - Bounds { - origin: self.origin.map(&f), - size: self.size.map(f), - } - } - - /// Applies a function to the origin of the bounds, producing a new `Bounds` with the new origin - /// - /// # Examples - /// - /// ``` - /// # use gpui::{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_origin(|value| value * 1.5); - /// - /// assert_eq!(new_bounds, Bounds { - /// origin: Point { x: 15.0, y: 15.0 }, - /// size: Size { width: 10.0, height: 20.0 }, - /// }); - /// ``` - pub fn map_origin(self, f: impl Fn(T) -> T) -> Bounds { - Bounds { - origin: self.origin.map(f), - size: self.size, - } - } - - /// Applies a function to the origin of the bounds, producing a new `Bounds` with the new origin - /// - /// # Examples - /// - /// ``` - /// # use gpui::{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_size(|value| value * 1.5); - /// - /// assert_eq!(new_bounds, Bounds { - /// origin: Point { x: 10.0, y: 10.0 }, - /// size: Size { width: 15.0, height: 30.0 }, - /// }); - /// ``` - pub fn map_size(self, f: impl Fn(T) -> T) -> Bounds { - Bounds { - origin: self.origin, - size: self.size.map(f), - } - } -} - -impl Bounds -where - T: Add + Sub + PartialOrd + Clone + Debug + Default + PartialEq, -{ - /// Convert a point to the coordinate space defined by this Bounds - pub fn localize(&self, point: &Point) -> Option> { - self.contains(point) - .then(|| point.relative_to(&self.origin)) - } -} - -/// Checks if the bounds represent an empty area. -/// -/// # Returns -/// -/// Returns `true` if either the width or the height of the bounds is less than or equal to zero, indicating an empty area. -impl Bounds { - /// Checks if the bounds represent an empty area. - /// - /// # Returns - /// - /// Returns `true` if either the width or the height of the bounds is less than or equal to zero, indicating an empty area. - #[must_use] - pub fn is_empty(&self) -> bool { - self.size.width <= T::default() || self.size.height <= T::default() - } -} - -impl> Display for Bounds { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{} - {} (size {})", - self.origin, - self.bottom_right(), - self.size - ) - } -} - -impl Size { - /// Converts the size from physical to logical pixels. - pub fn to_pixels(self, scale_factor: f32) -> Size { - size( - px(self.width.0 as f32 / scale_factor), - px(self.height.0 as f32 / scale_factor), - ) - } -} - -impl Size { - /// Converts the size from logical to physical pixels. - pub fn to_device_pixels(self, scale_factor: f32) -> Size { - size( - DevicePixels((self.width.0 * scale_factor).round() as i32), - DevicePixels((self.height.0 * scale_factor).round() as i32), - ) - } -} - -impl Bounds { - /// 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` 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` that represents the scaled bounds. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point, Size, Pixels, ScaledPixels, DevicePixels}; - /// let bounds = Bounds { - /// origin: Point { x: Pixels::from(10.0), y: Pixels::from(20.0) }, - /// size: Size { width: Pixels::from(30.0), height: Pixels::from(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::from(20.0), - /// y: ScaledPixels::from(40.0), - /// }, - /// size: Size { - /// width: ScaledPixels::from(60.0), - /// height: ScaledPixels::from(80.0) - /// }, - /// }); - /// ``` - pub fn scale(&self, factor: f32) -> Bounds { - Bounds { - origin: self.origin.scale(factor), - size: self.size.scale(factor), - } - } - - /// Convert the bounds from logical pixels to physical pixels - pub fn to_device_pixels(self, factor: f32) -> Bounds { - Bounds { - origin: point( - DevicePixels((self.origin.x.0 * factor).round() as i32), - DevicePixels((self.origin.y.0 * factor).round() as i32), - ), - size: self.size.to_device_pixels(factor), - } - } -} - -impl Bounds { - /// Convert the bounds from physical pixels to logical pixels - pub fn to_pixels(self, scale_factor: f32) -> Bounds { - Bounds { - origin: point( - px(self.origin.x.0 as f32 / scale_factor), - px(self.origin.y.0 as f32 / scale_factor), - ), - size: self.size.to_pixels(scale_factor), - } - } -} - -/// 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 gpui::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, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub struct Edges { - /// The size of the top edge. - pub top: T, - /// The size of the right edge. - pub right: T, - /// The size of the bottom edge. - pub bottom: T, - /// The size of the left edge. - pub left: T, -} - -impl Mul for Edges -where - T: Mul + Clone + Debug + Default + PartialEq, -{ - 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 * rhs.left, - } - } -} - -impl MulAssign for Edges -where - T: Mul + Clone + Debug + Default + PartialEq, - 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 Copy for Edges {} - -impl Edges { - /// 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 gpui::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`. - /// - /// This method allows for converting an `Edges` to an `Edges` 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` with each field mapped by the provided function. - /// - /// # Examples - /// - /// ``` - /// # use gpui::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(&self, f: impl Fn(&T) -> U) -> Edges - where - U: Clone + Debug + Default + PartialEq, - { - 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 gpui::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 bool>(&self, predicate: F) -> bool { - predicate(&self.top) - || predicate(&self.right) - || predicate(&self.bottom) - || predicate(&self.left) - } -} - -impl Edges { - /// 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` with all edges set to `Length::Auto`. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Edges, Length}; - /// 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` with all edges set to zero length. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{DefiniteLength, Edges, Length, Pixels}; - /// let no_edges = Edges::::zero(); - /// assert_eq!(no_edges.top, Length::Definite(DefiniteLength::from(Pixels::ZERO))); - /// assert_eq!(no_edges.right, Length::Definite(DefiniteLength::from(Pixels::ZERO))); - /// assert_eq!(no_edges.bottom, Length::Definite(DefiniteLength::from(Pixels::ZERO))); - /// assert_eq!(no_edges.left, Length::Definite(DefiniteLength::from(Pixels::ZERO))); - /// ``` - pub fn zero() -> Self { - Self { - top: px(0.).into(), - right: px(0.).into(), - bottom: px(0.).into(), - left: px(0.).into(), - } - } -} - -impl Edges { - /// 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` with all edges set to zero length. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{px, DefiniteLength, Edges}; - /// let no_edges = Edges::::zero(); - /// assert_eq!(no_edges.top, DefiniteLength::from(px(0.))); - /// assert_eq!(no_edges.right, DefiniteLength::from(px(0.))); - /// assert_eq!(no_edges.bottom, DefiniteLength::from(px(0.))); - /// assert_eq!(no_edges.left, DefiniteLength::from(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` representing the size of the parent element. - /// * `rem_size` - `Pixels` representing the size of one REM unit. - /// - /// # Returns - /// - /// Returns an `Edges` representing the edges with lengths converted to pixels. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Edges, DefiniteLength, px, AbsoluteLength, rems, 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, rem_size: Pixels) -> Edges { - 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 { - /// 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` with all edges set to zero length. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{AbsoluteLength, Edges, Pixels}; - /// let no_edges = Edges::::zero(); - /// assert_eq!(no_edges.top, AbsoluteLength::Pixels(Pixels::ZERO)); - /// assert_eq!(no_edges.right, AbsoluteLength::Pixels(Pixels::ZERO)); - /// assert_eq!(no_edges.bottom, AbsoluteLength::Pixels(Pixels::ZERO)); - /// assert_eq!(no_edges.left, AbsoluteLength::Pixels(Pixels::ZERO)); - /// ``` - 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` representing the edges with lengths converted to pixels. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Edges, AbsoluteLength, Pixels, px, rems}; - /// 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 { - 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 { - /// Scales the `Edges` by a given factor, returning `Edges`. - /// - /// 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` where each edge is the result of scaling the original edge by the given factor. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Edges, Pixels, ScaledPixels}; - /// let edges = Edges { - /// top: Pixels::from(10.0), - /// right: Pixels::from(20.0), - /// bottom: Pixels::from(30.0), - /// left: Pixels::from(40.0), - /// }; - /// let scaled_edges = edges.scale(2.0); - /// assert_eq!(scaled_edges.top, ScaledPixels::from(20.0)); - /// assert_eq!(scaled_edges.right, ScaledPixels::from(40.0)); - /// assert_eq!(scaled_edges.bottom, ScaledPixels::from(60.0)); - /// assert_eq!(scaled_edges.left, ScaledPixels::from(80.0)); - /// ``` - pub fn scale(&self, factor: f32) -> Edges { - 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 for Edges { - fn from(val: f32) -> Self { - let val: Pixels = val.into(); - val.into() - } -} - -impl From for Edges { - fn from(val: Pixels) -> Self { - Edges { - top: val, - right: val, - bottom: val, - left: val, - } - } -} - -/// Identifies a reference point on a 2D box, used to anchor positioned elements. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Anchor { - /// The top left corner - TopLeft, - /// The top right corner - TopRight, - /// The bottom left corner - BottomLeft, - /// The bottom right corner - BottomRight, - /// The top center position - TopCenter, - /// The bottom center position - BottomCenter, - /// The left center position - LeftCenter, - /// The right center position - RightCenter, -} - -impl Anchor { - /// Returns the directly opposite corner. - /// - /// # Examples - /// - /// ``` - /// # use gpui::Anchor; - /// assert_eq!(Anchor::TopLeft.opposite(), Anchor::BottomRight); - /// ``` - #[must_use] - pub fn opposite(self) -> Self { - match self { - Anchor::TopLeft => Anchor::BottomRight, - Anchor::TopRight => Anchor::BottomLeft, - Anchor::BottomLeft => Anchor::TopRight, - Anchor::BottomRight => Anchor::TopLeft, - Anchor::TopCenter => Anchor::BottomCenter, - Anchor::BottomCenter => Anchor::TopCenter, - Anchor::LeftCenter => Anchor::RightCenter, - Anchor::RightCenter => Anchor::LeftCenter, - } - } - - /// Returns the corner across from this corner, moving along the specified axis. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Axis, Anchor}; - /// let result = Anchor::TopLeft.other_side_along(Axis::Horizontal); - /// assert_eq!(result, Anchor::TopRight); - /// ``` - #[must_use] - pub fn other_side_along(self, axis: Axis) -> Self { - match axis { - Axis::Vertical => match self { - Anchor::TopLeft => Anchor::BottomLeft, - Anchor::TopRight => Anchor::BottomRight, - Anchor::BottomLeft => Anchor::TopLeft, - Anchor::BottomRight => Anchor::TopRight, - Anchor::TopCenter => Anchor::BottomCenter, - Anchor::BottomCenter => Anchor::TopCenter, - Anchor::LeftCenter => Anchor::LeftCenter, - Anchor::RightCenter => Anchor::RightCenter, - }, - Axis::Horizontal => match self { - Anchor::TopLeft => Anchor::TopRight, - Anchor::TopRight => Anchor::TopLeft, - Anchor::BottomLeft => Anchor::BottomRight, - Anchor::BottomRight => Anchor::BottomLeft, - Anchor::TopCenter => Anchor::TopCenter, - Anchor::BottomCenter => Anchor::BottomCenter, - Anchor::LeftCenter => Anchor::RightCenter, - Anchor::RightCenter => Anchor::LeftCenter, - }, - } - } - - /// Returns true if at the center. - #[inline] - pub fn is_center(&self) -> bool { - matches!( - self, - Self::TopCenter | Self::BottomCenter | Self::LeftCenter | Self::RightCenter - ) - } -} - -/// 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, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub struct Corners { - /// The value associated with the top left corner. - pub top_left: T, - /// The value associated with the top right corner. - pub top_right: T, - /// The value associated with the bottom right corner. - pub bottom_right: T, - /// The value associated with the bottom left corner. - pub bottom_left: T, -} - -impl Corners -where - T: Add + Half + Clone + Debug + Default + PartialEq, -{ - /// 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 gpui::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, - } - } - - /// Returns the requested corner value, supporting all eight corner positions. - /// - /// For the four basic corners (TopLeft, TopRight, BottomLeft, BottomRight), - /// this returns the corresponding field value directly. - /// - /// For the center positions (TopCenter, BottomCenter, LeftCenter, RightCenter), - /// this calculates the average of the two adjacent corners. - /// - /// # Returns - /// - /// A value of type `T` representing the corner requested by the parameter. - /// - /// # Examples - /// - /// Basic corner positions: - /// - /// ``` - /// # use gpui::{Anchor, Corners}; - /// let corners = Corners { - /// top_left: 10, - /// top_right: 20, - /// bottom_left: 30, - /// bottom_right: 40 - /// }; - /// assert_eq!(corners.corner(Anchor::TopLeft), 10); - /// assert_eq!(corners.corner(Anchor::BottomRight), 40); - /// ``` - /// - /// Center positions (calculated as average of adjacent corners): - /// - /// ``` - /// # use gpui::{Anchor, Corners}; - /// let corners = Corners { - /// top_left: 10, - /// top_right: 20, - /// bottom_left: 30, - /// bottom_right: 40 - /// }; - /// assert_eq!(corners.corner(Anchor::TopCenter), 15); - /// assert_eq!(corners.corner(Anchor::BottomCenter), 35); - /// assert_eq!(corners.corner(Anchor::LeftCenter), 20); - /// assert_eq!(corners.corner(Anchor::RightCenter), 30); - /// ``` - #[must_use] - pub fn corner(&self, corner: Anchor) -> T { - match corner { - Anchor::TopLeft => self.top_left.clone(), - Anchor::TopRight => self.top_right.clone(), - Anchor::BottomLeft => self.bottom_left.clone(), - Anchor::BottomRight => self.bottom_right.clone(), - Anchor::TopCenter => (self.top_left.clone() + self.top_right.clone()).half(), - Anchor::BottomCenter => (self.bottom_left.clone() + self.bottom_right.clone()).half(), - Anchor::LeftCenter => (self.top_left.clone() + self.bottom_left.clone()).half(), - Anchor::RightCenter => (self.top_right.clone() + self.bottom_right.clone()).half(), - } - } -} - -impl Corners { - /// Converts the `AbsoluteLength` to `Pixels` based on the provided rem size. - /// - /// # Arguments - /// - /// * `rem_size` - The size of one REM unit in pixels, used for conversion if the `AbsoluteLength` is in REMs. - /// - /// # Returns - /// - /// Returns a `Corners` instance with each corner's length converted to pixels. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Corners, AbsoluteLength, Pixels, Rems, Size}; - /// let corners = Corners { - /// top_left: AbsoluteLength::Pixels(Pixels::from(15.0)), - /// top_right: AbsoluteLength::Rems(Rems(1.0)), - /// bottom_right: AbsoluteLength::Pixels(Pixels::from(30.0)), - /// bottom_left: AbsoluteLength::Rems(Rems(2.0)), - /// }; - /// let rem_size = Pixels::from(16.0); - /// let corners_in_pixels = corners.to_pixels(rem_size); - /// - /// assert_eq!(corners_in_pixels.top_left, Pixels::from(15.0)); - /// assert_eq!(corners_in_pixels.top_right, Pixels::from(16.0)); // 1 rem converted to pixels - /// assert_eq!(corners_in_pixels.bottom_right, Pixels::from(30.0)); - /// assert_eq!(corners_in_pixels.bottom_left, Pixels::from(32.0)); // 2 rems converted to pixels - /// ``` - pub fn to_pixels(self, rem_size: Pixels) -> Corners { - Corners { - top_left: self.top_left.to_pixels(rem_size), - top_right: self.top_right.to_pixels(rem_size), - bottom_right: self.bottom_right.to_pixels(rem_size), - bottom_left: self.bottom_left.to_pixels(rem_size), - } - } -} - -impl Corners { - /// Scales the `Corners` by a given factor, returning `Corners`. - /// - /// 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` where each corner is the result of scaling the original corner by the given factor. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Corners, Pixels, ScaledPixels}; - /// let corners = Corners { - /// top_left: Pixels::from(10.0), - /// top_right: Pixels::from(20.0), - /// bottom_right: Pixels::from(30.0), - /// bottom_left: Pixels::from(40.0), - /// }; - /// let scaled_corners = corners.scale(2.0); - /// assert_eq!(scaled_corners.top_left, ScaledPixels::from(20.0)); - /// assert_eq!(scaled_corners.top_right, ScaledPixels::from(40.0)); - /// assert_eq!(scaled_corners.bottom_right, ScaledPixels::from(60.0)); - /// assert_eq!(scaled_corners.bottom_left, ScaledPixels::from(80.0)); - /// ``` - #[must_use] - pub fn scale(&self, factor: f32) -> Corners { - 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. - #[must_use] - pub fn max(&self) -> Pixels { - self.top_left - .max(self.top_right) - .max(self.bottom_right) - .max(self.bottom_left) - } -} - -impl + Ord + Clone + Debug + Default + PartialEq> Corners { - /// Clamps corner radii to be less than or equal to half the shortest side of a quad. - /// - /// # Arguments - /// - /// * `size` - The size of the quad which limits the size of the corner radii. - /// - /// # Returns - /// - /// Anchor radii values clamped to fit. - #[must_use] - pub fn clamp_radii_for_quad_size(self, size: Size) -> Corners { - let max = cmp::min(size.width, size.height) / 2.; - Corners { - top_left: cmp::min(self.top_left, max.clone()), - top_right: cmp::min(self.top_right, max.clone()), - bottom_right: cmp::min(self.bottom_right, max.clone()), - bottom_left: cmp::min(self.bottom_left, max), - } - } -} - -impl Corners { - /// Applies a function to each field of the `Corners`, producing a new `Corners`. - /// - /// This method allows for converting a `Corners` to a `Corners` 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` with each field mapped by the provided function. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Corners, Pixels, Rems}; - /// let corners = Corners { - /// top_left: Pixels::from(10.0), - /// top_right: Pixels::from(20.0), - /// bottom_right: Pixels::from(30.0), - /// bottom_left: Pixels::from(40.0), - /// }; - /// let corners_in_rems = corners.map(|&px| Rems(f32::from(px) / 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), - /// }); - /// ``` - #[must_use] - pub fn map(&self, f: impl Fn(&T) -> U) -> Corners - where - U: Clone + Debug + Default + PartialEq, - { - 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 Mul for Corners -where - T: Mul + Clone + Debug + Default + PartialEq, -{ - 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 * rhs.bottom_left, - } - } -} - -impl MulAssign for Corners -where - T: Mul + Clone + Debug + Default + PartialEq, - 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 Copy for Corners where T: Copy + Clone + Debug + Default + PartialEq {} - -impl From for Corners { - fn from(val: f32) -> Self { - Corners { - top_left: val.into(), - top_right: val.into(), - bottom_right: val.into(), - bottom_left: val.into(), - } - } -} - -impl From for Corners { - fn from(val: Pixels) -> Self { - Corners { - top_left: val, - top_right: val, - bottom_right: val, - bottom_left: val, - } - } -} - -/// Represents an angle in Radians -#[derive( - Clone, - Copy, - Default, - Add, - AddAssign, - Sub, - SubAssign, - Neg, - Div, - DivAssign, - PartialEq, - Serialize, - Deserialize, - Debug, -)] -#[repr(transparent)] -pub struct Radians(pub f32); - -/// Create a `Radian` from a raw value -pub fn radians(value: f32) -> Radians { - Radians(value) -} - -/// A type representing a percentage value. -#[derive( - Clone, - Copy, - Default, - Add, - AddAssign, - Sub, - SubAssign, - Neg, - Div, - DivAssign, - PartialEq, - Serialize, - Deserialize, - Debug, -)] -#[repr(transparent)] -pub struct Percentage(pub f32); - -/// Generate a `Radian` from a percentage of a full circle. -pub fn percentage(value: f32) -> Percentage { - debug_assert!( - (0.0..=1.0).contains(&value), - "Percentage must be between 0 and 1" - ); - Percentage(value) -} - -impl From for Radians { - fn from(value: Percentage) -> Self { - radians(value.0 * std::f32::consts::PI * 2.0) - } -} - -/// 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 gpui::{Pixels, ScaledPixels}; -/// -/// // Define a length of 10 pixels -/// let length = Pixels::from(10.0); -/// -/// // Define a length and scale it by a factor of 2 -/// let scaled_length = length.scale(2.0); -/// assert_eq!(scaled_length, ScaledPixels::from(20.0)); -/// ``` -#[derive( - Clone, - Copy, - Default, - Add, - AddAssign, - Sub, - SubAssign, - Neg, - Div, - DivAssign, - PartialEq, - Serialize, - Deserialize, - JsonSchema, -)] -#[repr(transparent)] -pub struct Pixels(pub(crate) f32); - -impl 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 for Pixels { - type Output = Self; - - fn mul(self, rhs: f32) -> Self { - Self(self.0 * rhs) - } -} - -impl Mul for f32 { - type Output = Pixels; - - fn mul(self, rhs: Pixels) -> Self::Output { - rhs * self - } -} - -impl Mul for Pixels { - type Output = Self; - - fn mul(self, rhs: usize) -> Self { - self * (rhs as f32) - } -} - -impl Mul for usize { - type Output = Pixels; - - fn mul(self, rhs: Pixels) -> Pixels { - rhs * self - } -} - -impl MulAssign for Pixels { - fn mul_assign(&mut self, rhs: f32) { - self.0 *= rhs; - } -} - -impl Display for Pixels { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}px", self.0) - } -} - -impl Debug for Pixels { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(self, f) - } -} - -impl std::iter::Sum for Pixels { - fn sum>(iter: I) -> Self { - iter.fold(Self::ZERO, |a, b| a + b) - } -} - -impl<'a> std::iter::Sum<&'a Pixels> for Pixels { - fn sum>(iter: I) -> Self { - iter.fold(Self::ZERO, |a, b| a + *b) - } -} - -impl TryFrom<&'_ str> for Pixels { - type Error = anyhow::Error; - - fn try_from(value: &'_ str) -> Result { - value - .strip_suffix("px") - .context("expected 'px' suffix") - .and_then(|number| Ok(number.parse()?)) - .map(Self) - } -} - -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); - /// The minimum value that can be represented by `Pixels`. - pub const MIN: Pixels = Pixels(f32::MIN); - - /// Returns the raw `f32` value of this `Pixels`. - pub fn as_f32(self) -> f32 { - self.0 - } - - /// 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. - #[must_use] - 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()) - } - - /// Returns the sign of the `Pixels` value. - /// - /// # Returns - /// - /// Returns: - /// * `1.0` if the value is positive - /// * `-1.0` if the value is negative - pub fn signum(&self) -> f32 { - self.0.signum() - } - - /// Returns the f64 value of `Pixels`. - /// - /// # Returns - /// - /// A f64 value of the `Pixels`. - pub fn to_f64(self) -> f64 { - self.0 as f64 - } -} - -impl Eq for Pixels {} - -impl PartialOrd for Pixels { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Pixels { - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.0.total_cmp(&other.0) - } -} - -impl std::hash::Hash for Pixels { - fn hash(&self, state: &mut H) { - self.0.to_bits().hash(state); - } -} - -impl From for Pixels { - fn from(pixels: f64) -> Self { - Pixels(pixels as f32) - } -} - -impl From for Pixels { - fn from(pixels: f32) -> Self { - Pixels(pixels) - } -} - -impl From for f32 { - fn from(pixels: Pixels) -> Self { - pixels.0 - } -} - -impl From<&Pixels> for f32 { - fn from(pixels: &Pixels) -> Self { - pixels.0 - } -} - -impl From for f64 { - fn from(pixels: Pixels) -> Self { - pixels.0 as f64 - } -} - -impl From for u32 { - fn from(pixels: Pixels) -> Self { - pixels.0 as u32 - } -} - -impl From<&Pixels> for u32 { - fn from(pixels: &Pixels) -> Self { - pixels.0 as u32 - } -} - -impl From for Pixels { - fn from(pixels: u32) -> Self { - Pixels(pixels as f32) - } -} - -impl From for usize { - fn from(pixels: Pixels) -> Self { - pixels.0 as usize - } -} - -impl From 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, - Serialize, - Deserialize, -)] -#[repr(transparent)] -pub struct DevicePixels(pub 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 gpui::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 for i32 { - fn from(device_pixels: DevicePixels) -> Self { - device_pixels.0 - } -} - -impl From for DevicePixels { - fn from(device_pixels: i32) -> Self { - DevicePixels(device_pixels) - } -} - -impl From for DevicePixels { - fn from(device_pixels: u32) -> Self { - DevicePixels(device_pixels as i32) - } -} - -impl From for u32 { - fn from(device_pixels: DevicePixels) -> Self { - device_pixels.0 as u32 - } -} - -impl From for u64 { - fn from(device_pixels: DevicePixels) -> Self { - device_pixels.0 as u64 - } -} - -impl From for DevicePixels { - fn from(device_pixels: u64) -> Self { - DevicePixels(device_pixels as i32) - } -} - -impl From for usize { - fn from(device_pixels: DevicePixels) -> Self { - device_pixels.0 as usize - } -} - -impl From 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, DivAssign, PartialEq)] -#[repr(transparent)] -pub struct ScaledPixels(pub f32); - -impl ScaledPixels { - /// Returns the raw `f32` value of this `ScaledPixels`. - pub fn as_f32(self) -> f32 { - self.0 - } - - /// 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 round(&self) -> Self { - Self(self.0.round()) - } - - /// Ceils the `ScaledPixels` value to the nearest whole number. - /// - /// # Returns - /// - /// Returns a new `ScaledPixels` instance with the ceiled value. - pub fn ceil(&self) -> Self { - Self(self.0.ceil()) - } -} - -impl Eq for ScaledPixels {} - -impl PartialOrd for ScaledPixels { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for ScaledPixels { - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.0.total_cmp(&other.0) - } -} - -impl Debug for ScaledPixels { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}px (scaled)", self.0) - } -} - -impl From for DevicePixels { - fn from(scaled: ScaledPixels) -> Self { - DevicePixels(scaled.0.ceil() as i32) - } -} - -impl From for ScaledPixels { - fn from(device: DevicePixels) -> Self { - ScaledPixels(device.0 as f32) - } -} - -impl From for f64 { - fn from(scaled_pixels: ScaledPixels) -> Self { - scaled_pixels.0 as f64 - } -} - -impl From for u32 { - fn from(pixels: ScaledPixels) -> Self { - pixels.0 as u32 - } -} - -impl From for ScaledPixels { - fn from(pixels: f32) -> Self { - Self(pixels) - } -} - -impl Div for ScaledPixels { - type Output = f32; - - fn div(self, rhs: Self) -> Self::Output { - self.0 / rhs.0 - } -} - -impl std::ops::DivAssign for ScaledPixels { - fn div_assign(&mut self, rhs: Self) { - *self = Self(self.0 / rhs.0); - } -} - -impl std::ops::RemAssign for ScaledPixels { - fn rem_assign(&mut self, rhs: Self) { - self.0 %= rhs.0; - } -} - -impl std::ops::Rem for ScaledPixels { - type Output = Self; - - fn rem(self, rhs: Self) -> Self { - Self(self.0 % rhs.0) - } -} - -impl Mul for ScaledPixels { - type Output = Self; - - fn mul(self, rhs: f32) -> Self { - Self(self.0 * rhs) - } -} - -impl Mul for f32 { - type Output = ScaledPixels; - - fn mul(self, rhs: ScaledPixels) -> Self::Output { - rhs * self - } -} - -impl Mul for ScaledPixels { - type Output = Self; - - fn mul(self, rhs: usize) -> Self { - self * (rhs as f32) - } -} - -impl Mul for usize { - type Output = ScaledPixels; - - fn mul(self, rhs: ScaledPixels) -> ScaledPixels { - rhs * self - } -} - -impl MulAssign for ScaledPixels { - fn mul_assign(&mut self, rhs: f32) { - self.0 *= rhs; - } -} - -/// Represents a length in rems, a unit based on the font-size of the window, which can be assigned with [`Window::set_rem_size`][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 `` 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`. -/// -/// [set_rem_size]: crate::Window::set_rem_size -#[derive(Clone, Copy, Default, Add, Sub, Mul, Div, Neg, PartialEq)] -pub struct Rems(pub f32); - -impl Rems { - /// A length of zero. - pub const ZERO: Self = Self(0.0); - /// Convert this Rem value to pixels. - pub fn to_pixels(self, rem_size: Pixels) -> Pixels { - self * rem_size - } - /// Convert from pixels to Rem - pub fn from_pixels(length: Pixels, window: &gpui::Window) -> Self { - Self(length / window.rem_size()) - } -} - -impl Mul for Rems { - type Output = Pixels; - - fn mul(self, other: Pixels) -> Pixels { - Pixels(self.0 * other.0) - } -} - -impl AddAssign for Rems { - fn add_assign(&mut self, rhs: Rems) { - self.0 += rhs.0 - } -} - -impl Display for Rems { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}rem", self.0) - } -} - -impl Debug for Rems { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(self, f) - } -} - -impl TryFrom<&'_ str> for Rems { - type Error = anyhow::Error; - - fn try_from(value: &'_ str) -> Result { - value - .strip_suffix("rem") - .context("expected 'rem' suffix") - .and_then(|number| Ok(number.parse()?)) - .map(Self) - } -} - -/// 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, Neg, PartialEq)] -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 for AbsoluteLength { - fn from(pixels: Pixels) -> Self { - AbsoluteLength::Pixels(pixels) - } -} - -impl From 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 gpui::{AbsoluteLength, Pixels, Rems}; - /// let length_in_pixels = AbsoluteLength::Pixels(Pixels::from(42.0)); - /// let length_in_rems = AbsoluteLength::Rems(Rems(2.0)); - /// let rem_size = Pixels::from(16.0); - /// - /// assert_eq!(length_in_pixels.to_pixels(rem_size), Pixels::from(42.0)); - /// assert_eq!(length_in_rems.to_pixels(rem_size), Pixels::from(32.0)); - /// ``` - pub fn to_pixels(self, rem_size: Pixels) -> Pixels { - match self { - AbsoluteLength::Pixels(pixels) => pixels, - AbsoluteLength::Rems(rems) => rems.to_pixels(rem_size), - } - } - - /// Converts an `AbsoluteLength` to `Rems` based on a given `rem_size`. - /// - /// # Arguments - /// - /// * `rem_size` - The size of one rem in pixels. - /// - /// # Returns - /// - /// Returns the `AbsoluteLength` as `Pixels`. - pub fn to_rems(self, rem_size: Pixels) -> Rems { - match self { - AbsoluteLength::Pixels(pixels) => Rems(pixels.0 / rem_size.0), - AbsoluteLength::Rems(rems) => rems, - } - } -} - -impl Default for AbsoluteLength { - fn default() -> Self { - px(0.).into() - } -} - -impl Display for AbsoluteLength { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Pixels(pixels) => write!(f, "{pixels}"), - Self::Rems(rems) => write!(f, "{rems}"), - } - } -} - -impl Debug for AbsoluteLength { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(self, f) - } -} - -const EXPECTED_ABSOLUTE_LENGTH: &str = "number with 'px' or 'rem' suffix"; - -impl TryFrom<&'_ str> for AbsoluteLength { - type Error = anyhow::Error; - - fn try_from(value: &'_ str) -> Result { - if let Ok(pixels) = value.try_into() { - Ok(Self::Pixels(pixels)) - } else if let Ok(rems) = value.try_into() { - Ok(Self::Rems(rems)) - } else { - Err(anyhow!( - "invalid AbsoluteLength '{value}', expected {EXPECTED_ABSOLUTE_LENGTH}" - )) - } - } -} - -impl JsonSchema for AbsoluteLength { - fn schema_name() -> Cow<'static, str> { - "AbsoluteLength".into() - } - - fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema { - json_schema!({ - "type": "string", - "pattern": r"^-?\d+(\.\d+)?(px|rem)$" - }) - } -} - -impl<'de> Deserialize<'de> for AbsoluteLength { - fn deserialize>(deserializer: D) -> Result { - struct StringVisitor; - - impl de::Visitor<'_> for StringVisitor { - type Value = AbsoluteLength; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{EXPECTED_ABSOLUTE_LENGTH}") - } - - fn visit_str(self, value: &str) -> Result { - AbsoluteLength::try_from(value).map_err(E::custom) - } - } - - deserializer.deserialize_str(StringVisitor) - } -} - -impl Serialize for AbsoluteLength { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{self}")) - } -} - -/// 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, PartialEq)] -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 gpui::{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::from(42.0)); - /// assert_eq!(length_in_rems.to_pixels(base_size, rem_size), Pixels::from(32.0)); - /// assert_eq!(length_as_fraction.to_pixels(base_size, rem_size), Pixels::from(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 { - Display::fmt(self, f) - } -} - -impl Display for DefiniteLength { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - DefiniteLength::Absolute(length) => write!(f, "{length}"), - DefiniteLength::Fraction(fraction) => write!(f, "{}%", (fraction * 100.0) as i32), - } - } -} - -const EXPECTED_DEFINITE_LENGTH: &str = "expected number with 'px', 'rem', or '%' suffix"; - -impl TryFrom<&'_ str> for DefiniteLength { - type Error = anyhow::Error; - - fn try_from(value: &'_ str) -> Result { - if let Some(percentage) = value.strip_suffix('%') { - let fraction: f32 = percentage.parse::().with_context(|| { - format!("invalid DefiniteLength '{value}', expected {EXPECTED_DEFINITE_LENGTH}") - })?; - Ok(DefiniteLength::Fraction(fraction / 100.0)) - } else if let Ok(absolute_length) = value.try_into() { - Ok(DefiniteLength::Absolute(absolute_length)) - } else { - Err(anyhow!( - "invalid DefiniteLength '{value}', expected {EXPECTED_DEFINITE_LENGTH}" - )) - } - } -} - -impl JsonSchema for DefiniteLength { - fn schema_name() -> Cow<'static, str> { - "DefiniteLength".into() - } - - fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema { - json_schema!({ - "type": "string", - "pattern": r"^-?\d+(\.\d+)?(px|rem|%)$" - }) - } -} - -impl<'de> Deserialize<'de> for DefiniteLength { - fn deserialize>(deserializer: D) -> Result { - struct StringVisitor; - - impl de::Visitor<'_> for StringVisitor { - type Value = DefiniteLength; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{EXPECTED_DEFINITE_LENGTH}") - } - - fn visit_str(self, value: &str) -> Result { - DefiniteLength::try_from(value).map_err(E::custom) - } - } - - deserializer.deserialize_str(StringVisitor) - } -} - -impl Serialize for DefiniteLength { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{self}")) - } -} - -impl From for DefiniteLength { - fn from(pixels: Pixels) -> Self { - Self::Absolute(pixels.into()) - } -} - -impl From for DefiniteLength { - fn from(rems: Rems) -> Self { - Self::Absolute(rems.into()) - } -} - -impl From 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, PartialEq)] -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 { - Display::fmt(self, f) - } -} - -impl Display 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"), - } - } -} - -const EXPECTED_LENGTH: &str = "expected 'auto' or number with 'px', 'rem', or '%' suffix"; - -impl TryFrom<&'_ str> for Length { - type Error = anyhow::Error; - - fn try_from(value: &'_ str) -> Result { - if value == "auto" { - Ok(Length::Auto) - } else if let Ok(definite_length) = value.try_into() { - Ok(Length::Definite(definite_length)) - } else { - Err(anyhow!( - "invalid Length '{value}', expected {EXPECTED_LENGTH}" - )) - } - } -} - -impl JsonSchema for Length { - fn schema_name() -> Cow<'static, str> { - "Length".into() - } - - fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema { - json_schema!({ - "type": "string", - "pattern": r"^(auto|-?\d+(\.\d+)?(px|rem|%))$" - }) - } -} - -impl<'de> Deserialize<'de> for Length { - fn deserialize>(deserializer: D) -> Result { - struct StringVisitor; - - impl de::Visitor<'_> for StringVisitor { - type Value = Length; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{EXPECTED_LENGTH}") - } - - fn visit_str(self, value: &str) -> Result { - Length::try_from(value).map_err(E::custom) - } - } - - deserializer.deserialize_str(StringVisitor) - } -} - -impl Serialize for Length { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{self}")) - } -} - -/// 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 const fn relative(fraction: f32) -> DefiniteLength { - DefiniteLength::Fraction(fraction) -} - -/// Returns the Golden Ratio, i.e. `~(1.0 + sqrt(5.0)) / 2.0`. -pub const 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 const 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 const fn auto() -> Length { - Length::Auto -} - -impl From for Length { - fn from(pixels: Pixels) -> Self { - Self::Definite(pixels.into()) - } -} - -impl From for Length { - fn from(rems: Rems) -> Self { - Self::Definite(rems.into()) - } -} - -impl From for Length { - fn from(length: DefiniteLength) -> Self { - Self::Definite(length) - } -} - -impl From 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()) - } -} - -/// A location in a grid layout. -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, JsonSchema, Default)] -pub struct GridLocation { - /// The rows this item uses within the grid. - pub row: Range, - /// The columns this item uses within the grid. - pub column: Range, -} - -/// The placement of an item within a grid layout's column or row. -#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize, JsonSchema, Default)] -pub enum GridPlacement { - /// The grid line index to place this item. - Line(i16), - /// The number of grid lines to span. - Span(u16), - /// Automatically determine the placement, equivalent to Span(1) - #[default] - Auto, -} - -impl From for taffy::GridPlacement { - fn from(placement: GridPlacement) -> Self { - match placement { - GridPlacement::Line(index) => taffy::GridPlacement::from_line_index(index), - GridPlacement::Span(span) => taffy::GridPlacement::from_span(span), - GridPlacement::Auto => taffy::GridPlacement::Auto, - } - } -} - -/// 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 i32 { - fn half(&self) -> Self { - self / 2 - } -} - -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.) - } -} - -/// 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 IsZero for Point { - fn is_zero(&self) -> bool { - self.x.is_zero() && self.y.is_zero() - } -} - -impl IsZero for Size -where - T: IsZero + Clone + Debug + Default + PartialEq, -{ - fn is_zero(&self) -> bool { - self.width.is_zero() || self.height.is_zero() - } -} - -impl IsZero for Bounds { - fn is_zero(&self) -> bool { - self.size.is_zero() - } -} - -impl IsZero for Corners -where - T: IsZero + Clone + Debug + Default + PartialEq, -{ - 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!(bounds1.intersects(&bounds2)); - - // Test Case 2: Non-Intersecting bounds - assert!(!bounds1.intersects(&bounds3)); - - // Test Case 3: Bounds intersecting with themselves - assert!(bounds1.intersects(&bounds1)); - } -} diff --git a/crates/gpui_pre/src/gestures.rs b/crates/gpui_pre/src/gestures.rs deleted file mode 100644 index ec611576..00000000 --- a/crates/gpui_pre/src/gestures.rs +++ /dev/null @@ -1,2211 +0,0 @@ -//! Touch gesture recognition vocabulary. -//! -//! GPUI recognizes gestures from raw [`TouchEvent`](crate::TouchEvent)s in a -//! single, portable arena in gpui core: recognizers compete for in-flight -//! touches, winners claim them, and losers are cancelled. Recognized gestures -//! are surfaced through *existing* semantic events wherever possible, a tap -//! becomes [`ClickEvent::Touch`](crate::ClickEvent), a pan becomes -//! [`ScrollWheelEvent`](crate::ScrollWheelEvent)s carrying a -//! [`TouchPhase`](crate::TouchPhase), and a pinch becomes -//! [`PinchEvent`](crate::PinchEvent)s — so components written against -//! `on_click` and scroll containers work untouched on mobile. - -use std::collections::VecDeque; -use std::mem; -use std::time::Duration; - -use scheduler::Instant; -use smallvec::SmallVec; - -use crate::{ - Axis, GestureEvent, InputEvent, IsZero, Modifiers, MouseButton, MouseDownEvent, MouseEvent, - MouseUpEvent, Pixels, PlatformInput, Point, ScrollDelta, ScrollWheelEvent, TouchEvent, TouchId, - TouchPhase, point, px, seal::Sealed, -}; - -const SCROLL_EVENT_SEPARATION: Duration = Duration::from_millis(28); - -fn dominant_axis(delta: Point) -> Axis { - if delta.x.abs() <= delta.y.abs() { - Axis::Vertical - } else { - Axis::Horizontal - } -} - -fn lock_delta_to_axis(delta: &mut Point, axis: Axis) { - match axis { - Axis::Vertical => delta.x = Pixels::ZERO, - Axis::Horizontal => delta.y = Pixels::ZERO, - } -} - -fn movements_oppose(left: Point, right: Point) -> bool { - f32::from(left.x) * f32::from(right.x) + f32::from(left.y) * f32::from(right.y) < 0. -} - -/// Tracks the dominant axis across the events in a scroll gesture. -#[derive(Clone, Copy, Debug, Default)] -pub struct OngoingScroll { - last_event: Option, - axis: Option, -} - -impl OngoingScroll { - /// Filters the given delta to the dominant axis of the current scroll gesture. - /// - /// Gestures are delimited by their touch phase when available, with a timeout - /// fallback for platforms that only emit [`TouchPhase::Moved`]. - pub fn filter(&mut self, delta: &mut Point, touch_phase: TouchPhase) { - self.filter_at(delta, touch_phase, Instant::now()) - } - - fn filter_at(&mut self, delta: &mut Point, touch_phase: TouchPhase, now: Instant) { - const UNLOCK_PERCENT: f32 = 1.9; - const UNLOCK_LOWER_BOUND: Pixels = px(6.); - - if matches!(touch_phase, TouchPhase::Ended | TouchPhase::Cancelled) { - self.last_event = None; - self.axis = None; - return; - } - - let x = delta.x.abs(); - let y = delta.y.abs(); - if x.is_zero() && y.is_zero() { - if touch_phase == TouchPhase::Started { - self.last_event = None; - self.axis = None; - } - return; - } - - let starts_new_gesture = touch_phase == TouchPhase::Started - || self - .last_event - .is_none_or(|last_event| now.duration_since(last_event) >= SCROLL_EVENT_SEPARATION); - let mut axis = self.axis; - if starts_new_gesture { - axis = Some(dominant_axis(*delta)); - } else if x.max(y) >= UNLOCK_LOWER_BOUND { - match axis { - Some(Axis::Vertical) if x > y && x >= y * UNLOCK_PERCENT => { - axis = None; - } - Some(Axis::Horizontal) if y > x && y >= x * UNLOCK_PERCENT => { - axis = None; - } - _ => {} - } - } - - self.last_event = Some(now); - self.axis = axis; - if let Some(axis) = axis { - lock_delta_to_axis(delta, axis); - } - } -} - -/// Feel constants consumed by gesture recognizers. Provided on a best-effort -/// basis, depending on each platform's support, defaulting to GPUI's own -/// (iOS flavored) values -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct GestureTuning { - /// Distance a touch may travel before it stops being a potential tap and - /// becomes a pan/drag. - pub touch_slop: Pixels, - /// Maximum interval between taps for them to accumulate a tap count. - pub multi_tap_interval: Duration, - /// Maximum distance between taps for them to accumulate a tap count. - pub multi_tap_slop: Pixels, - /// How long a touch must remain within [`Self::touch_slop`] to be - /// recognized as a long press. - pub long_press_duration: Duration, - /// How scroll momentum decelerates after a fling. - pub scroll_physics: ScrollPhysics, - /// Minimum release velocity, in pixels per second, required to start - /// scroll momentum. - pub min_fling_velocity: f32, -} - -impl Default for GestureTuning { - fn default() -> Self { - Self { - touch_slop: px(8.), - multi_tap_interval: Duration::from_millis(400), - multi_tap_slop: px(16.), - long_press_duration: Duration::from_millis(500), - scroll_physics: ScrollPhysics::ios(), - min_fling_velocity: 50., - } - } -} - -/// How free scrolling decelerates after a fling. -/// -/// This models deceleration only. Boundary behavior — bouncing, edge glow, -/// clamping — is the scroll container's policy: the container is the one that -/// knows its extents. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum ScrollPhysics { - /// Exponential velocity decay, the `UIScrollView` model: - /// `velocity(t) = v₀ · decay_per_msᵐˢ`. - Exponential { - /// Per-millisecond velocity decay factor. `UIScrollView`'s normal - /// deceleration rate is `0.998`. - decay_per_ms: f32, - }, - /// The friction spline of Android's `OverScroller`: fling duration and - /// distance follow a logarithmic deceleration law, and progress along - /// the fling follows a cubic-Bezier ease-out curve. Transcribed from - /// AOSP's `SplineOverScroller` (Apache-2.0). - FrictionSpline { - /// The scroll friction coefficient; - /// `ViewConfiguration.getScrollFriction()` is `0.015` on Android. - friction: f32, - /// Pixels per physical inch of the display, in the coordinate space - /// the fling runs in. Android folds display density into its - /// deceleration coefficient, so the same finger speed flings - /// further in pixels on a denser screen. - pixels_per_inch: f32, - }, -} - -impl ScrollPhysics { - /// iOS scroll feel: `UIScrollView`'s normal deceleration rate. - pub fn ios() -> Self { - Self::Exponential { - decay_per_ms: 0.998, - } - } - - /// Android scroll feel: `OverScroller` with stock friction, at Android's - /// nominal density of 160 density-independent pixels per inch — the - /// right pairing when fling distances are in logical pixels. Platforms - /// that fling in physical pixels, or know the display's true density in - /// their logical space, should construct - /// [`ScrollPhysics::FrictionSpline`] directly. - pub fn android() -> Self { - Self::FrictionSpline { - friction: 0.015, - pixels_per_inch: 160., - } - } - - /// How long a fling released at `speed` pixels per second coasts before - /// it stops. - fn fling_duration(self, speed: f32) -> Duration { - match self { - Self::Exponential { decay_per_ms } => { - if speed <= MOMENTUM_STOP_VELOCITY { - return Duration::ZERO; - } - let milliseconds = (MOMENTUM_STOP_VELOCITY / speed).ln() / decay_per_ms.ln(); - Duration::from_secs_f32(milliseconds / 1000.) - } - Self::FrictionSpline { - friction, - pixels_per_inch, - } => { - if speed <= 0. { - return Duration::ZERO; - } - let deceleration = friction_spline::deceleration(speed, friction, pixels_per_inch); - let seconds = (deceleration / (friction_spline::deceleration_rate() - 1.)).exp(); - Duration::from_secs_f64(seconds) - } - } - } - - /// Distance traveled `elapsed` into a fling released at `speed` pixels - /// per second, in pixels along the fling direction. Evaluated in closed - /// form so the trajectory is independent of tick timing. - fn fling_distance(self, speed: f32, elapsed: Duration) -> f32 { - let duration = self.fling_duration(speed); - if duration.is_zero() { - return 0.; - } - let elapsed = elapsed.min(duration); - match self { - Self::Exponential { decay_per_ms } => { - // ∫₀ᵗ v₀·kᵐˢ dms, with speed converted to pixels per - // millisecond. - let milliseconds = elapsed.as_secs_f32() * 1000.; - (speed / 1000.) * (decay_per_ms.powf(milliseconds) - 1.) / decay_per_ms.ln() - } - Self::FrictionSpline { - friction, - pixels_per_inch, - } => { - let deceleration = friction_spline::deceleration(speed, friction, pixels_per_inch); - let rate = friction_spline::deceleration_rate(); - let total_distance = friction as f64 - * friction_spline::physical_coefficient(pixels_per_inch) - * (rate / (rate - 1.) * deceleration).exp(); - let progress = elapsed.as_secs_f64() / duration.as_secs_f64(); - total_distance as f32 * friction_spline::distance_coefficient(progress as f32) - } - } - } -} - -/// The fling model of Android's `OverScroller.SplineOverScroller`, -/// transcribed from AOSP (Apache-2.0). `SPLINE_TIME`, which AOSP uses for -/// programmatic scroll animations rather than flings, is intentionally not -/// transcribed. -mod friction_spline { - use std::sync::LazyLock; - - const NB_SAMPLES: usize = 100; - const INFLEXION: f32 = 0.35; - const START_TENSION: f32 = 0.5; - const END_TENSION: f32 = 1.0; - const P1: f32 = START_TENSION * INFLEXION; - const P2: f32 = 1.0 - END_TENSION * (1.0 - INFLEXION); - - /// Android's `DECELERATION_RATE`: `ln(0.78) / ln(0.9)`. - pub(super) fn deceleration_rate() -> f64 { - 0.78f64.ln() / 0.9f64.ln() - } - - /// `SPLINE_POSITION` from AOSP's static initializer: fractional fling - /// distance sampled at 100 evenly spaced fractions of the fling - /// duration, from a cubic Bezier with control points shaped by - /// `INFLEXION` and the start/end tensions. - static SPLINE_POSITION: LazyLock<[f32; NB_SAMPLES + 1]> = LazyLock::new(|| { - let mut spline_position = [0f32; NB_SAMPLES + 1]; - let mut x_min = 0f32; - for (i, sample) in spline_position.iter_mut().take(NB_SAMPLES).enumerate() { - let alpha = i as f32 / NB_SAMPLES as f32; - let mut x_max = 1f32; - let (x, coefficient) = loop { - let x = x_min + (x_max - x_min) / 2.; - let coefficient = 3. * x * (1. - x); - let time = coefficient * ((1. - x) * P1 + x * P2) + x * x * x; - if (time - alpha).abs() < 1e-5 { - break (x, coefficient); - } - if time > alpha { - x_max = x; - } else { - x_min = x; - } - }; - *sample = coefficient * ((1. - x) * START_TENSION + x) + x * x * x; - } - spline_position[NB_SAMPLES] = 1.; - spline_position - }); - - /// `SensorManager.GRAVITY_EARTH · 39.37 in/m · ppi · 0.84`, AOSP's - /// `mPhysicalCoeff`: gravity expressed in pixels, times an empirical - /// "look and feel" tuning factor. - pub(super) fn physical_coefficient(pixels_per_inch: f32) -> f64 { - 9.80665 * 39.37 * pixels_per_inch as f64 * 0.84 - } - - /// AOSP's `getSplineDeceleration`. - pub(super) fn deceleration(speed: f32, friction: f32, pixels_per_inch: f32) -> f64 { - (INFLEXION as f64 * speed as f64 - / (friction as f64 * physical_coefficient(pixels_per_inch))) - .ln() - } - - /// Fraction of the total fling distance covered at fraction `time` of - /// the fling duration: table lookup plus linear interpolation, as in - /// `SplineOverScroller.update`. - pub(super) fn distance_coefficient(time: f32) -> f32 { - if time >= 1. { - return 1.; - } - let index = ((NB_SAMPLES as f32 * time) as usize).min(NB_SAMPLES - 1); - let time_lower = index as f32 / NB_SAMPLES as f32; - let time_upper = (index + 1) as f32 / NB_SAMPLES as f32; - let distance_lower = SPLINE_POSITION[index]; - let distance_upper = SPLINE_POSITION[index + 1]; - let velocity_coefficient = (distance_upper - distance_lower) / (time_upper - time_lower); - distance_lower + (time - time_lower) * velocity_coefficient - } - - #[cfg(test)] - pub(super) fn bezier_time_and_position(parameter: f32) -> (f32, f32) { - let coefficient = 3. * parameter * (1. - parameter); - let cubed = parameter * parameter * parameter; - ( - coefficient * ((1. - parameter) * P1 + parameter * P2) + cubed, - coefficient * ((1. - parameter) * START_TENSION + parameter) + cubed, - ) - } - - #[cfg(test)] - pub(super) fn spline_position_samples() -> &'static [f32; NB_SAMPLES + 1] { - &SPLINE_POSITION - } -} - -/// The set of gesture kinds that participate in recognition. -/// -/// Used by [`PlatformGestures::native_recognizers`] to declare which gestures -/// the platform recognizes natively rather than leaving to gpui core's -/// portable recognizers. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub struct GestureKinds { - /// Tap (and multi-tap), surfaced as [`ClickEvent::Touch`](crate::ClickEvent). - pub tap: bool, - /// Long press, surfaced as [`LongPressEvent`]. - pub long_press: bool, - /// Pan/scroll (including fling momentum), surfaced as - /// [`ScrollWheelEvent`](crate::ScrollWheelEvent)s. - pub pan: bool, - /// Pinch to zoom, surfaced as [`PinchEvent`](crate::PinchEvent)s. - pub pinch: bool, -} - -impl GestureKinds { - /// No gestures; gpui core's portable recognizers handle everything. - pub const NONE: Self = Self { - tap: false, - long_press: false, - pan: false, - pinch: false, - }; - - /// All gesture kinds. - pub const ALL: Self = Self { - tap: true, - long_press: true, - pan: true, - pinch: true, - }; -} - -/// A direct touch drag claimed by an element before touch input becomes a tap, -/// long press, or scrolling gesture. -#[derive(Clone, Debug)] -pub struct TouchDragEvent { - /// The phase of the touch drag. - pub phase: TouchPhase, - /// The position where the touch started. - pub start_position: Point, - /// The touch's current position. - pub position: Point, -} - -impl Sealed for TouchDragEvent {} -impl InputEvent for TouchDragEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::TouchDrag(self) - } -} -impl GestureEvent for TouchDragEvent {} -impl MouseEvent for TouchDragEvent {} - -/// A phased long-press gesture recognized from a touch. -#[derive(Clone, Debug)] -pub struct LongPressEvent { - /// The phase of the long press. - pub phase: TouchPhase, - /// The position where the touch started. - pub start_position: Point, - /// The touch's current position. - pub position: Point, -} - -impl Default for LongPressEvent { - fn default() -> Self { - Self { - phase: TouchPhase::Started, - start_position: Point::default(), - position: Point::default(), - } - } -} - -impl Sealed for LongPressEvent {} -impl InputEvent for LongPressEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::LongPress(self) - } -} -impl GestureEvent for LongPressEvent {} -impl MouseEvent for LongPressEvent {} - -/// Platform gesture recognition services. -/// -/// If your mobile platform supports native gesture recognition, use this -/// to share it with GPUI. -pub trait PlatformGestures { - /// Feel constants for the portable recognizers on this platform. - fn tuning(&self) -> GestureTuning { - GestureTuning::default() - } - - /// The gesture kinds this platform recognizes natively. - fn native_recognizers(&self) -> GestureKinds { - GestureKinds::NONE - } -} - -/// A no-op [`PlatformGestures`] implementation: no native recognizers and -/// default tuning. Suitable for desktop platforms and tests. -pub struct NullPlatformGestures; - -impl PlatformGestures for NullPlatformGestures {} - -/// Ceiling on recognized fling velocity, in pixels per second (matches -/// Flutter's `kMaxFlingVelocity`). -const MAX_FLING_VELOCITY: f32 = 8000.; - -/// Momentum below this speed, in pixels per second, is imperceptible. The -/// exponential model, which never mathematically stops, treats reaching this -/// speed as the end of the fling. (The friction spline has a finite duration -/// of its own.) -const MOMENTUM_STOP_VELOCITY: f32 = 10.; - -/// How far back the release-velocity estimate looks. Samples older than this -/// reflect an earlier part of the gesture, not the speed at release. -const VELOCITY_WINDOW: Duration = Duration::from_millis(100); - -/// A pause between samples longer than this means the finger stopped: -/// anything before the pause describes an earlier motion, not the release -/// (Flutter's `kAssumePointerMoveStoppedMilliseconds`). Touch hardware -/// reports movement every 8–16ms while the finger is in motion. -const VELOCITY_ASSUME_STOPPED_GAP: Duration = Duration::from_millis(40); - -const VELOCITY_MAX_SAMPLES: usize = 20; - -/// The portable recognizer behind raw touch input: it watches the -/// [`TouchEvent`] stream for one touch at a time and resolves it into either -/// a tap or a pan, following the competition model described in the module -/// docs. Pans continue into post-release momentum when the touch lifts at -/// speed; the window drives that phase through [`Self::tick_momentum`]. -/// -/// Taps are currently surfaced as synthesized mouse presses rather than -/// [`ClickEvent::Touch`](crate::ClickEvent), which keeps every existing -/// mouse-driven behavior (click listeners, caret placement, double-tap -/// selection) working before elements grow a direct tap-delivery path. -/// Pinch recognition is not implemented yet, and additional touches are ignored -/// while one is being recognized. -pub(crate) struct TouchGestureRecognizer { - tuning: GestureTuning, - state: TouchGestureState, - momentum: Option, - last_tap: Option, -} - -/// A semantic event recognized from raw touches, ready to dispatch through -/// the window's existing input paths. -#[derive(Debug)] -pub(crate) enum RecognizedTouchGesture { - /// One step of a pan (or of its post-release momentum), delivered to - /// scroll listeners at the pan's starting position. - Scroll(ScrollWheelEvent), - /// A recognized tap, delivered as a synthesized mouse press and release. - Tap { - down: MouseDownEvent, - up: MouseUpEvent, - }, - TouchDrag(TouchDragEvent), - LongPress(LongPressEvent), -} - -enum TouchGestureState { - Idle, - /// The touch is still within `touch_slop` of where it started: it can - /// still resolve into either a tap or a pan. - Pending { - touch: ActiveTouch, - deadline: Instant, - long_press_offered: bool, - touch_drag_offered: bool, - }, - /// The touch exceeded `touch_slop`: it is a pan until it ends, and its - /// movement flows out as scroll events. - Panning { - touch: ActiveTouch, - axis: Axis, - }, - LongPressing(ActiveTouch), - TouchDragging(ActiveTouch), -} - -struct ActiveTouch { - id: TouchId, - start_position: Point, - /// The latest raw position reported for this touch. - last_position: Point, - /// The position pan output has scrolled to so far. While panning this - /// may run ahead of the raw touch by the event's predicted position; - /// the release event targets the raw position again, so the total - /// scrolled distance always converges to the finger's actual travel. - emitted_position: Point, - /// Retained across stationary samples so prediction corrections cannot - /// reverse a pan when integer browser coordinates repeat. - last_movement: Point, - velocity_tracker: VelocityTracker, -} - -struct CompletedTap { - position: Point, - time: Instant, - count: usize, -} - -/// One fling in progress. The trajectory is a closed-form curve of elapsed -/// time — each tick evaluates it and emits the increment — so the fling is -/// exactly frame-rate independent: a stalled frame simply resumes further -/// along the same curve. -struct Momentum { - /// Where the pan started; synthesized scroll events keep hit-testing - /// there so momentum stays with the container the gesture began on. - position: Point, - /// Unit vector of the release velocity. - direction: Point, - axis: Axis, - /// Release speed in pixels per second. - speed: f32, - started_at: Instant, - duration: Duration, - /// Distance already emitted along `direction`, in pixels. - emitted_distance: f32, -} - -impl TouchGestureRecognizer { - pub(crate) fn new(tuning: GestureTuning) -> Self { - Self { - tuning, - state: TouchGestureState::Idle, - momentum: None, - last_tap: None, - } - } - - pub(crate) fn handle_event( - &mut self, - event: &TouchEvent, - ) -> SmallVec<[RecognizedTouchGesture; 2]> { - self.handle_event_at(event, Instant::now()) - } - - fn handle_event_at( - &mut self, - event: &TouchEvent, - now: Instant, - ) -> SmallVec<[RecognizedTouchGesture; 2]> { - let mut recognized = SmallVec::new(); - match event.phase { - TouchPhase::Started => { - let caught_fling = if let Some(momentum) = self.momentum.take() { - recognized.push(RecognizedTouchGesture::Scroll(scroll_event( - momentum.position, - Point::default(), - TouchPhase::Ended, - ))); - Some(momentum.axis) - } else { - None - }; - if matches!(self.state, TouchGestureState::Idle) { - let mut velocity_tracker = VelocityTracker::default(); - velocity_tracker.push(now, event.position); - let touch = ActiveTouch { - id: event.id, - start_position: event.position, - last_position: event.position, - emitted_position: event.position, - last_movement: Point::default(), - velocity_tracker, - }; - if let Some(axis) = caught_fling { - // A touch that catches a fling is a drag from the - // first pixel: waiting out the slop would freeze the - // content mid-scroll and then jump. It can also never - // be a tap; releasing it just leaves the content - // stopped, as on Android and iOS. - recognized.push(RecognizedTouchGesture::Scroll(scroll_event( - touch.start_position, - Point::default(), - TouchPhase::Started, - ))); - self.state = TouchGestureState::Panning { touch, axis }; - } else { - self.state = TouchGestureState::Pending { - touch, - deadline: now + self.tuning.long_press_duration, - long_press_offered: false, - touch_drag_offered: false, - }; - } - } - } - TouchPhase::Moved => match mem::replace(&mut self.state, TouchGestureState::Idle) { - TouchGestureState::Pending { - mut touch, - deadline, - long_press_offered, - touch_drag_offered, - } if touch.id == event.id => { - touch.velocity_tracker.push(now, event.position); - touch.last_position = event.position; - let accumulated = event.position - touch.start_position; - if accumulated.magnitude() > f64::from(self.tuning.touch_slop) { - // Carry the full movement so far into the first scroll - // step: the content catches up to the finger instead - // of losing the slop distance. - let mut target = event.predicted_position.unwrap_or(event.position); - let axis = dominant_axis(accumulated); - let mut delta = target - touch.start_position; - lock_delta_to_axis(&mut delta, axis); - touch.last_movement = accumulated; - lock_delta_to_axis(&mut touch.last_movement, axis); - if movements_oppose(delta, touch.last_movement) { - target = event.position; - delta = accumulated; - lock_delta_to_axis(&mut delta, axis); - } - touch.emitted_position = target; - recognized.push(RecognizedTouchGesture::Scroll(scroll_event( - touch.start_position, - delta, - TouchPhase::Started, - ))); - self.state = TouchGestureState::Panning { touch, axis }; - } else { - self.state = TouchGestureState::Pending { - touch, - deadline, - long_press_offered, - touch_drag_offered, - }; - } - } - TouchGestureState::Panning { mut touch, axis } if touch.id == event.id => { - let mut raw_delta = event.position - touch.last_position; - lock_delta_to_axis(&mut raw_delta, axis); - if raw_delta != Point::default() { - touch.last_movement = raw_delta; - } - touch.velocity_tracker.push(now, event.position); - touch.last_position = event.position; - let mut target = event.predicted_position.unwrap_or(event.position); - let mut delta = target - touch.emitted_position; - lock_delta_to_axis(&mut delta, axis); - // Prediction error must not reverse content while the raw - // touch still advances. Fall back to the raw position so a - // real finger reversal remains responsive. - if movements_oppose(delta, touch.last_movement) { - target = event.position; - delta = target - touch.emitted_position; - lock_delta_to_axis(&mut delta, axis); - if movements_oppose(delta, touch.last_movement) { - target = touch.emitted_position; - delta = Point::default(); - } - } - touch.emitted_position = target; - recognized.push(RecognizedTouchGesture::Scroll(scroll_event( - touch.start_position, - delta, - TouchPhase::Moved, - ))); - self.state = TouchGestureState::Panning { touch, axis }; - } - TouchGestureState::LongPressing(mut touch) if touch.id == event.id => { - touch.last_position = event.position; - recognized.push(RecognizedTouchGesture::LongPress(LongPressEvent { - phase: TouchPhase::Moved, - start_position: touch.start_position, - position: event.position, - })); - self.state = TouchGestureState::LongPressing(touch); - } - TouchGestureState::TouchDragging(mut touch) if touch.id == event.id => { - touch.last_position = event.position; - recognized.push(RecognizedTouchGesture::TouchDrag(TouchDragEvent { - phase: TouchPhase::Moved, - start_position: touch.start_position, - position: event.position, - })); - self.state = TouchGestureState::TouchDragging(touch); - } - other => self.state = other, - }, - TouchPhase::Ended => match mem::replace(&mut self.state, TouchGestureState::Idle) { - TouchGestureState::Pending { touch, .. } if touch.id == event.id => { - let tap_count = match &self.last_tap { - Some(tap) - if now.duration_since(tap.time) <= self.tuning.multi_tap_interval - && (event.position - tap.position).magnitude() - <= f64::from(self.tuning.multi_tap_slop) => - { - tap.count + 1 - } - _ => 1, - }; - self.last_tap = Some(CompletedTap { - position: event.position, - time: now, - count: tap_count, - }); - recognized.push(RecognizedTouchGesture::Tap { - down: MouseDownEvent { - button: MouseButton::Left, - position: event.position, - modifiers: Modifiers::default(), - click_count: tap_count, - first_mouse: false, - }, - up: MouseUpEvent { - button: MouseButton::Left, - position: event.position, - modifiers: Modifiers::default(), - click_count: tap_count, - }, - }); - } - TouchGestureState::Panning { touch, axis } if touch.id == event.id => { - // The release deliberately contributes no velocity - // sample: it usually repeats the last movement's position - // with a later timestamp, which would dilute the - // estimate. But a release long after the last movement - // means the finger had already stopped, so nothing - // flings. - let finger_stopped = - touch - .velocity_tracker - .latest_sample_time() - .is_none_or(|latest| { - now.duration_since(latest) > VELOCITY_ASSUME_STOPPED_GAP - }); - let mut velocity = if finger_stopped { - Point::default() - } else { - touch.velocity_tracker.velocity() - }; - match axis { - Axis::Vertical => velocity.x = 0., - Axis::Horizontal => velocity.y = 0., - } - let speed = (velocity.x.powi(2) + velocity.y.powi(2)).sqrt(); - let mut release_delta = event.position - touch.emitted_position; - lock_delta_to_axis(&mut release_delta, axis); - if speed >= self.tuning.min_fling_velocity { - let direction = point(velocity.x / speed, velocity.y / speed); - let speed = speed.min(MAX_FLING_VELOCITY); - let duration = self.tuning.scroll_physics.fling_duration(speed); - if !duration.is_zero() { - let total_distance = - self.tuning.scroll_physics.fling_distance(speed, duration); - // Prediction may have left the content ahead of - // the raw release position. Emitting that - // correction here would visibly snap the content - // backwards just as the fling launches, so fold - // it into the fling instead: start the curve - // already advanced by the overshoot, keeping the - // total travel exact while staying monotonic. - let overshoot = -(f32::from(release_delta.x) * direction.x - + f32::from(release_delta.y) * direction.y); - let emitted_distance = if overshoot > 0. && overshoot < total_distance { - release_delta += - point(px(direction.x * overshoot), px(direction.y * overshoot)); - overshoot - } else { - 0. - }; - self.momentum = Some(Momentum { - position: touch.start_position, - direction, - axis, - speed, - started_at: now, - duration, - emitted_distance, - }); - } - } - recognized.push(RecognizedTouchGesture::Scroll(scroll_event( - touch.start_position, - release_delta, - TouchPhase::Ended, - ))); - } - TouchGestureState::LongPressing(touch) if touch.id == event.id => { - recognized.push(RecognizedTouchGesture::LongPress(LongPressEvent { - phase: TouchPhase::Ended, - start_position: touch.start_position, - position: event.position, - })); - } - TouchGestureState::TouchDragging(touch) if touch.id == event.id => { - recognized.push(RecognizedTouchGesture::TouchDrag(TouchDragEvent { - phase: TouchPhase::Ended, - start_position: touch.start_position, - position: event.position, - })); - } - other => self.state = other, - }, - TouchPhase::Cancelled => match mem::replace(&mut self.state, TouchGestureState::Idle) { - TouchGestureState::Pending { touch, .. } if touch.id == event.id => {} - TouchGestureState::Panning { touch, .. } if touch.id == event.id => { - recognized.push(RecognizedTouchGesture::Scroll(scroll_event( - touch.start_position, - Point::default(), - TouchPhase::Cancelled, - ))); - } - TouchGestureState::LongPressing(touch) if touch.id == event.id => { - recognized.push(RecognizedTouchGesture::LongPress(LongPressEvent { - phase: TouchPhase::Cancelled, - start_position: touch.start_position, - position: event.position, - })); - } - TouchGestureState::TouchDragging(touch) if touch.id == event.id => { - recognized.push(RecognizedTouchGesture::TouchDrag(TouchDragEvent { - phase: TouchPhase::Cancelled, - start_position: touch.start_position, - position: event.position, - })); - } - other => self.state = other, - }, - } - recognized - } - - pub(crate) fn pending_long_press(&self) -> Option<(TouchId, Duration)> { - let TouchGestureState::Pending { - touch, - deadline, - long_press_offered: false, - .. - } = &self.state - else { - return None; - }; - Some((touch.id, deadline.saturating_duration_since(Instant::now()))) - } - - pub(crate) fn offer_long_press(&mut self, id: TouchId) -> Option { - let TouchGestureState::Pending { - touch, - long_press_offered, - .. - } = &mut self.state - else { - return None; - }; - if touch.id != id || *long_press_offered { - return None; - } - *long_press_offered = true; - Some(RecognizedTouchGesture::LongPress(LongPressEvent { - phase: TouchPhase::Started, - start_position: touch.start_position, - position: touch.last_position, - })) - } - - pub(crate) fn resolve_long_press(&mut self, claimed: bool) { - if !claimed { - return; - } - let state = mem::replace(&mut self.state, TouchGestureState::Idle); - self.state = match state { - TouchGestureState::Pending { - touch, - long_press_offered: true, - .. - } => TouchGestureState::LongPressing(touch), - other => other, - }; - } - - pub(crate) fn offer_touch_drag(&mut self, id: TouchId) -> Option { - let TouchGestureState::Pending { - touch, - touch_drag_offered, - .. - } = &mut self.state - else { - return None; - }; - if touch.id != id || *touch_drag_offered { - return None; - } - *touch_drag_offered = true; - Some(RecognizedTouchGesture::TouchDrag(TouchDragEvent { - phase: TouchPhase::Started, - start_position: touch.start_position, - position: touch.last_position, - })) - } - - pub(crate) fn resolve_touch_drag(&mut self, claimed: bool) { - if !claimed { - return; - } - let state = mem::replace(&mut self.state, TouchGestureState::Idle); - self.state = match state { - TouchGestureState::Pending { - touch, - touch_drag_offered: true, - .. - } => TouchGestureState::TouchDragging(touch), - other => other, - }; - } - - pub(crate) fn has_momentum(&self) -> bool { - self.momentum.is_some() - } - - /// Advances post-fling momentum by one frame, returning the scroll step - /// to dispatch, or `None` when no momentum is in progress. The final step - /// carries [`TouchPhase::Ended`] to close the synthetic scroll stream. - pub(crate) fn tick_momentum(&mut self) -> Option { - self.tick_momentum_at(Instant::now()) - } - - fn tick_momentum_at(&mut self, now: Instant) -> Option { - let momentum = self.momentum.as_mut()?; - let elapsed = now.duration_since(momentum.started_at); - let distance = self - .tuning - .scroll_physics - .fling_distance(momentum.speed, elapsed); - // Prediction overshoot can start momentum ahead of its curve. Hold - // that position until the curve catches up instead of stepping back. - let step = (distance - momentum.emitted_distance).max(0.); - momentum.emitted_distance = momentum.emitted_distance.max(distance); - let delta = point( - px(momentum.direction.x * step), - px(momentum.direction.y * step), - ); - let position = momentum.position; - if elapsed >= momentum.duration { - self.momentum = None; - Some(RecognizedTouchGesture::Scroll(scroll_event( - position, - delta, - TouchPhase::Ended, - ))) - } else { - Some(RecognizedTouchGesture::Scroll(scroll_event( - position, - delta, - TouchPhase::Moved, - ))) - } - } -} - -fn scroll_event( - position: Point, - delta: Point, - touch_phase: TouchPhase, -) -> ScrollWheelEvent { - ScrollWheelEvent { - position, - delta: ScrollDelta::Pixels(delta), - modifiers: Modifiers::default(), - touch_phase, - } -} - -/// Estimates the velocity a touch had at its newest sample. -#[derive(Default)] -struct VelocityTracker { - samples: VecDeque<(Instant, Point)>, -} - -impl VelocityTracker { - fn push(&mut self, time: Instant, position: Point) { - self.samples.push_back((time, position)); - while self.samples.len() > VELOCITY_MAX_SAMPLES { - self.samples.pop_front(); - } - } - - fn latest_sample_time(&self) -> Option { - self.samples.back().map(|(time, _)| *time) - } - - /// The velocity at the newest sample, in pixels per second. - /// - /// Fits a second-degree polynomial by least squares over the trailing - /// [`VELOCITY_WINDOW`] and takes its derivative at the newest sample, - /// like Flutter's `VelocityTracker` and Android's `lsq2` strategy. An - /// endpoint difference over the same window would report the window's - /// *average* speed, which for a flick — still accelerating at lift-off — - /// is roughly half the speed the finger actually had at release. - fn velocity(&self) -> Point { - let Some((newest_time, _)) = self.samples.back() else { - return Point::default(); - }; - let mut times_seconds: SmallVec<[f64; VELOCITY_MAX_SAMPLES]> = SmallVec::new(); - let mut horizontal: SmallVec<[f64; VELOCITY_MAX_SAMPLES]> = SmallVec::new(); - let mut vertical: SmallVec<[f64; VELOCITY_MAX_SAMPLES]> = SmallVec::new(); - let mut previous_time = *newest_time; - for (time, position) in self.samples.iter().rev() { - let age = newest_time.duration_since(*time); - if age > VELOCITY_WINDOW - || previous_time.duration_since(*time) > VELOCITY_ASSUME_STOPPED_GAP - { - break; - } - previous_time = *time; - times_seconds.push(-age.as_secs_f64()); - horizontal.push(f64::from(f32::from(position.x))); - vertical.push(f64::from(f32::from(position.y))); - } - - let endpoint_estimate = |values: &[f64]| -> f32 { - let elapsed = -times_seconds.last().copied().unwrap_or(0.); - if elapsed <= f64::EPSILON { - return 0.; - } - ((values.first().copied().unwrap_or(0.) - values.last().copied().unwrap_or(0.)) - / elapsed) as f32 - }; - if times_seconds.len() < 3 { - return point(endpoint_estimate(&horizontal), endpoint_estimate(&vertical)); - } - point( - quadratic_velocity_at_newest(×_seconds, &horizontal).map_or_else( - || endpoint_estimate(&horizontal), - |velocity| velocity as f32, - ), - quadratic_velocity_at_newest(×_seconds, &vertical) - .map_or_else(|| endpoint_estimate(&vertical), |velocity| velocity as f32), - ) - } -} - -/// Least-squares fit of `value = a0 + a1·t + a2·t²` returning `a1`: the -/// fitted curve's velocity at `t = 0`, which callers place at the newest -/// sample. `None` when the samples are too degenerate to fit (all -/// simultaneous, for example). -fn quadratic_velocity_at_newest(times: &[f64], values: &[f64]) -> Option { - let count = times.len() as f64; - let (mut sum_t1, mut sum_t2, mut sum_t3, mut sum_t4) = (0., 0., 0., 0.); - let (mut sum_v, mut sum_vt, mut sum_vt2) = (0., 0., 0.); - for (&time, &value) in times.iter().zip(values) { - let time_squared = time * time; - sum_t1 += time; - sum_t2 += time_squared; - sum_t3 += time_squared * time; - sum_t4 += time_squared * time_squared; - sum_v += value; - sum_vt += value * time; - sum_vt2 += value * time_squared; - } - // Cramer's rule on the 3×3 normal equations, solved for the linear - // coefficient only. - let determinant = count * (sum_t2 * sum_t4 - sum_t3 * sum_t3) - - sum_t1 * (sum_t1 * sum_t4 - sum_t3 * sum_t2) - + sum_t2 * (sum_t1 * sum_t3 - sum_t2 * sum_t2); - if determinant.abs() < 1e-12 { - return None; - } - let linear_determinant = count * (sum_vt * sum_t4 - sum_t3 * sum_vt2) - - sum_v * (sum_t1 * sum_t4 - sum_t3 * sum_t2) - + sum_t2 * (sum_t1 * sum_vt2 - sum_vt * sum_t2); - Some(linear_determinant / determinant) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::point; - - #[test] - fn ongoing_scroll_locks_to_dominant_axis() { - let now = Instant::now(); - let mut ongoing_scroll = OngoingScroll::default(); - let mut horizontal_delta = point(px(10.), px(2.)); - ongoing_scroll.filter_at(&mut horizontal_delta, TouchPhase::Started, now); - assert_eq!(ongoing_scroll.axis, Some(Axis::Horizontal)); - assert_eq!(horizontal_delta, point(px(10.), px(0.))); - - let mut continued_delta = point(px(3.), px(2.)); - ongoing_scroll.filter_at( - &mut continued_delta, - TouchPhase::Moved, - now + Duration::from_millis(1), - ); - assert_eq!(ongoing_scroll.axis, Some(Axis::Horizontal)); - assert_eq!(continued_delta, point(px(3.), px(0.))); - } - - #[test] - fn ongoing_scroll_unlocks_when_direction_changes() { - let now = Instant::now(); - let mut ongoing_scroll = OngoingScroll::default(); - let mut horizontal_delta = point(px(10.), px(2.)); - ongoing_scroll.filter_at(&mut horizontal_delta, TouchPhase::Started, now); - - let mut vertical_delta = point(px(2.), px(10.)); - ongoing_scroll.filter_at( - &mut vertical_delta, - TouchPhase::Moved, - now + Duration::from_millis(1), - ); - assert_eq!(ongoing_scroll.axis, None); - assert_eq!(vertical_delta, point(px(2.), px(10.))); - } - - #[test] - fn ongoing_scroll_starts_new_gesture_at_timeout_boundary() { - let now = Instant::now(); - let mut ongoing_scroll = OngoingScroll::default(); - let mut horizontal_delta = point(px(10.), px(2.)); - ongoing_scroll.filter_at(&mut horizontal_delta, TouchPhase::Moved, now); - - let mut vertical_delta = point(px(2.), px(10.)); - ongoing_scroll.filter_at( - &mut vertical_delta, - TouchPhase::Moved, - now + SCROLL_EVENT_SEPARATION, - ); - assert_eq!(ongoing_scroll.axis, Some(Axis::Vertical)); - assert_eq!(vertical_delta, point(px(0.), px(10.))); - } - - #[test] - fn ongoing_scroll_ignores_zero_delta_and_resets_when_ended() { - let now = Instant::now(); - let mut ongoing_scroll = OngoingScroll::default(); - let mut horizontal_delta = point(px(10.), px(2.)); - ongoing_scroll.filter_at(&mut horizontal_delta, TouchPhase::Started, now); - - let mut zero_delta = Point::default(); - ongoing_scroll.filter_at( - &mut zero_delta, - TouchPhase::Ended, - now + Duration::from_millis(1), - ); - assert_eq!(ongoing_scroll.axis, None); - - let mut vertical_delta = point(px(2.), px(3.)); - ongoing_scroll.filter_at( - &mut vertical_delta, - TouchPhase::Moved, - now + Duration::from_millis(2), - ); - assert_eq!(ongoing_scroll.axis, Some(Axis::Vertical)); - assert_eq!(vertical_delta, point(px(0.), px(3.))); - } - - #[test] - fn ongoing_scroll_ignores_zero_delta_movement() { - let now = Instant::now(); - let mut ongoing_scroll = OngoingScroll::default(); - let mut horizontal_delta = point(px(10.), px(2.)); - ongoing_scroll.filter_at(&mut horizontal_delta, TouchPhase::Started, now); - - let mut zero_delta = Point::default(); - ongoing_scroll.filter_at( - &mut zero_delta, - TouchPhase::Moved, - now + SCROLL_EVENT_SEPARATION, - ); - - let mut vertical_delta = point(px(2.), px(10.)); - ongoing_scroll.filter_at( - &mut vertical_delta, - TouchPhase::Moved, - now + SCROLL_EVENT_SEPARATION, - ); - assert_eq!(ongoing_scroll.axis, Some(Axis::Vertical)); - assert_eq!(vertical_delta, point(px(0.), px(10.))); - } - - #[test] - fn ongoing_scroll_supports_moved_only_platforms() { - let now = Instant::now(); - let mut ongoing_scroll = OngoingScroll::default(); - let mut horizontal_delta = point(px(10.), px(2.)); - ongoing_scroll.filter_at(&mut horizontal_delta, TouchPhase::Moved, now); - assert_eq!(ongoing_scroll.axis, Some(Axis::Horizontal)); - assert_eq!(horizontal_delta, point(px(10.), px(0.))); - } - - #[test] - fn touch_within_slop_resolves_to_tap() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - let touch = TouchId(1); - - let recognized = - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 10., 10.), now); - assert!(recognized.is_empty()); - let recognized = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 12., 11.), - now + Duration::from_millis(20), - ); - assert!(recognized.is_empty()); - - let recognized = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Ended, 12., 11.), - now + Duration::from_millis(60), - ); - let [RecognizedTouchGesture::Tap { down, up }] = recognized.as_slice() else { - panic!("expected tap, got {recognized:?}"); - }; - assert_eq!(down.click_count, 1); - assert_eq!(down.position, point(px(12.), px(11.))); - assert_eq!(up.click_count, 1); - } - - #[test] - fn consecutive_taps_accumulate_tap_count() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - - recognizer.handle_event_at(&touch_event(TouchId(1), TouchPhase::Started, 10., 10.), now); - recognizer.handle_event_at( - &touch_event(TouchId(1), TouchPhase::Ended, 10., 10.), - now + Duration::from_millis(40), - ); - - let second_down = now + Duration::from_millis(200); - recognizer.handle_event_at( - &touch_event(TouchId(2), TouchPhase::Started, 14., 10.), - second_down, - ); - let recognized = recognizer.handle_event_at( - &touch_event(TouchId(2), TouchPhase::Ended, 14., 10.), - second_down + Duration::from_millis(40), - ); - let [RecognizedTouchGesture::Tap { down, .. }] = recognized.as_slice() else { - panic!("expected tap, got {recognized:?}"); - }; - assert_eq!(down.click_count, 2); - - let late_down = second_down + Duration::from_secs(2); - recognizer.handle_event_at( - &touch_event(TouchId(3), TouchPhase::Started, 14., 10.), - late_down, - ); - let recognized = recognizer.handle_event_at( - &touch_event(TouchId(3), TouchPhase::Ended, 14., 10.), - late_down + Duration::from_millis(40), - ); - let [RecognizedTouchGesture::Tap { down, .. }] = recognized.as_slice() else { - panic!("expected tap, got {recognized:?}"); - }; - assert_eq!(down.click_count, 1); - } - - #[test] - fn touch_beyond_slop_resolves_to_pan() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - let touch = TouchId(1); - - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 100., 100.), now); - - let recognized = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 100., 120.), - now + Duration::from_millis(16), - ); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.touch_phase, TouchPhase::Started); - assert_eq!(scroll.position, point(px(100.), px(100.))); - assert_eq!(scroll.delta.pixel_delta(px(16.)), point(px(0.), px(20.))); - - let recognized = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 100., 135.), - now + Duration::from_millis(32), - ); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.touch_phase, TouchPhase::Moved); - assert_eq!(scroll.position, point(px(100.), px(100.))); - assert_eq!(scroll.delta.pixel_delta(px(16.)), point(px(0.), px(15.))); - - let recognized = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Ended, 100., 135.), - now + Duration::from_millis(48), - ); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.touch_phase, TouchPhase::Ended); - } - - #[test] - fn touch_pan_stays_locked_to_its_initial_dominant_axis() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - let touch = TouchId(1); - - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 100., 100.), now); - - let recognized = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 104., 120.), - now + Duration::from_millis(16), - ); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.delta.pixel_delta(px(16.)), point(px(0.), px(20.))); - - let recognized = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 134., 125.), - now + Duration::from_millis(32), - ); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.delta.pixel_delta(px(16.)), point(px(0.), px(5.))); - } - - #[test] - fn touch_pan_locks_to_horizontal_axis() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - let touch = TouchId(1); - - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 100., 100.), now); - - let recognized = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 120., 104.), - now + Duration::from_millis(16), - ); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.delta.pixel_delta(px(16.)), point(px(20.), px(0.))); - } - - #[test] - fn predicted_positions_lead_the_pan_but_totals_converge_on_release() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - let touch = TouchId(1); - - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 100., 100.), now); - - // The first pan step scrolls to the predicted position, not the raw one. - let mut moved = touch_event(touch, TouchPhase::Moved, 100., 120.); - moved.predicted_position = Some(point(px(106.), px(128.))); - let recognized = recognizer.handle_event_at(&moved, now + Duration::from_millis(16)); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.delta.pixel_delta(px(16.)), point(px(0.), px(28.))); - - // The next step is measured from where the previous prediction left - // the content, so an overshoot is paid back here. - let mut moved = touch_event(touch, TouchPhase::Moved, 100., 130.); - moved.predicted_position = Some(point(px(104.), px(134.))); - let recognized = recognizer.handle_event_at(&moved, now + Duration::from_millis(32)); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.delta.pixel_delta(px(16.)), point(px(0.), px(6.))); - - // A release without a fling (the finger stopped long before lifting) - // targets the raw position: the total scrolled distance equals the - // finger's actual travel despite the predictions. - let recognized = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Ended, 100., 130.), - now + Duration::from_millis(120), - ); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.touch_phase, TouchPhase::Ended); - assert!(!recognizer.has_momentum()); - assert_eq!(scroll.delta.pixel_delta(px(16.)), point(px(0.), px(-4.))); - } - - #[test] - fn predicted_positions_do_not_emit_false_reversals() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - let touch = TouchId(1); - - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 100., 100.), now); - - let mut moved = touch_event(touch, TouchPhase::Moved, 100., 120.); - moved.predicted_position = Some(point(px(100.), px(130.))); - let recognized = recognizer.handle_event_at(&moved, now + Duration::from_millis(16)); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.delta.pixel_delta(px(16.)), point(px(0.), px(30.))); - - let mut moved = touch_event(touch, TouchPhase::Moved, 100., 125.); - moved.predicted_position = Some(point(px(100.), px(127.))); - let recognized = recognizer.handle_event_at(&moved, now + Duration::from_millis(32)); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!( - scroll.delta.pixel_delta(px(16.)), - Point::::default() - ); - - let mut moved = touch_event(touch, TouchPhase::Moved, 100., 125.); - moved.predicted_position = Some(point(px(100.), px(126.))); - let recognized = recognizer.handle_event_at(&moved, now + Duration::from_millis(40)); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!( - scroll.delta.pixel_delta(px(16.)), - Point::::default() - ); - - let mut moved = touch_event(touch, TouchPhase::Moved, 100., 132.); - moved.predicted_position = Some(point(px(100.), px(136.))); - let recognized = recognizer.handle_event_at(&moved, now + Duration::from_millis(48)); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.delta.pixel_delta(px(16.)), point(px(0.), px(6.))); - - let mut moved = touch_event(touch, TouchPhase::Moved, 100., 124.); - moved.predicted_position = Some(point(px(100.), px(140.))); - let recognized = recognizer.handle_event_at(&moved, now + Duration::from_millis(64)); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.delta.pixel_delta(px(16.)), point(px(0.), px(-12.))); - } - - #[test] - fn predicted_overshoot_folds_into_the_fling_without_scrolling_backwards() { - let now = Instant::now(); - let mut total_with_prediction = 0f32; - let mut total_without_prediction = 0f32; - for use_prediction in [true, false] { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let mut total = 0f32; - let mut drain = |recognized: &[RecognizedTouchGesture], upward_only: bool| { - for gesture in recognized { - let RecognizedTouchGesture::Scroll(scroll) = gesture else { - panic!("expected scroll, got {gesture:?}"); - }; - let delta = scroll.delta.pixel_delta(px(16.)).y; - if upward_only { - assert!( - delta <= px(0.), - "content moved backwards by {delta:?} during an upward gesture" - ); - } - total += f32::from(delta); - } - }; - - recognizer.handle_event_at( - &touch_event(TouchId(1), TouchPhase::Started, 100., 500.), - now, - ); - for step in 1..=5u64 { - let raw_y = 500. - step as f32 * 40.; - let mut moved = touch_event(TouchId(1), TouchPhase::Moved, 100., raw_y); - if use_prediction { - moved.predicted_position = Some(point(px(100.), px(raw_y - 25.))); - } - let recognized = - recognizer.handle_event_at(&moved, now + Duration::from_millis(step * 16)); - drain(&recognized, use_prediction); - } - // The release leaves the emitted position 25px ahead of the raw - // one; with prediction the correction must not scroll backwards. - let recognized = recognizer.handle_event_at( - &touch_event(TouchId(1), TouchPhase::Ended, 100., 300.), - now + Duration::from_millis(90), - ); - drain(&recognized, use_prediction); - assert!(recognizer.has_momentum()); - let mut tick = now + Duration::from_millis(91); - while recognizer.has_momentum() { - if let Some(gesture) = recognizer.tick_momentum_at(tick) { - drain(&[gesture], use_prediction); - } - tick += Duration::from_millis(16); - } - - if use_prediction { - total_with_prediction = total; - } else { - total_without_prediction = total; - } - } - // Folding the overshoot into the fling redistributes the travel but - // must not change where the content comes to rest. - assert!( - (total_with_prediction - total_without_prediction).abs() < 0.01, - "totals diverged: {total_with_prediction} vs {total_without_prediction}" - ); - } - - #[test] - fn fast_release_starts_momentum_that_decays_to_a_stop() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - let touch = TouchId(1); - - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 100., 300.), now); - for step in 1..=5 { - recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 100., 300. - step as f32 * 20.), - now + Duration::from_millis(step * 16), - ); - } - recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Ended, 100., 200.), - now + Duration::from_millis(6 * 16), - ); - assert!(recognizer.has_momentum()); - - let tick = now + Duration::from_millis(6 * 16 + 16); - let recognized = recognizer.tick_momentum_at(tick); - let Some(RecognizedTouchGesture::Scroll(scroll)) = recognized else { - panic!("expected momentum scroll, got {recognized:?}"); - }; - assert_eq!(scroll.touch_phase, TouchPhase::Moved); - assert_eq!(scroll.position, point(px(100.), px(300.))); - let delta = scroll.delta.pixel_delta(px(16.)); - assert!( - delta.y < px(0.), - "momentum should continue upward, got {delta:?}" - ); - // The least-squares fit may leave float residue on the motionless axis. - assert!( - delta.x.abs() < px(0.001), - "expected no x motion, got {delta:?}" - ); - - let mut last_phase = TouchPhase::Moved; - let mut ticks = 0; - let mut time = tick; - while recognizer.has_momentum() { - time += Duration::from_millis(16); - ticks += 1; - assert!(ticks < 1000, "momentum never stopped"); - if let Some(RecognizedTouchGesture::Scroll(scroll)) = recognizer.tick_momentum_at(time) - { - last_phase = scroll.touch_phase; - } - } - assert_eq!(last_phase, TouchPhase::Ended); - assert!(recognizer.tick_momentum_at(time).is_none()); - } - - #[test] - fn diagonal_release_flings_only_on_locked_axis() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - let touch = TouchId(1); - - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 100., 300.), now); - for step in 1..=5 { - let recognized = recognizer.handle_event_at( - &touch_event( - touch, - TouchPhase::Moved, - 100. + step as f32 * 3., - 300. - step as f32 * 20., - ), - now + Duration::from_millis(step * 16), - ); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.delta.pixel_delta(px(16.)).x, px(0.)); - } - recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Ended, 115., 200.), - now + Duration::from_millis(6 * 16), - ); - assert!(recognizer.has_momentum()); - - let mut time = now + Duration::from_millis(6 * 16); - while recognizer.has_momentum() { - time += Duration::from_millis(16); - if let Some(RecognizedTouchGesture::Scroll(scroll)) = recognizer.tick_momentum_at(time) - { - let delta = scroll.delta.pixel_delta(px(16.)); - assert_eq!(delta.x, px(0.)); - assert!(delta.y <= px(0.)); - } - } - } - - #[test] - fn slow_release_does_not_start_momentum() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - let touch = TouchId(1); - - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 100., 300.), now); - recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 100., 280.), - now + Duration::from_millis(16), - ); - recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 100., 279.), - now + Duration::from_millis(500), - ); - recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Ended, 100., 279.), - now + Duration::from_millis(600), - ); - assert!(!recognizer.has_momentum()); - } - - #[test] - fn new_touch_interrupts_momentum() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - - recognizer.handle_event_at( - &touch_event(TouchId(1), TouchPhase::Started, 100., 300.), - now, - ); - for step in 1..=3 { - recognizer.handle_event_at( - &touch_event( - TouchId(1), - TouchPhase::Moved, - 100., - 300. - step as f32 * 33., - ), - now + Duration::from_millis(step * 16), - ); - } - recognizer.handle_event_at( - &touch_event(TouchId(1), TouchPhase::Ended, 100., 200.), - now + Duration::from_millis(64), - ); - assert!(recognizer.has_momentum()); - - let recognized = recognizer.handle_event_at( - &touch_event(TouchId(2), TouchPhase::Started, 100., 200.), - now + Duration::from_millis(200), - ); - assert!(!recognizer.has_momentum()); - let [ - RecognizedTouchGesture::Scroll(closing), - RecognizedTouchGesture::Scroll(opening), - ] = recognized.as_slice() - else { - panic!("expected closing and opening scrolls, got {recognized:?}"); - }; - assert_eq!(closing.touch_phase, TouchPhase::Ended); - assert!(closing.delta.pixel_delta(px(16.)).is_zero()); - assert_eq!(opening.touch_phase, TouchPhase::Started); - assert!(opening.delta.pixel_delta(px(16.)).is_zero()); - } - - #[test] - fn catching_a_fling_pans_immediately_and_never_taps() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - - recognizer.handle_event_at( - &touch_event(TouchId(1), TouchPhase::Started, 100., 300.), - now, - ); - for step in 1..=3 { - recognizer.handle_event_at( - &touch_event( - TouchId(1), - TouchPhase::Moved, - 100., - 300. - step as f32 * 33., - ), - now + Duration::from_millis(step * 16), - ); - } - recognizer.handle_event_at( - &touch_event(TouchId(1), TouchPhase::Ended, 100., 200.), - now + Duration::from_millis(64), - ); - assert!(recognizer.has_momentum()); - - recognizer.handle_event_at( - &touch_event(TouchId(2), TouchPhase::Started, 100., 200.), - now + Duration::from_millis(200), - ); - - // A movement well within the slop scrolls immediately. - let recognized = recognizer.handle_event_at( - &touch_event(TouchId(2), TouchPhase::Moved, 100., 197.), - now + Duration::from_millis(216), - ); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.touch_phase, TouchPhase::Moved); - assert_eq!(scroll.delta.pixel_delta(px(16.)), point(px(0.), px(-3.))); - - // Releasing the catch is not a tap. - let recognized = recognizer.handle_event_at( - &touch_event(TouchId(2), TouchPhase::Ended, 100., 197.), - now + Duration::from_millis(232), - ); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.touch_phase, TouchPhase::Ended); - } - - #[test] - fn cancelled_pan_emits_cancelled_scroll_and_no_tap() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - let touch = TouchId(1); - - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 100., 100.), now); - recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 100., 150.), - now + Duration::from_millis(16), - ); - let recognized = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Cancelled, 100., 150.), - now + Duration::from_millis(32), - ); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected cancelled scroll, got {recognized:?}"); - }; - assert_eq!(scroll.touch_phase, TouchPhase::Cancelled); - assert!(!recognizer.has_momentum()); - - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 100., 100.), now); - let recognized = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Cancelled, 100., 102.), - now + Duration::from_millis(16), - ); - assert!(recognized.is_empty(), "cancelled tap must not click"); - } - - #[test] - fn concurrent_touches_are_ignored_while_one_is_active() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let now = Instant::now(); - - recognizer.handle_event_at( - &touch_event(TouchId(1), TouchPhase::Started, 100., 100.), - now, - ); - let recognized = recognizer.handle_event_at( - &touch_event(TouchId(2), TouchPhase::Started, 200., 200.), - now + Duration::from_millis(8), - ); - assert!(recognized.is_empty()); - let recognized = recognizer.handle_event_at( - &touch_event(TouchId(2), TouchPhase::Moved, 200., 300.), - now + Duration::from_millis(16), - ); - assert!(recognized.is_empty()); - let recognized = recognizer.handle_event_at( - &touch_event(TouchId(2), TouchPhase::Ended, 200., 300.), - now + Duration::from_millis(24), - ); - assert!(recognized.is_empty()); - - // The first touch still resolves normally. - let recognized = recognizer.handle_event_at( - &touch_event(TouchId(1), TouchPhase::Moved, 100., 150.), - now + Duration::from_millis(32), - ); - let [RecognizedTouchGesture::Scroll(scroll)] = recognized.as_slice() else { - panic!("expected scroll, got {recognized:?}"); - }; - assert_eq!(scroll.touch_phase, TouchPhase::Started); - } - - #[test] - fn spline_position_table_matches_the_bezier_curve() { - let samples = friction_spline::spline_position_samples(); - // AOSP's initializer solves sample 0 numerically like every other - // sample, so it lands within solver tolerance of zero, not at zero. - assert!(samples[0].abs() < 1e-4); - assert_eq!(samples[100], 1.); - for window in samples.windows(2) { - assert!(window[0] < window[1], "table must be strictly increasing"); - } - // Each table entry must lie on the defining parametric Bezier: for - // sample i there must be a curve parameter whose time component is - // i/100 and whose position component is the stored value. - for (i, &stored_position) in samples.iter().enumerate().take(100) { - let alpha = i as f32 / 100.; - let (mut lower, mut upper) = (0f32, 1f32); - for _ in 0..50 { - let middle = (lower + upper) / 2.; - let (time, _) = friction_spline::bezier_time_and_position(middle); - if time > alpha { - upper = middle; - } else { - lower = middle; - } - } - let (time, position) = friction_spline::bezier_time_and_position((lower + upper) / 2.); - assert!( - (time - alpha).abs() < 1e-4, - "sample {i}: time {time} != {alpha}" - ); - assert!( - (position - stored_position).abs() < 1e-3, - "sample {i}: position {position} != stored {stored_position}" - ); - } - } - - #[test] - fn fling_curves_are_sane_for_both_physics() { - for physics in [ScrollPhysics::ios(), ScrollPhysics::android()] { - let slow = physics.fling_duration(500.); - let fast = physics.fling_duration(4000.); - assert!(slow > Duration::ZERO, "{physics:?}"); - assert!(fast > slow, "faster flings must coast longer: {physics:?}"); - - let halfway = physics.fling_distance(4000., fast / 2); - let total = physics.fling_distance(4000., fast); - assert!(halfway > 0. && halfway < total, "{physics:?}"); - assert!( - physics.fling_distance(4000., fast * 2) == total, - "distance must not grow past the fling duration: {physics:?}" - ); - assert!( - physics.fling_distance(4000., fast) > physics.fling_distance(500., slow), - "faster flings must travel further: {physics:?}" - ); - } - } - - #[test] - fn momentum_is_frame_rate_independent() { - // The same fling ticked at 60Hz and as one huge stalled frame must - // cover identical ground. - let total_distance_with_tick_length = |tick: Duration| -> f32 { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning { - scroll_physics: ScrollPhysics::android(), - ..GestureTuning::default() - }); - let now = Instant::now(); - recognizer.handle_event_at( - &touch_event(TouchId(1), TouchPhase::Started, 100., 500.), - now, - ); - for step in 1..=3 { - recognizer.handle_event_at( - &touch_event( - TouchId(1), - TouchPhase::Moved, - 100., - 500. - step as f32 * 40., - ), - now + Duration::from_millis(step * 16), - ); - } - recognizer.handle_event_at( - &touch_event(TouchId(1), TouchPhase::Ended, 100., 380.), - now + Duration::from_millis(64), - ); - assert!(recognizer.has_momentum()); - - let mut total = 0f32; - let mut time = now + Duration::from_millis(64); - let mut guard = 0; - while recognizer.has_momentum() { - time += tick; - guard += 1; - assert!(guard < 10_000, "momentum never stopped"); - if let Some(RecognizedTouchGesture::Scroll(scroll)) = - recognizer.tick_momentum_at(time) - { - total += f32::from(scroll.delta.pixel_delta(px(16.)).y); - } - } - total - }; - - let smooth = total_distance_with_tick_length(Duration::from_millis(16)); - let stalled = total_distance_with_tick_length(Duration::from_secs(10)); - assert!( - (smooth - stalled).abs() < 0.01, - "expected identical fling distance, got {smooth} vs {stalled}" - ); - } - - #[test] - fn flick_velocity_reflects_release_speed_not_window_average() { - // A uniformly accelerating flick: position grows quadratically, so - // the speed at the newest sample (2·k·t) is twice the window - // average (k·t). The estimator must report the former. - let mut velocity_tracker = VelocityTracker::default(); - let start = Instant::now(); - for step in 0..=6 { - let t = step as f32 * 0.016; - velocity_tracker.push( - start + Duration::from_millis(step * 16), - point(px(0.), px(1000. * t * t)), - ); - } - let velocity = velocity_tracker.velocity(); - let release_speed = 2. * 1000. * 0.096; - assert!( - (velocity.y - release_speed).abs() < 1., - "expected ≈{release_speed} px/s at release, got {} px/s", - velocity.y - ); - assert_eq!(velocity.x, 0.); - } - - #[test] - fn samples_before_a_pause_do_not_contribute_velocity() { - // Fast motion, then a hold longer than the stopped-finger gap, then - // a slow nudge: only the motion after the pause describes the - // release. - let mut velocity_tracker = VelocityTracker::default(); - let start = Instant::now(); - velocity_tracker.push(start, point(px(0.), px(0.))); - velocity_tracker.push(start + Duration::from_millis(16), point(px(0.), px(50.))); - velocity_tracker.push(start + Duration::from_millis(80), point(px(0.), px(52.))); - velocity_tracker.push(start + Duration::from_millis(96), point(px(0.), px(54.))); - let velocity = velocity_tracker.velocity(); - assert!( - velocity.y < 200., - "pre-pause motion leaked into the estimate: {} px/s", - velocity.y - ); - } - - #[test] - fn claimed_touch_drag_emits_phased_stream_without_pan_or_tap() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let touch = TouchId(1); - let now = Instant::now(); - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 10., 20.), now); - let Some(RecognizedTouchGesture::TouchDrag(started)) = recognizer.offer_touch_drag(touch) - else { - panic!("expected touch drag"); - }; - assert_eq!(started.phase, TouchPhase::Started); - assert_eq!(started.start_position, point(px(10.), px(20.))); - recognizer.resolve_touch_drag(true); - - let moved = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 40., 50.), - now + Duration::from_millis(10), - ); - let [RecognizedTouchGesture::TouchDrag(moved)] = moved.as_slice() else { - panic!("expected moved touch drag, got {moved:?}"); - }; - assert_eq!(moved.phase, TouchPhase::Moved); - assert_eq!(moved.position, point(px(40.), px(50.))); - - let ended = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Ended, 45., 55.), - now + Duration::from_millis(20), - ); - let [RecognizedTouchGesture::TouchDrag(ended)] = ended.as_slice() else { - panic!("expected ended touch drag, got {ended:?}"); - }; - assert_eq!(ended.phase, TouchPhase::Ended); - assert_eq!(ended.position, point(px(45.), px(55.))); - } - - #[test] - fn unclaimed_touch_drag_remains_a_pan_candidate() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let touch = TouchId(1); - let now = Instant::now(); - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 0., 0.), now); - assert!(recognizer.offer_touch_drag(touch).is_some()); - recognizer.resolve_touch_drag(false); - - let moved = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 20., 0.), - now + Duration::from_millis(10), - ); - assert!(matches!( - moved.as_slice(), - [RecognizedTouchGesture::Scroll(ScrollWheelEvent { - touch_phase: TouchPhase::Started, - .. - })] - )); - } - - #[test] - fn claimed_long_press_emits_phased_stream_without_tap() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let touch = TouchId(1); - let now = Instant::now(); - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 10., 20.), now); - let Some(RecognizedTouchGesture::LongPress(started)) = recognizer.offer_long_press(touch) - else { - panic!("expected long press"); - }; - assert_eq!(started.phase, TouchPhase::Started); - assert_eq!(started.start_position, point(px(10.), px(20.))); - recognizer.resolve_long_press(true); - - let moved = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 12., 21.), - now + Duration::from_millis(510), - ); - let [RecognizedTouchGesture::LongPress(moved)] = moved.as_slice() else { - panic!("expected moved long press, got {moved:?}"); - }; - assert_eq!(moved.phase, TouchPhase::Moved); - - let ended = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Ended, 12., 21.), - now + Duration::from_millis(520), - ); - let [RecognizedTouchGesture::LongPress(ended)] = ended.as_slice() else { - panic!("expected ended long press, got {ended:?}"); - }; - assert_eq!(ended.phase, TouchPhase::Ended); - } - - #[test] - fn unclaimed_long_press_remains_a_tap_candidate() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let touch = TouchId(1); - let now = Instant::now(); - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 10., 20.), now); - assert!(recognizer.offer_long_press(touch).is_some()); - recognizer.resolve_long_press(false); - - let ended = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Ended, 10., 20.), - now + Duration::from_millis(510), - ); - assert!(matches!( - ended.as_slice(), - [RecognizedTouchGesture::Tap { .. }] - )); - } - - #[test] - fn unclaimed_long_press_can_still_become_a_pan() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let touch = TouchId(1); - let now = Instant::now(); - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 0., 0.), now); - assert!(recognizer.offer_long_press(touch).is_some()); - recognizer.resolve_long_press(false); - - let moved = recognizer.handle_event_at( - &touch_event(touch, TouchPhase::Moved, 20., 0.), - now + Duration::from_millis(510), - ); - assert!(matches!( - moved.as_slice(), - [RecognizedTouchGesture::Scroll(ScrollWheelEvent { - touch_phase: TouchPhase::Started, - .. - })] - )); - } - - #[test] - fn long_press_offer_is_one_shot_and_specific_to_pending_touch() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let touch = TouchId(1); - recognizer.handle_event(&touch_event(touch, TouchPhase::Started, 0., 0.)); - - assert!( - recognizer - .handle_event(&touch_event(TouchId(2), TouchPhase::Moved, 20., 0.)) - .is_empty() - ); - assert!(recognizer.offer_long_press(TouchId(2)).is_none()); - assert!(recognizer.offer_long_press(touch).is_some()); - assert!(recognizer.offer_long_press(touch).is_none()); - } - - #[test] - fn long_press_cannot_be_offered_after_pending_touch_resolves() { - for phase in [TouchPhase::Ended, TouchPhase::Cancelled, TouchPhase::Moved] { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let touch = TouchId(1); - let now = Instant::now(); - recognizer.handle_event_at(&touch_event(touch, TouchPhase::Started, 0., 0.), now); - let position = if phase == TouchPhase::Moved { 20. } else { 0. }; - recognizer.handle_event_at( - &touch_event(touch, phase, position, 0.), - now + Duration::from_millis(10), - ); - assert!(recognizer.offer_long_press(touch).is_none()); - } - } - - #[test] - fn claimed_long_press_emits_cancelled_for_its_touch_only() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let touch = TouchId(1); - recognizer.handle_event(&touch_event(touch, TouchPhase::Started, 4., 5.)); - assert!(recognizer.offer_long_press(touch).is_some()); - recognizer.resolve_long_press(true); - - assert!( - recognizer - .handle_event(&touch_event(TouchId(2), TouchPhase::Cancelled, 9., 9.)) - .is_empty() - ); - let cancelled = recognizer.handle_event(&touch_event(touch, TouchPhase::Cancelled, 6., 7.)); - let [RecognizedTouchGesture::LongPress(cancelled)] = cancelled.as_slice() else { - panic!("expected cancelled long press, got {cancelled:?}"); - }; - assert_eq!(cancelled.phase, TouchPhase::Cancelled); - assert_eq!(cancelled.start_position, point(px(4.), px(5.))); - assert_eq!(cancelled.position, point(px(6.), px(7.))); - } - - #[test] - fn unrelated_touch_cannot_end_or_cancel_pending_touch() { - for phase in [TouchPhase::Ended, TouchPhase::Cancelled] { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let touch = TouchId(1); - recognizer.handle_event(&touch_event(touch, TouchPhase::Started, 4., 5.)); - - assert!( - recognizer - .handle_event(&touch_event(TouchId(2), phase, 9., 9.)) - .is_empty() - ); - assert!(recognizer.offer_long_press(touch).is_some()); - } - } - - #[test] - fn completed_touch_id_cannot_claim_replacement_touch() { - let mut recognizer = TouchGestureRecognizer::new(GestureTuning::default()); - let completed_touch = TouchId(1); - let replacement_touch = TouchId(2); - recognizer.handle_event(&touch_event(completed_touch, TouchPhase::Started, 0., 0.)); - recognizer.handle_event(&touch_event(completed_touch, TouchPhase::Cancelled, 0., 0.)); - recognizer.handle_event(&touch_event(replacement_touch, TouchPhase::Started, 5., 5.)); - - assert!(recognizer.offer_long_press(completed_touch).is_none()); - assert!(recognizer.offer_long_press(replacement_touch).is_some()); - } - - fn touch_event(id: TouchId, phase: TouchPhase, x: f32, y: f32) -> TouchEvent { - TouchEvent { - id, - phase, - position: point(px(x), px(y)), - predicted_position: None, - force: None, - } - } -} diff --git a/crates/gpui_pre/src/global.rs b/crates/gpui_pre/src/global.rs deleted file mode 100644 index a16934c1..00000000 --- a/crates/gpui_pre/src/global.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::{App, BorrowAppContext}; - -/// A marker trait for types that can be stored in GPUI's global state. -/// -/// This trait exists to provide type-safe access to globals by ensuring only -/// types that implement [`Global`] can be used with the accessor methods. For -/// example, trying to access a global with a type that does not implement -/// [`Global`] will result in a compile-time error. -/// -/// Implement this on types you want to store in the context as a global. -/// -/// ## Restricting Access to Globals -/// -/// In some situations you may need to store some global state, but want to -/// restrict access to reading it or writing to it. -/// -/// In these cases, Rust's visibility system can be used to restrict access to -/// a global value. For example, you can create a private struct that implements -/// [`Global`] and holds the global state. Then create a newtype struct that wraps -/// the global type and create custom accessor methods to expose the desired subset -/// of operations. -pub trait Global: 'static { - // This trait is intentionally left empty, by virtue of being a marker trait. - // - // Use additional traits with blanket implementations to attach functionality - // to types that implement `Global`. -} - -/// A trait for reading a global value from the context. -pub trait ReadGlobal { - /// Returns the global instance of the implementing type. - /// - /// Panics if a global for that type has not been assigned. - fn global(cx: &App) -> &Self; -} - -impl ReadGlobal for T { - fn global(cx: &App) -> &Self { - cx.global::() - } -} - -/// A trait for updating a global value in the context. -pub trait UpdateGlobal { - /// Updates the global instance of the implementing type using the provided closure. - /// - /// This method provides the closure with mutable access to the context and the global simultaneously. - fn update_global(cx: &mut C, update: F) -> R - where - C: BorrowAppContext, - F: FnOnce(&mut Self, &mut C) -> R; - - /// Set the global instance of the implementing type. - fn set_global(cx: &mut C, global: Self) - where - C: BorrowAppContext; -} - -impl UpdateGlobal for T { - #[track_caller] - fn update_global(cx: &mut C, update: F) -> R - where - C: BorrowAppContext, - F: FnOnce(&mut Self, &mut C) -> R, - { - cx.update_global(update) - } - - fn set_global(cx: &mut C, global: Self) - where - C: BorrowAppContext, - { - cx.set_global(global) - } -} diff --git a/crates/gpui_pre/src/gpui.rs b/crates/gpui_pre/src/gpui.rs deleted file mode 100644 index cb97c3c0..00000000 --- a/crates/gpui_pre/src/gpui.rs +++ /dev/null @@ -1,354 +0,0 @@ -#![doc = include_str!("../README.md")] -#![warn(missing_docs)] -#![allow(clippy::type_complexity)] // Not useful, GPUI makes heavy use of callbacks -#![allow(clippy::collapsible_else_if)] // False positives in platform specific code -#![allow(unused_mut)] // False positives in platform specific code - -extern crate self as gpui; -#[macro_use] -mod action; -mod app; - -mod arena; -mod asset_cache; -mod assets; -mod bounds_tree; -mod clip; -mod color; -/// The default colors used by GPUI. -pub mod colors; -#[cfg(feature = "profiler")] -mod debug_overlay; -mod element; -mod elements; -mod executor; -mod platform_scheduler; -pub(crate) use platform_scheduler::PlatformScheduler; -mod geometry; -mod gestures; -mod global; -mod input; -mod inspector; -mod interactive; -mod key_dispatch; -mod keymap; -mod path_builder; -mod platform; -pub mod prelude; -/// Profiling utilities for task, frame, and thread performance tracking. -pub mod profiler; -#[cfg(any( - test, - target_os = "windows", - target_os = "linux", - target_family = "wasm", - feature = "test-support", - feature = "bench-support" -))] -#[expect(missing_docs)] -pub mod queue; -mod scene; -mod shared_uri; -mod spring; -mod style; -mod styled; -mod subscription; -mod svg_renderer; -mod tab_stop; -mod taffy; -#[cfg(any(test, feature = "test-support"))] -pub mod test; -mod text_system; -mod util; -mod view; -mod window; - -#[cfg(any(test, feature = "test-support"))] -pub use proptest; - -#[cfg(doc)] -pub mod _accessibility; -#[cfg(doc)] -pub mod _ownership_and_data_flow; - -/// Do not touch, here be dragons for use by gpui_macros and such. -#[doc(hidden)] -pub mod private { - pub use anyhow; - pub use inventory; - pub use schemars; - pub use serde; - pub use serde_json; -} - -mod seal { - /// A mechanism for restricting implementations of a trait to only those in GPUI. - /// See: - pub trait Sealed {} -} - -pub use accesskit; -pub use accesskit::Action as AccessibleAction; -pub use accesskit::{Orientation, Role, Toggled}; -pub use action::*; -pub use anyhow::Result; -pub use app::*; -pub(crate) use arena::*; -pub use asset_cache::*; -pub use assets::*; -pub use clip::*; -pub use color::*; -pub use ctor::ctor; -#[cfg(feature = "profiler")] -pub use debug_overlay::*; -pub use element::*; -pub use elements::*; -pub use executor::*; -pub use geometry::*; -pub use gestures::*; -pub use global::*; -pub use gpui_macros::{ - bench, property_test, register_action, test, AppContext, IntoElement, Render, VisualContext, -}; -pub use spring::*; - -/// Defines a Criterion benchmark group for benchmarks annotated with [`gpui::bench`]. -/// -/// This mirrors `criterion::criterion_group!` so GPUI benchmark files can keep the -/// same shape as ordinary Criterion benchmarks. -/// -/// [`gpui::bench`]: crate::bench -#[macro_export] -macro_rules! bench_group { - ($($tokens:tt)*) => { - criterion::criterion_group!($($tokens)*); - }; -} - -/// Defines the entry point for GPUI Criterion benchmark groups. -/// -/// This mirrors `criterion::criterion_main!` so GPUI benchmark files can keep the -/// same shape as ordinary Criterion benchmarks. -#[macro_export] -macro_rules! bench_main { - ($($tokens:tt)*) => { - criterion::criterion_main!($($tokens)*); - }; -} -pub use gpui_shared_string::*; -pub use gpui_util::arc_cow::ArcCow; -pub use http_client; -pub use input::*; -pub use inspector::*; -pub use interactive::*; -use key_dispatch::*; -pub use keymap::*; -pub use path_builder::*; -pub use platform::*; -pub use profiler::*; -#[cfg(any(target_os = "windows", target_os = "linux", target_family = "wasm"))] -pub use queue::{PriorityQueueReceiver, PriorityQueueSender}; -pub use refineable::*; -pub use scene::*; -pub use shared_uri::*; -use std::{any::Any, future::Future}; -pub use style::*; -pub use styled::*; -pub use subscription::*; -pub use svg_renderer::*; -pub(crate) use tab_stop::*; -use taffy::TaffyLayoutEngine; -pub use taffy::{AvailableSpace, LayoutId}; -#[cfg(any(test, feature = "test-support"))] -pub use test::*; -pub use text_system::*; -pub use util::{FutureExt, Timeout}; -pub use view::*; -pub use window::*; - -#[cfg(not(target_family = "wasm"))] -pub use pollster::block_on; - -/// The context trait, allows the different contexts in GPUI to be used -/// interchangeably for certain operations. -pub trait AppContext { - /// Create a new entity in the app context. - #[expect( - clippy::wrong_self_convention, - reason = "`App::new` is an ubiquitous function for creating entities" - )] - fn new(&mut self, build_entity: impl FnOnce(&mut Context) -> T) -> Entity; - - /// Reserve a slot for a entity to be inserted later. - /// The returned [Reservation] allows you to obtain the [EntityId] for the future entity. - fn reserve_entity(&mut self) -> Reservation; - - /// Insert a new entity in the app context based on a [Reservation] previously obtained from [`reserve_entity`]. - /// - /// [`reserve_entity`]: Self::reserve_entity - fn insert_entity( - &mut self, - reservation: Reservation, - build_entity: impl FnOnce(&mut Context) -> T, - ) -> Entity; - - /// Update a entity in the app context. - fn update_entity( - &mut self, - handle: &Entity, - update: impl FnOnce(&mut T, &mut Context) -> R, - ) -> R - where - T: 'static; - - /// Update a entity in the app context. - fn as_mut<'a, T>(&'a mut self, handle: &Entity) -> GpuiBorrow<'a, T> - where - T: 'static; - - /// Read a entity from the app context. - fn read_entity(&self, handle: &Entity, read: impl FnOnce(&T, &App) -> R) -> R - where - T: 'static; - - /// Update a window for the given handle. - fn update_window(&mut self, window: AnyWindowHandle, f: F) -> Result - where - F: FnOnce(AnyView, &mut Window, &mut App) -> T; - - /// Run `f` against the entity's *current* window — the most recently - /// rendered window that referenced the entity. Returns `None` if the - /// entity has no current window or that window is unavailable. See - /// [`App::with_window`] for the underlying lookup. - fn with_window( - &mut self, - entity_id: EntityId, - f: impl FnOnce(&mut Window, &mut App) -> R, - ) -> Option; - - /// Read a window off of the application context. - fn read_window( - &self, - window: &WindowHandle, - read: impl FnOnce(Entity, &App) -> R, - ) -> Result - where - T: 'static; - - /// Spawn a future on a background thread - fn background_spawn(&self, future: impl Future + Send + 'static) -> Task - where - R: Send + 'static; - - /// Read a global from this app context - fn read_global(&self, callback: impl FnOnce(&G, &App) -> R) -> R - where - G: Global; -} - -/// Returned by [Context::reserve_entity] to later be passed to [Context::insert_entity]. -/// Allows you to obtain the [EntityId] for a entity before it is created. -pub struct Reservation(pub(crate) Slot); - -impl Reservation { - /// Returns the [EntityId] that will be associated with the entity once it is inserted. - pub fn entity_id(&self) -> EntityId { - self.0.entity_id() - } -} - -/// This trait is used for the different visual contexts in GPUI that -/// require a window to be present. -pub trait VisualContext: AppContext { - /// The result type for window operations. - type Result; - - /// Returns the handle of the window associated with this context. - fn window_handle(&self) -> AnyWindowHandle; - - /// Update a view with the given callback - fn update_window_entity( - &mut self, - entity: &Entity, - update: impl FnOnce(&mut T, &mut Window, &mut Context) -> R, - ) -> Self::Result; - - /// Create a new entity, with access to `Window`. - fn new_window_entity( - &mut self, - build_entity: impl FnOnce(&mut Window, &mut Context) -> T, - ) -> Self::Result>; - - /// Replace the root view of a window with a new view. - fn replace_root_view( - &mut self, - build_view: impl FnOnce(&mut Window, &mut Context) -> V, - ) -> Self::Result> - where - V: 'static + Render; - - /// Focus a entity in the window, if it implements the [`Focusable`] trait. - fn focus(&mut self, entity: &Entity) -> Self::Result<()> - where - V: Focusable; -} - -/// A trait for tying together the types of a GPUI entity and the events it can -/// emit. -pub trait EventEmitter: 'static {} - -/// A helper trait for auto-implementing certain methods on contexts that -/// can be used interchangeably. -pub trait BorrowAppContext { - /// Set a global value on the context. - fn set_global(&mut self, global: T); - /// Updates the global state of the given type. - fn update_global(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R - where - G: Global; - /// Updates the global state of the given type, creating a default if it didn't exist before. - fn update_default_global(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R - where - G: Global + Default; -} - -impl BorrowAppContext for C -where - C: std::borrow::BorrowMut, -{ - fn set_global(&mut self, global: G) { - self.borrow_mut().set_global(global) - } - - #[track_caller] - fn update_global(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R - where - G: Global, - { - let mut global = self.borrow_mut().lease_global::(); - let result = f(&mut global, self); - self.borrow_mut().end_global_lease(global); - result - } - - fn update_default_global(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R - where - G: Global + Default, - { - self.borrow_mut().default_global::(); - self.update_global(f) - } -} - -/// Information about the GPU GPUI is running on. -#[derive(Default, Debug, serde::Serialize, serde::Deserialize, Clone)] -pub struct GpuSpecs { - /// Whether the GPU is really a fake (like `llvmpipe`) running on the CPU. - pub is_software_emulated: bool, - /// The name of the device, as reported by Vulkan. - pub device_name: String, - /// The name of the driver, as reported by Vulkan. - pub driver_name: String, - /// Further information about the driver, as reported by Vulkan. - pub driver_info: String, -} diff --git a/crates/gpui_pre/src/input.rs b/crates/gpui_pre/src/input.rs deleted file mode 100644 index 4167aa09..00000000 --- a/crates/gpui_pre/src/input.rs +++ /dev/null @@ -1,485 +0,0 @@ -use crate::{ - App, Bounds, ClipboardItem, Context, Entity, InputHandler, Pixels, TextInputConfiguration, - UTF16Selection, Window, -}; -use std::ops::Range; - -/// Implement this trait to allow views to handle textual input when implementing an editor, field, etc. -/// -/// Once your view implements this trait, you can use it to construct an [`ElementInputHandler`]. -/// This input handler can then be assigned during paint by calling [`Window::handle_input`]. -/// -/// See [`InputHandler`] for details on how to implement each method. -pub trait EntityInputHandler: 'static + Sized { - /// See [`InputHandler::text_for_range`] for details - fn text_for_range( - &mut self, - range: Range, - adjusted_range: &mut Option>, - window: &mut Window, - cx: &mut Context, - ) -> Option; - - /// See [`InputHandler::selected_text_range`] for details - fn selected_text_range( - &mut self, - ignore_disabled_input: bool, - window: &mut Window, - cx: &mut Context, - ) -> Option; - - /// See [`InputHandler::marked_text_range`] for details - fn marked_text_range( - &self, - window: &mut Window, - cx: &mut Context, - ) -> Option>; - - /// See [`InputHandler::unmark_text`] for details - fn unmark_text(&mut self, window: &mut Window, cx: &mut Context); - - /// See [`InputHandler::paste`] for details - fn paste(&mut self, item: ClipboardItem, window: &mut Window, cx: &mut Context) { - if let Some(text) = item.text() { - self.replace_text_in_range(None, &text, window, cx); - } - } - - /// See [`InputHandler::replace_text_in_range`] for details - fn replace_text_in_range( - &mut self, - range: Option>, - text: &str, - window: &mut Window, - cx: &mut Context, - ); - - /// See [`InputHandler::replace_and_mark_text_in_range`] for details - fn replace_and_mark_text_in_range( - &mut self, - range: Option>, - new_text: &str, - new_selected_range: Option>, - window: &mut Window, - cx: &mut Context, - ); - - /// See [`InputHandler::bounds_for_range`] for details - fn bounds_for_range( - &mut self, - range_utf16: Range, - element_bounds: Bounds, - window: &mut Window, - cx: &mut Context, - ) -> Option>; - - /// See [`InputHandler::character_index_for_point`] for details - fn character_index_for_point( - &mut self, - point: crate::Point, - window: &mut Window, - cx: &mut Context, - ) -> Option; - - /// See [`InputHandler::set_selected_text_range`] for details - fn set_selected_text_range( - &mut self, - _range_utf16: Range, - _window: &mut Window, - _cx: &mut Context, - ) { - } - - /// See [`InputHandler::text_length_utf16`] for details - fn text_length_utf16( - &mut self, - _window: &mut Window, - _cx: &mut Context, - ) -> Option { - None - } - - /// See [`InputHandler::accepts_text_input`] for details - fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context) -> bool { - true - } - - /// See [`InputHandler::text_input_configuration`] for details - fn text_input_configuration( - &mut self, - _window: &mut Window, - _cx: &mut Context, - ) -> TextInputConfiguration { - TextInputConfiguration::default() - } - - /// See [`InputHandler::text_input_editable_range`] for details - fn text_input_editable_range( - &mut self, - _window: &mut Window, - _cx: &mut Context, - ) -> Option> { - None - } -} - -/// The canonical implementation of [`crate::PlatformInputHandler`]. Call [`Window::handle_input`] -/// with an instance during your element's paint. -pub struct ElementInputHandler { - view: Entity, - element_bounds: Bounds, -} - -impl ElementInputHandler { - /// Used in [`Element::paint`][element_paint] with the element's bounds, a `Window`, and a `App` context. - /// - /// [element_paint]: crate::Element::paint - pub fn new(element_bounds: Bounds, view: Entity) -> Self { - ElementInputHandler { - view, - element_bounds, - } - } -} - -impl InputHandler for ElementInputHandler { - fn selected_text_range( - &mut self, - ignore_disabled_input: bool, - window: &mut Window, - cx: &mut App, - ) -> Option { - self.view.update(cx, |view, cx| { - view.selected_text_range(ignore_disabled_input, window, cx) - }) - } - - fn marked_text_range(&mut self, window: &mut Window, cx: &mut App) -> Option> { - self.view - .update(cx, |view, cx| view.marked_text_range(window, cx)) - } - - fn text_for_range( - &mut self, - range_utf16: Range, - adjusted_range: &mut Option>, - window: &mut Window, - cx: &mut App, - ) -> Option { - self.view.update(cx, |view, cx| { - view.text_for_range(range_utf16, adjusted_range, window, cx) - }) - } - - fn replace_text_in_range( - &mut self, - replacement_range: Option>, - text: &str, - window: &mut Window, - cx: &mut App, - ) { - self.view.update(cx, |view, cx| { - view.replace_text_in_range(replacement_range, text, window, cx) - }); - } - - fn replace_and_mark_text_in_range( - &mut self, - range_utf16: Option>, - new_text: &str, - new_selected_range: Option>, - window: &mut Window, - cx: &mut App, - ) { - self.view.update(cx, |view, cx| { - view.replace_and_mark_text_in_range( - range_utf16, - new_text, - new_selected_range, - window, - cx, - ) - }); - } - - fn unmark_text(&mut self, window: &mut Window, cx: &mut App) { - self.view - .update(cx, |view, cx| view.unmark_text(window, cx)); - } - - fn paste(&mut self, item: ClipboardItem, window: &mut Window, cx: &mut App) { - self.view - .update(cx, |view, cx| view.paste(item, window, cx)); - } - - fn bounds_for_range( - &mut self, - range_utf16: Range, - window: &mut Window, - cx: &mut App, - ) -> Option> { - self.view.update(cx, |view, cx| { - view.bounds_for_range(range_utf16, self.element_bounds, window, cx) - }) - } - - fn character_index_for_point( - &mut self, - point: crate::Point, - window: &mut Window, - cx: &mut App, - ) -> Option { - self.view.update(cx, |view, cx| { - view.character_index_for_point(point, window, cx) - }) - } - - fn set_selected_text_range( - &mut self, - range_utf16: Range, - window: &mut Window, - cx: &mut App, - ) { - self.view.update(cx, |view, cx| { - view.set_selected_text_range(range_utf16, window, cx) - }) - } - - fn element_bounds(&mut self, _window: &mut Window, _cx: &mut App) -> Option> { - Some(self.element_bounds) - } - - fn text_length_utf16(&mut self, window: &mut Window, cx: &mut App) -> Option { - self.view - .update(cx, |view, cx| view.text_length_utf16(window, cx)) - } - - fn accepts_text_input(&mut self, window: &mut Window, cx: &mut App) -> bool { - self.view - .update(cx, |view, cx| view.accepts_text_input(window, cx)) - } - - fn prefers_ime_for_printable_keys(&mut self, window: &mut Window, cx: &mut App) -> bool { - self.view - .update(cx, |view, cx| view.accepts_text_input(window, cx)) - } - - fn text_input_configuration( - &mut self, - window: &mut Window, - cx: &mut App, - ) -> TextInputConfiguration { - self.view - .update(cx, |view, cx| view.text_input_configuration(window, cx)) - } - - fn text_input_editable_range( - &mut self, - window: &mut Window, - cx: &mut App, - ) -> Option> { - self.view - .update(cx, |view, cx| view.text_input_editable_range(window, cx)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - AnyWindowHandle, AppContext as _, FocusHandle, InteractiveElement as _, IntoElement, - ParentElement as _, Render, Styled as _, TestAppContext, TextInputAction, - TextInputStateChange, canvas, div, - }; - - #[gpui::test] - fn text_input_configuration_and_focus_state_are_forwarded_on_change(cx: &mut TestAppContext) { - let custom = TextInputConfiguration { - autocorrect: true, - input_action: TextInputAction::Send, - ..Default::default() - }; - let window = cx.add_window({ - let custom = custom.clone(); - move |_, cx| ConfigurationTestView { - focus_handle: cx.focus_handle(), - configuration: custom, - } - }); - let view = window.root(cx).unwrap(); - let test_window = cx.test_window(window.into()); - let window = AnyWindowHandle::from(window); - let draw = |cx: &mut TestAppContext| { - cx.update_window(window, |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - }; - - // Nothing is focused, so the platform learns the default configuration. - draw(cx); - assert_eq!( - test_window.text_input_configurations(), - vec![TextInputConfiguration::default()] - ); - assert!(test_window.text_input_state_changes().is_empty()); - - // Focusing the view routes its configuration to the platform. - cx.update_window(window, |_, window, cx| { - let focus_handle = view.read(cx).focus_handle.clone(); - window.focus(&focus_handle, cx); - }) - .unwrap(); - draw(cx); - assert_eq!( - test_window.text_input_configurations(), - vec![TextInputConfiguration::default(), custom.clone()] - ); - assert_eq!( - test_window.text_input_state_changes(), - vec![TextInputStateChange::FocusGained] - ); - - // Redrawing without a change forwards nothing. - draw(cx); - assert_eq!(test_window.text_input_configurations().len(), 2); - assert_eq!(test_window.text_input_state_changes().len(), 1); - - // Changing the configuration forwards the new value. - let updated = TextInputConfiguration { - suggestions: true, - ..custom - }; - view.update(cx, { - let updated = updated.clone(); - |view, cx| { - view.configuration = updated; - cx.notify(); - } - }); - draw(cx); - assert_eq!( - test_window.text_input_configurations().last(), - Some(&updated) - ); - assert_eq!(test_window.text_input_configurations().len(), 3); - assert_eq!(test_window.text_input_state_changes().len(), 1); - - // Losing focus reverts the platform to the default configuration. - cx.update_window(window, |_, window, cx| window.blur(cx)) - .unwrap(); - draw(cx); - assert_eq!( - test_window.text_input_configurations().last(), - Some(&TextInputConfiguration::default()) - ); - assert_eq!(test_window.text_input_configurations().len(), 4); - assert_eq!( - test_window.text_input_state_changes(), - vec![ - TextInputStateChange::FocusGained, - TextInputStateChange::FocusLost - ] - ); - } - - struct ConfigurationTestView { - focus_handle: FocusHandle, - configuration: TextInputConfiguration, - } - - impl Render for ConfigurationTestView { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let view = cx.entity(); - let focus_handle = self.focus_handle.clone(); - div().size_full().track_focus(&self.focus_handle).child( - canvas( - |_, _, _| {}, - move |bounds, _, window, cx| { - window.handle_input( - &focus_handle, - ElementInputHandler::new(bounds, view), - cx, - ); - }, - ) - .size_full(), - ) - } - } - - impl EntityInputHandler for ConfigurationTestView { - fn text_for_range( - &mut self, - _range: std::ops::Range, - _adjusted_range: &mut Option>, - _window: &mut Window, - _cx: &mut Context, - ) -> Option { - None - } - - fn selected_text_range( - &mut self, - _ignore_disabled_input: bool, - _window: &mut Window, - _cx: &mut Context, - ) -> Option { - None - } - - fn marked_text_range( - &self, - _window: &mut Window, - _cx: &mut Context, - ) -> Option> { - None - } - - fn unmark_text(&mut self, _window: &mut Window, _cx: &mut Context) {} - - fn replace_text_in_range( - &mut self, - _range: Option>, - _text: &str, - _window: &mut Window, - _cx: &mut Context, - ) { - } - - fn replace_and_mark_text_in_range( - &mut self, - _range: Option>, - _new_text: &str, - _new_selected_range: Option>, - _window: &mut Window, - _cx: &mut Context, - ) { - } - - fn bounds_for_range( - &mut self, - _range_utf16: std::ops::Range, - _element_bounds: Bounds, - _window: &mut Window, - _cx: &mut Context, - ) -> Option> { - None - } - - fn character_index_for_point( - &mut self, - _point: crate::Point, - _window: &mut Window, - _cx: &mut Context, - ) -> Option { - None - } - - fn text_input_configuration( - &mut self, - _window: &mut Window, - _cx: &mut Context, - ) -> TextInputConfiguration { - self.configuration.clone() - } - } -} diff --git a/crates/gpui_pre/src/inspector.rs b/crates/gpui_pre/src/inspector.rs deleted file mode 100644 index 12995f03..00000000 --- a/crates/gpui_pre/src/inspector.rs +++ /dev/null @@ -1,254 +0,0 @@ -/// A unique identifier for an element that can be inspected. -#[derive(Debug, Eq, PartialEq, Hash, Clone)] -pub struct InspectorElementId { - /// Stable part of the ID. - #[cfg(any(feature = "inspector", debug_assertions))] - pub path: std::rc::Rc, - /// Disambiguates elements that have the same path. - #[cfg(any(feature = "inspector", debug_assertions))] - pub instance_id: usize, -} - -impl Into for &InspectorElementId { - fn into(self) -> InspectorElementId { - self.clone() - } -} - -#[cfg(any(feature = "inspector", debug_assertions))] -pub use conditional::*; - -#[cfg(any(feature = "inspector", debug_assertions))] -mod conditional { - use super::*; - use crate::{AnyElement, App, Context, Empty, IntoElement, Render, Window}; - use collections::{FxHashMap, TypeIdHashMap}; - use std::any::{Any, TypeId}; - - /// `GlobalElementId` qualified by source location of element construction. - #[derive(Debug, Eq, PartialEq, Hash)] - pub struct InspectorElementPath { - /// The path to the nearest ancestor element that has an `ElementId`. - #[cfg(any(feature = "inspector", debug_assertions))] - pub global_id: crate::GlobalElementId, - /// Source location where this element was constructed. - #[cfg(any(feature = "inspector", debug_assertions))] - pub source_location: &'static std::panic::Location<'static>, - } - - impl Clone for InspectorElementPath { - fn clone(&self) -> Self { - Self { - global_id: self.global_id.clone(), - source_location: self.source_location, - } - } - } - - impl Into for &InspectorElementPath { - fn into(self) -> InspectorElementPath { - self.clone() - } - } - - /// Function set on `App` to render the inspector UI. - pub type InspectorRenderer = - Box) -> AnyElement>; - - /// Manages inspector state - which element is currently selected and whether the inspector is - /// in picking mode. - pub struct Inspector { - active_element: Option, - pub(crate) pick_depth: Option, - } - - struct InspectedElement { - id: InspectorElementId, - states: TypeIdHashMap>, - } - - impl InspectedElement { - fn new(id: InspectorElementId) -> Self { - InspectedElement { - id, - states: Default::default(), - } - } - } - - impl Inspector { - pub(crate) fn new() -> Self { - Self { - active_element: None, - pick_depth: Some(0.0), - } - } - - pub(crate) fn select(&mut self, id: InspectorElementId, window: &mut Window) { - self.set_active_element_id(id, window); - self.pick_depth = None; - } - - pub(crate) fn hover(&mut self, id: InspectorElementId, window: &mut Window) { - if self.is_picking() { - let changed = self.set_active_element_id(id, window); - if changed { - self.pick_depth = Some(0.0); - } - } - } - - pub(crate) fn set_active_element_id( - &mut self, - id: InspectorElementId, - window: &mut Window, - ) -> bool { - let changed = Some(&id) != self.active_element_id(); - if changed { - self.active_element = Some(InspectedElement::new(id)); - window.refresh(); - } - changed - } - - /// ID of the currently hovered or selected element. - pub fn active_element_id(&self) -> Option<&InspectorElementId> { - self.active_element.as_ref().map(|e| &e.id) - } - - pub(crate) fn with_active_element_state( - &mut self, - window: &mut Window, - f: impl FnOnce(&mut Option, &mut Window) -> R, - ) -> R { - let Some(active_element) = &mut self.active_element else { - return f(&mut None, window); - }; - - let type_id = TypeId::of::(); - let mut inspector_state = active_element - .states - .remove(&type_id) - .map(|state| *state.downcast().unwrap()); - - let result = f(&mut inspector_state, window); - - if let Some(inspector_state) = inspector_state { - active_element - .states - .insert(type_id, Box::new(inspector_state)); - } - - result - } - - /// Starts element picking mode, allowing the user to select elements by clicking. - pub fn start_picking(&mut self) { - self.pick_depth = Some(0.0); - } - - /// Returns whether the inspector is currently in picking mode. - pub fn is_picking(&self) -> bool { - self.pick_depth.is_some() - } - - /// Renders elements for all registered inspector states of the active inspector element. - pub fn render_inspector_states( - &mut self, - window: &mut Window, - cx: &mut Context, - ) -> Vec { - let mut elements = Vec::new(); - if let Some(active_element) = self.active_element.take() { - for (type_id, state) in &active_element.states { - if let Some(render_inspector) = cx - .inspector_element_registry - .renderers_by_type_id - .remove(type_id) - { - let mut element = (render_inspector)( - active_element.id.clone(), - state.as_ref(), - window, - cx, - ); - elements.push(element); - cx.inspector_element_registry - .renderers_by_type_id - .insert(*type_id, render_inspector); - } - } - - self.active_element = Some(active_element); - } - - elements - } - } - - impl Render for Inspector { - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - if let Some(inspector_renderer) = cx.inspector_renderer.take() { - let result = inspector_renderer(self, window, cx); - cx.inspector_renderer = Some(inspector_renderer); - result - } else { - Empty.into_any_element() - } - } - } - - #[derive(Default)] - pub(crate) struct InspectorElementRegistry { - renderers_by_type_id: FxHashMap< - TypeId, - Box AnyElement>, - >, - } - - impl InspectorElementRegistry { - pub fn register( - &mut self, - f: impl 'static + Fn(InspectorElementId, &T, &mut Window, &mut App) -> R, - ) { - self.renderers_by_type_id.insert( - TypeId::of::(), - Box::new(move |id, value, window, cx| { - let value = value.downcast_ref().unwrap(); - f(id, value, window, cx).into_any_element() - }), - ); - } - } -} - -/// Provides definitions used by `#[derive_inspector_reflection]`. -#[cfg(any(feature = "inspector", debug_assertions))] -pub mod inspector_reflection { - use std::any::Any; - - /// Reification of a function that has the signature `fn some_fn(T) -> T`. Provides the name, - /// documentation, and ability to invoke the function. - #[derive(Clone, Copy)] - pub struct FunctionReflection { - /// The name of the function - pub name: &'static str, - /// The method - pub function: fn(Box) -> Box, - /// Documentation for the function - pub documentation: Option<&'static str>, - /// `PhantomData` for the type of the argument and result - pub _type: std::marker::PhantomData, - } - - impl FunctionReflection { - /// Invoke this method on a value and return the result. - pub fn invoke(&self, value: T) -> T { - let boxed = Box::new(value) as Box; - let result = (self.function)(boxed); - *result - .downcast::() - .expect("Type mismatch in reflection invoke") - } - } -} diff --git a/crates/gpui_pre/src/interactive.rs b/crates/gpui_pre/src/interactive.rs deleted file mode 100644 index 913cf1ba..00000000 --- a/crates/gpui_pre/src/interactive.rs +++ /dev/null @@ -1,971 +0,0 @@ -use crate::{ - Bounds, Capslock, Context, Empty, IntoElement, Keystroke, LongPressEvent, Modifiers, Pixels, - Point, Render, TouchDragEvent, Window, point, seal::Sealed, -}; -use smallvec::SmallVec; -use std::{any::Any, fmt::Debug, ops::Deref, path::PathBuf}; - -/// An event from a platform input source. -pub trait InputEvent: Sealed + 'static { - /// Convert this event into the platform input enum. - fn to_platform_input(self) -> PlatformInput; -} - -/// A key event from the platform. -pub trait KeyEvent: InputEvent {} - -/// A mouse event from the platform. -pub trait MouseEvent: InputEvent {} - -/// A gesture event from the platform. -pub trait GestureEvent: InputEvent {} - -/// The key down event equivalent for the platform. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct KeyDownEvent { - /// The keystroke that was generated. - pub keystroke: Keystroke, - - /// Whether the key is currently held down. - pub is_held: bool, - - /// Whether to prefer character input over keybindings for this keystroke. - /// In some cases, like AltGr on Windows, modifiers are significant for character input. - pub prefer_character_input: bool, -} - -impl Sealed for KeyDownEvent {} -impl InputEvent for KeyDownEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::KeyDown(self) - } -} -impl KeyEvent for KeyDownEvent {} - -/// The key up event equivalent for the platform. -#[derive(Clone, Debug)] -pub struct KeyUpEvent { - /// The keystroke that was released. - pub keystroke: Keystroke, -} - -impl Sealed for KeyUpEvent {} -impl InputEvent for KeyUpEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::KeyUp(self) - } -} -impl KeyEvent for KeyUpEvent {} - -/// The modifiers changed event equivalent for the platform. -#[derive(Clone, Debug, Default)] -pub struct ModifiersChangedEvent { - /// The new state of the modifier keys - pub modifiers: Modifiers, - /// The new state of the capslock key - pub capslock: Capslock, -} - -impl Sealed for ModifiersChangedEvent {} -impl InputEvent for ModifiersChangedEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::ModifiersChanged(self) - } -} -impl KeyEvent for ModifiersChangedEvent {} - -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, Default, PartialEq, Eq)] -pub enum TouchPhase { - /// The touch started. - Started, - /// The touch event is moving. - #[default] - Moved, - /// The touch phase has ended - Ended, - /// The touch was cancelled: the system took it and it will not end - /// normally. Consumers must fully unwind any in-progress interaction, - /// treating the touch as if it never committed. - Cancelled, -} - -/// Identifies one touch (finger or stylus contact) for its lifetime, from -/// [`TouchPhase::Started`] through [`TouchPhase::Ended`] or -/// [`TouchPhase::Cancelled`]. -/// -/// The value is opaque and assigned by the platform. A platform window must -/// not reuse an identifier for a later touch. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct TouchId(pub u64); - -/// A raw touch event from the platform. -/// -/// -/// Dispatch contract (core implementation pending): a touch is hit-tested -/// once, at [`TouchPhase::Started`], occlusion-aware; all subsequent events -/// for the same [`TouchId`] are delivered to the elements under the starting -/// position, even after the touch moves outside them. -#[derive(Clone, Debug, Default)] -pub struct TouchEvent { - /// Which touch this event belongs to. - pub id: TouchId, - /// The phase of the touch. - pub phase: TouchPhase, - /// The position of the touch in window coordinates. - pub position: Point, - /// Where the platform predicts the touch will be roughly one frame from - /// now, in the same coordinate space as `position`, when the platform - /// offers a prediction for a [`TouchPhase::Moved`] event. - /// - /// Best-effort latency compensation only: it may influence how far a - /// recognized pan scrolls within a frame, but never hit testing, gesture - /// classification, or velocity estimation, and any error it introduces - /// must be corrected by later events for the same touch. - pub predicted_position: Option>, - /// Normalized touch force in `0.0..=1.0`, if the hardware reports it. - pub force: Option, -} - -impl Sealed for TouchEvent {} -impl InputEvent for TouchEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::Touch(self) - } -} - -/// A mouse down event from the platform -#[derive(Clone, Debug, Default)] -pub struct MouseDownEvent { - /// Which mouse button was pressed. - pub button: MouseButton, - - /// The position of the mouse on the window. - pub position: Point, - - /// The modifiers that were held down when the mouse was pressed. - pub modifiers: Modifiers, - - /// The number of times the button has been clicked. - pub click_count: usize, - - /// Whether this is the first, focusing click. - pub first_mouse: bool, -} - -impl Sealed for MouseDownEvent {} -impl InputEvent for MouseDownEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::MouseDown(self) - } -} -impl MouseEvent for MouseDownEvent {} - -impl MouseDownEvent { - /// Returns true if this mouse up event should focus the element. - pub fn is_focusing(&self) -> bool { - match self.button { - MouseButton::Left => true, - _ => false, - } - } -} - -/// A mouse up event from the platform -#[derive(Clone, Debug, Default)] -pub struct MouseUpEvent { - /// Which mouse button was released. - pub button: MouseButton, - - /// The position of the mouse on the window. - pub position: Point, - - /// The modifiers that were held down when the mouse was released. - pub modifiers: Modifiers, - - /// The number of times the button has been clicked. - pub click_count: usize, -} - -impl Sealed for MouseUpEvent {} -impl InputEvent for MouseUpEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::MouseUp(self) - } -} - -impl MouseEvent for MouseUpEvent {} - -impl MouseUpEvent { - /// Returns true if this mouse up event should focus the element. - pub fn is_focusing(&self) -> bool { - match self.button { - MouseButton::Left => true, - _ => false, - } - } -} - -/// A click event, generated when a mouse button is pressed and released. -#[derive(Clone, Debug, Default)] -pub struct MouseClickEvent { - /// The mouse event when the button was pressed. - pub down: MouseDownEvent, - - /// The mouse event when the button was released. - pub up: MouseUpEvent, -} - -/// The stage of a pressure click event. -#[derive(Clone, Copy, Debug, Default, PartialEq)] -pub enum PressureStage { - /// No pressure. - #[default] - Zero, - /// Normal click pressure. - Normal, - /// High pressure, enough to trigger a force click. - Force, -} - -/// A mouse pressure event from the platform. Generated when a force-sensitive trackpad is pressed hard. -/// Currently only implemented for macOS trackpads. -#[derive(Debug, Clone, Default)] -pub struct MousePressureEvent { - /// Pressure of the current stage as a float between 0 and 1 - pub pressure: f32, - /// The pressure stage of the event. - pub stage: PressureStage, - /// The position of the mouse on the window. - pub position: Point, - /// The modifiers that were held down when the mouse pressure changed. - pub modifiers: Modifiers, -} - -impl Sealed for MousePressureEvent {} -impl InputEvent for MousePressureEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::MousePressure(self) - } -} -impl MouseEvent for MousePressureEvent {} - -/// A click event that was generated by a keyboard button being pressed and released. -#[derive(Clone, Debug, Default)] -pub struct KeyboardClickEvent { - /// The keyboard button that was pressed to trigger the click. - pub button: KeyboardButton, - - /// The bounds of the element that was clicked. - pub bounds: Bounds, -} - -/// A click event that was generated by a recognized tap gesture on a touch -/// screen. -#[derive(Clone, Debug, Default)] -pub struct TouchClickEvent { - /// The position of the tap in window coordinates. - pub position: Point, - /// The number of consecutive taps at this location (double tap = 2), - /// analogous to the mouse `click_count`. - pub tap_count: usize, - /// Whether this was a long press rather than a tap. Long presses are - /// touch's secondary activation: they are delivered to aux-click - /// listeners alongside right clicks, not to primary click listeners. - pub long_press: bool, -} - -/// A click event, generated when a mouse button or keyboard button is pressed and released, -/// or when a tap gesture is recognized on a touch screen. -#[derive(Clone, Debug)] -pub enum ClickEvent { - /// A click event trigger by a mouse button being pressed and released. - Mouse(MouseClickEvent), - /// A click event trigger by a keyboard button being pressed and released. - Keyboard(KeyboardClickEvent), - /// A click event triggered by a recognized tap gesture on a touch screen. - Touch(TouchClickEvent), -} - -impl Default for ClickEvent { - fn default() -> Self { - ClickEvent::Keyboard(KeyboardClickEvent::default()) - } -} - -impl ClickEvent { - /// Returns the modifiers that were held during the click event - /// - /// `Keyboard`: The keyboard click events never have modifiers. - /// `Mouse`: Modifiers that were held during the mouse key up event. - pub fn modifiers(&self) -> Modifiers { - match self { - // Click events are only generated from keyboard events _without any modifiers_, so we know the modifiers are always Default - ClickEvent::Keyboard(_) => Modifiers::default(), - // Click events on the web only reflect the modifiers for the keyup event, - // tested via observing the behavior of the `ClickEvent.shiftKey` field in Chrome 138 - // under various combinations of modifiers and keyUp / keyDown events. - ClickEvent::Mouse(event) => event.up.modifiers, - // Touch screens have no modifier keys. - ClickEvent::Touch(_) => Modifiers::default(), - } - } - - /// Returns the position of the click event - /// - /// `Keyboard`: The bottom left corner of the clicked hitbox - /// `Mouse`: The position of the mouse when the button was released. - /// `Touch`: The position of the tap. - pub fn position(&self) -> Point { - match self { - ClickEvent::Keyboard(event) => event.bounds.bottom_left(), - ClickEvent::Mouse(event) => event.up.position, - ClickEvent::Touch(event) => event.position, - } - } - - /// Returns the mouse position of the click event - /// - /// `Keyboard`: None - /// `Mouse`: The position of the mouse when the button was released. - /// `Touch`: None, touches are not mouse input and there is no cursor. - pub fn mouse_position(&self) -> Option> { - match self { - ClickEvent::Keyboard(_) => None, - ClickEvent::Mouse(event) => Some(event.up.position), - ClickEvent::Touch(_) => None, - } - } - - /// Returns if this was a right click - /// - /// `Keyboard`: false - /// `Mouse`: Whether the right button was pressed and released - pub fn is_right_click(&self) -> bool { - match self { - ClickEvent::Keyboard(_) => false, - ClickEvent::Mouse(event) => { - event.down.button == MouseButton::Right && event.up.button == MouseButton::Right - } - ClickEvent::Touch(_) => false, - } - } - - /// Returns if this was a middle click - /// - /// `Keyboard`: false - /// `Mouse`: Whether the middle button was pressed and released - pub fn is_middle_click(&self) -> bool { - match self { - ClickEvent::Keyboard(_) => false, - ClickEvent::Mouse(event) => { - event.down.button == MouseButton::Middle && event.up.button == MouseButton::Middle - } - ClickEvent::Touch(_) => false, - } - } - - /// Returns whether the click is a secondary activation, i.e. a context - /// menu trigger: a right click from a mouse (macOS ctrl-clicks arrive - /// already converted to right clicks by the platform layer), or a long - /// press on a touch screen. - pub fn is_secondary(&self) -> bool { - match self { - ClickEvent::Keyboard(_) => false, - ClickEvent::Mouse(event) => { - event.down.button == MouseButton::Right && event.up.button == MouseButton::Right - } - ClickEvent::Touch(event) => event.long_press, - } - } - - /// Returns whether the click was a standard click - /// - /// `Keyboard`: Always true - /// `Mouse`: Left button pressed and released - /// `Touch`: A tap, but not a long press - pub fn standard_click(&self) -> bool { - match self { - ClickEvent::Keyboard(_) => true, - ClickEvent::Mouse(event) => { - event.down.button == MouseButton::Left && event.up.button == MouseButton::Left - } - ClickEvent::Touch(event) => !event.long_press, - } - } - - /// Returns whether the click focused the element - /// - /// `Keyboard`: false, keyboard clicks only work if an element is already focused - /// `Mouse`: Whether this was the first focusing click - /// `Touch`: false, mobile windows are already active when tappable - pub fn first_focus(&self) -> bool { - match self { - ClickEvent::Keyboard(_) => false, - ClickEvent::Mouse(event) => event.down.first_mouse, - ClickEvent::Touch(_) => false, - } - } - - /// Returns the click count of the click event - /// - /// `Keyboard`: Always 1 - /// `Mouse`: Count of clicks from MouseUpEvent - /// `Touch`: Count of consecutive taps - pub fn click_count(&self) -> usize { - match self { - ClickEvent::Keyboard(_) => 1, - ClickEvent::Mouse(event) => event.up.click_count, - ClickEvent::Touch(event) => event.tap_count, - } - } - - /// Returns whether the click event is generated by a keyboard event - pub fn is_keyboard(&self) -> bool { - match self { - ClickEvent::Mouse(_) | ClickEvent::Touch(_) => false, - ClickEvent::Keyboard(_) => true, - } - } -} - -/// An enum representing the keyboard button that was pressed for a click event. -#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug, Default)] -pub enum KeyboardButton { - /// Enter key was clicked - #[default] - Enter, - /// Space key was clicked - Space, -} - -/// An enum representing the mouse button that was pressed. -#[derive(Hash, Default, PartialEq, Eq, Copy, Clone, Debug)] -pub enum MouseButton { - /// The left mouse button. - #[default] - Left, - - /// The right mouse button. - Right, - - /// The middle mouse button. - Middle, - - /// A navigation button, such as back or forward. - Navigate(NavigationDirection), -} - -impl MouseButton { - /// Get all the mouse buttons in a list. - pub fn all() -> Vec { - vec![ - MouseButton::Left, - MouseButton::Right, - MouseButton::Middle, - MouseButton::Navigate(NavigationDirection::Back), - MouseButton::Navigate(NavigationDirection::Forward), - ] - } -} - -/// A navigation direction, such as back or forward. -#[derive(Hash, Default, PartialEq, Eq, Copy, Clone, Debug)] -pub enum NavigationDirection { - /// The back button. - #[default] - Back, - - /// The forward button. - Forward, -} - -/// A mouse move event from the platform. -#[derive(Clone, Debug, Default)] -pub struct MouseMoveEvent { - /// The position of the mouse on the window. - pub position: Point, - - /// The mouse button that was pressed, if any. - pub pressed_button: Option, - - /// The modifiers that were held down when the mouse was moved. - pub modifiers: Modifiers, -} - -impl Sealed for MouseMoveEvent {} -impl InputEvent for MouseMoveEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::MouseMove(self) - } -} -impl MouseEvent for MouseMoveEvent {} - -impl MouseMoveEvent { - /// Returns true if the left mouse button is currently held down. - pub fn dragging(&self) -> bool { - self.pressed_button == Some(MouseButton::Left) - } -} - -/// A mouse wheel event from the platform. -#[derive(Clone, Debug, Default)] -pub struct ScrollWheelEvent { - /// The position of the mouse on the window. - pub position: Point, - - /// The change in scroll wheel position for this event. - pub delta: ScrollDelta, - - /// The modifiers that were held down when the mouse was moved. - pub modifiers: Modifiers, - - /// The phase of the touch event. - pub touch_phase: TouchPhase, -} - -impl Sealed for ScrollWheelEvent {} -impl InputEvent for ScrollWheelEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::ScrollWheel(self) - } -} -impl MouseEvent for ScrollWheelEvent {} - -impl Deref for ScrollWheelEvent { - type Target = Modifiers; - - fn deref(&self) -> &Self::Target { - &self.modifiers - } -} - -/// The scroll delta for a scroll wheel event. -#[derive(Clone, Copy, Debug)] -pub enum ScrollDelta { - /// An exact scroll delta in pixels. - Pixels(Point), - /// An inexact scroll delta in lines. - Lines(Point), -} - -impl Default for ScrollDelta { - fn default() -> Self { - Self::Lines(Default::default()) - } -} - -/// A pinch gesture event from the platform, generated when the user performs -/// a pinch-to-zoom gesture (typically on a trackpad). -/// -#[derive(Clone, Debug, Default)] -pub struct PinchEvent { - /// The position of the pinch center on the window. - pub position: Point, - - /// The zoom delta for this event. - /// Positive values indicate zooming in, negative values indicate zooming out. - /// For example, 0.1 represents a 10% zoom increase. - pub delta: f32, - - /// The modifiers that were held down during the pinch gesture. - pub modifiers: Modifiers, - - /// The phase of the pinch gesture. - pub phase: TouchPhase, -} - -impl Sealed for PinchEvent {} -impl InputEvent for PinchEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::Pinch(self) - } -} -impl GestureEvent for PinchEvent {} -impl MouseEvent for PinchEvent {} - -impl Deref for PinchEvent { - type Target = Modifiers; - - fn deref(&self) -> &Self::Target { - &self.modifiers - } -} - -impl ScrollDelta { - /// Returns true if this is a precise scroll delta in pixels. - pub fn precise(&self) -> bool { - match self { - ScrollDelta::Pixels(_) => true, - ScrollDelta::Lines(_) => false, - } - } - - /// Converts this scroll event into exact pixels. - pub fn pixel_delta(&self, line_height: Pixels) -> Point { - match self { - ScrollDelta::Pixels(delta) => *delta, - ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y), - } - } - - /// Combines two scroll deltas into one. - /// If the signs of the deltas are the same (both positive or both negative), - /// the deltas are added together. If the signs are opposite, the second delta - /// (other) is used, effectively overriding the first delta. - pub fn coalesce(self, other: ScrollDelta) -> ScrollDelta { - match (self, other) { - (ScrollDelta::Pixels(a), ScrollDelta::Pixels(b)) => { - let x = if a.x.signum() == b.x.signum() { - a.x + b.x - } else { - b.x - }; - - let y = if a.y.signum() == b.y.signum() { - a.y + b.y - } else { - b.y - }; - - ScrollDelta::Pixels(point(x, y)) - } - - (ScrollDelta::Lines(a), ScrollDelta::Lines(b)) => { - let x = if a.x.signum() == b.x.signum() { - a.x + b.x - } else { - b.x - }; - - let y = if a.y.signum() == b.y.signum() { - a.y + b.y - } else { - b.y - }; - - ScrollDelta::Lines(point(x, y)) - } - - _ => other, - } - } -} - -/// A mouse exit event from the platform, generated when the mouse leaves the window. -#[derive(Clone, Debug, Default)] -pub struct MouseExitEvent { - /// The position of the mouse relative to the window. - pub position: Point, - /// The mouse button that was pressed, if any. - pub pressed_button: Option, - /// The modifiers that were held down when the mouse was moved. - pub modifiers: Modifiers, -} - -impl Sealed for MouseExitEvent {} -impl InputEvent for MouseExitEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::MouseExited(self) - } -} - -impl MouseEvent for MouseExitEvent {} - -impl Deref for MouseExitEvent { - type Target = Modifiers; - - fn deref(&self) -> &Self::Target { - &self.modifiers - } -} - -/// A collection of paths from the platform, such as from a file drop. -#[derive(Debug, Clone, Default, Eq, PartialEq)] -pub struct ExternalPaths(pub SmallVec<[PathBuf; 2]>); - -impl ExternalPaths { - /// Convert this collection of paths into a slice. - pub fn paths(&self) -> &[PathBuf] { - &self.0 - } -} - -/// Data offered to the platform when an internal drag leaves the window and is -/// promoted to a native drag session. -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum ExternalDragPayload { - /// Real on-disk paths, handed to the platform as an outbound file drag. - Files(FileDragPaths), -} - -/// Paths handed to the platform for a native file drag. Directory metadata is -/// provided by the caller to avoid querying it when the platform drag starts. -#[derive(Debug, Clone, Default, Eq, PartialEq)] -pub struct FileDragPaths(SmallVec<[(PathBuf, bool); 2]>); - -impl FileDragPaths { - /// Creates a native file-drag payload from paths paired with whether each path is a directory. - pub fn new(entries: impl IntoIterator) -> Self { - Self(entries.into_iter().collect()) - } - - /// The dragged paths, each paired with whether it is a directory. - pub fn entries(&self) -> &[(PathBuf, bool)] { - &self.0 - } -} - -impl Render for ExternalPaths { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - // the platform will render icons for the dragged files - Empty - } -} - -/// A file drop event from the platform, generated when files are dragged and dropped onto the window. -#[derive(Debug, Clone)] -pub enum FileDropEvent { - /// The files have entered the window. - Entered { - /// The position of the mouse relative to the window. - position: Point, - /// The paths of the files that are being dragged. - paths: ExternalPaths, - }, - /// The files are being dragged over the window - Pending { - /// The position of the mouse relative to the window. - position: Point, - }, - /// The files have been dropped onto the window. - Submit { - /// The position of the mouse relative to the window. - position: Point, - }, - /// The user has stopped dragging the files over the window. - Exited, - /// The platform-owned drag session has ended. - Ended, -} - -impl Sealed for FileDropEvent {} -impl InputEvent for FileDropEvent { - fn to_platform_input(self) -> PlatformInput { - PlatformInput::FileDrop(self) - } -} -impl MouseEvent for FileDropEvent {} - -/// An enum corresponding to all kinds of platform input events. -#[derive(Clone, Debug)] -pub enum PlatformInput { - /// A key was pressed. - KeyDown(KeyDownEvent), - /// A key was released. - KeyUp(KeyUpEvent), - /// The keyboard modifiers were changed. - ModifiersChanged(ModifiersChangedEvent), - /// The mouse was pressed. - MouseDown(MouseDownEvent), - /// The mouse was released. - MouseUp(MouseUpEvent), - /// Mouse pressure. - MousePressure(MousePressureEvent), - /// The mouse was moved. - MouseMove(MouseMoveEvent), - /// The mouse exited the window. - MouseExited(MouseExitEvent), - /// The scroll wheel was used. - ScrollWheel(ScrollWheelEvent), - /// A pinch gesture was performed. - Pinch(PinchEvent), - /// A long-press gesture recognized from touch input. - LongPress(LongPressEvent), - /// A direct touch drag claimed by an element. - TouchDrag(TouchDragEvent), - /// Files were dragged and dropped onto the window. - FileDrop(FileDropEvent), - /// A raw touch event on a touch screen. - Touch(TouchEvent), -} - -impl PlatformInput { - pub(crate) fn mouse_event(&self) -> Option<&dyn Any> { - match self { - PlatformInput::KeyDown { .. } => None, - PlatformInput::KeyUp { .. } => None, - PlatformInput::ModifiersChanged { .. } => None, - PlatformInput::MouseDown(event) => Some(event), - PlatformInput::MouseUp(event) => Some(event), - PlatformInput::MouseMove(event) => Some(event), - PlatformInput::MousePressure(event) => Some(event), - PlatformInput::MouseExited(event) => Some(event), - PlatformInput::ScrollWheel(event) => Some(event), - PlatformInput::Pinch(event) => Some(event), - PlatformInput::LongPress(event) => Some(event), - PlatformInput::TouchDrag(event) => Some(event), - PlatformInput::FileDrop(event) => Some(event), - PlatformInput::Touch(_) => None, - } - } - - pub(crate) fn keyboard_event(&self) -> Option<&dyn Any> { - match self { - PlatformInput::KeyDown(event) => Some(event), - PlatformInput::KeyUp(event) => Some(event), - PlatformInput::ModifiersChanged(event) => Some(event), - PlatformInput::MouseDown(_) => None, - PlatformInput::MouseUp(_) => None, - PlatformInput::MouseMove(_) => None, - PlatformInput::MousePressure(_) => None, - PlatformInput::MouseExited(_) => None, - PlatformInput::ScrollWheel(_) => None, - PlatformInput::Pinch(_) => None, - PlatformInput::LongPress(_) => None, - PlatformInput::TouchDrag(_) => None, - PlatformInput::FileDrop(_) => None, - PlatformInput::Touch(_) => None, - } - } - - /// A short static name for this input's variant, for diagnostics and - /// telemetry. - pub fn kind_name(&self) -> &'static str { - match self { - PlatformInput::KeyDown(_) => "key_down", - PlatformInput::KeyUp(_) => "key_up", - PlatformInput::ModifiersChanged(_) => "modifiers_changed", - PlatformInput::MouseDown(_) => "mouse_down", - PlatformInput::MouseUp(_) => "mouse_up", - PlatformInput::MousePressure(_) => "mouse_pressure", - PlatformInput::MouseMove(_) => "mouse_move", - PlatformInput::MouseExited(_) => "mouse_exited", - PlatformInput::ScrollWheel(_) => "scroll_wheel", - PlatformInput::Pinch(_) => "pinch", - PlatformInput::LongPress(_) => "long_press", - PlatformInput::TouchDrag(_) => "touch_drag", - PlatformInput::FileDrop(_) => "file_drop", - PlatformInput::Touch(_) => "touch", - } - } - - /// Returns the touch event contained in this input, if any. - pub fn touch_event(&self) -> Option<&TouchEvent> { - match self { - PlatformInput::Touch(event) => Some(event), - _ => None, - } - } -} - -#[cfg(test)] -mod test { - - use crate::{ - self as gpui, AppContext as _, Context, FocusHandle, InteractiveElement, IntoElement, - KeyBinding, Keystroke, Modifiers, ParentElement, Render, TestAppContext, Window, div, - }; - - struct TestView { - saw_key_down: bool, - saw_action: bool, - focus_handle: FocusHandle, - } - - actions!(test_only, [TestAction]); - - impl Render for TestView { - fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { - div().id("testview").child( - div() - .key_context("parent") - .on_key_down(cx.listener(|this, _, _, cx| { - cx.stop_propagation(); - this.saw_key_down = true - })) - .on_action(cx.listener(|this: &mut TestView, _: &TestAction, _, _| { - this.saw_action = true - })) - .child( - div() - .key_context("nested") - .track_focus(&self.focus_handle) - .into_element(), - ), - ) - } - } - - #[gpui::test] - fn test_on_events(cx: &mut TestAppContext) { - let window = cx.update(|cx| { - cx.open_window(Default::default(), |_, cx| { - cx.new(|cx| TestView { - saw_key_down: false, - saw_action: false, - focus_handle: cx.focus_handle(), - }) - }) - .unwrap() - }); - - cx.update(|cx| { - cx.bind_keys(vec![KeyBinding::new("ctrl-g", TestAction, Some("parent"))]); - }); - - window - .update(cx, |test_view, window, cx| { - window.focus(&test_view.focus_handle, cx) - }) - .unwrap(); - - cx.dispatch_keystroke(*window, Keystroke::parse("a").unwrap()); - cx.dispatch_keystroke(*window, Keystroke::parse("ctrl-g").unwrap()); - - window - .update(cx, |test_view, _, _| { - assert!(test_view.saw_key_down || test_view.saw_action); - assert!(test_view.saw_key_down); - assert!(test_view.saw_action); - }) - .unwrap(); - } - - #[gpui::test] - fn test_multi_modifier_gesture_does_not_dispatch_standalone_modifier_binding( - cx: &mut TestAppContext, - ) { - let (test_view, cx) = cx.add_window_view(|_, cx| TestView { - saw_key_down: false, - saw_action: false, - focus_handle: cx.focus_handle(), - }); - - cx.update(|_, cx| { - cx.bind_keys(vec![KeyBinding::new("shift", TestAction, None)]); - }); - test_view.update_in(cx, |test_view, window, cx| { - window.focus(&test_view.focus_handle, cx); - }); - - cx.simulate_modifiers_change(Modifiers::alt()); - cx.simulate_modifiers_change(Modifiers::alt() | Modifiers::shift()); - cx.simulate_modifiers_change(Modifiers::shift()); - cx.simulate_modifiers_change(Modifiers::none()); - assert!(!test_view.read_with(cx, |test_view, _| test_view.saw_action)); - - cx.simulate_modifiers_change(Modifiers::shift()); - cx.simulate_modifiers_change(Modifiers::none()); - assert!(test_view.read_with(cx, |test_view, _| test_view.saw_action)); - } -} diff --git a/crates/gpui_pre/src/key_dispatch.rs b/crates/gpui_pre/src/key_dispatch.rs deleted file mode 100644 index f18bd0e9..00000000 --- a/crates/gpui_pre/src/key_dispatch.rs +++ /dev/null @@ -1,1691 +0,0 @@ -//! KeyDispatch is where GPUI deals with binding actions to key events. -//! -//! The key pieces to making a key binding work are to define an action, -//! implement a method that takes that action as a type parameter, -//! and then to register the action during render on a focused node -//! with a keymap context: -//! -//! ```ignore -//! actions!(editor,[Undo, Redo]); -//! -//! impl Editor { -//! fn undo(&mut self, _: &Undo, _window: &mut Window, _cx: &mut Context) { ... } -//! fn redo(&mut self, _: &Redo, _window: &mut Window, _cx: &mut Context) { ... } -//! } -//! -//! impl Render for Editor { -//! fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { -//! div() -//! .track_focus(&self.focus_handle(cx)) -//! .key_context("Editor") -//! .on_action(cx.listener(Editor::undo)) -//! .on_action(cx.listener(Editor::redo)) -//! ... -//! } -//! } -//!``` -//! -//! The keybindings themselves are managed independently by calling cx.bind_keys(). -//! (Though mostly when developing Zed itself, you just need to add a new line to -//! assets/keymaps/default-{platform}.json). -//! -//! ```ignore -//! cx.bind_keys([ -//! KeyBinding::new("cmd-z", Editor::undo, Some("Editor")), -//! KeyBinding::new("cmd-shift-z", Editor::redo, Some("Editor")), -//! ]) -//! ``` -//! -//! With all of this in place, GPUI will ensure that if you have an Editor that contains -//! the focus, hitting cmd-z will Undo. -//! -//! In real apps, it is a little more complicated than this, because typically you have -//! several nested views that each register keyboard handlers. In this case action matching -//! bubbles up from the bottom. For example in Zed, the Workspace is the top-level view, which contains Pane's, which contain Editors. If there are conflicting keybindings defined -//! then the Editor's bindings take precedence over the Pane's bindings, which take precedence over the Workspace. -//! -//! In GPUI, keybindings are not limited to just single keystrokes, you can define -//! sequences by separating the keys with a space: -//! -//! KeyBinding::new("cmd-k left", pane::SplitLeft, Some("Pane")) - -use crate::{ - Action, ActionRegistry, App, DispatchPhase, EntityId, FocusId, KeyBinding, KeyContext, Keymap, - Keystroke, ModifiersChangedEvent, Window, -}; -use collections::FxHashMap; -use smallvec::SmallVec; -use std::{ - any::{Any, TypeId}, - cell::RefCell, - mem, - ops::Range, - rc::Rc, -}; - -/// ID of a node within `DispatchTree`. Note that these are **not** stable between frames, and so a -/// `DispatchNodeId` should only be used with the `DispatchTree` that provided it. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -pub(crate) struct DispatchNodeId(usize); - -pub(crate) struct DispatchTree { - node_stack: Vec, - pub(crate) context_stack: Vec, - view_stack: Vec, - nodes: Vec, - focusable_node_ids: FxHashMap, - view_node_ids: FxHashMap, - keymap: Rc>, - action_registry: Rc, -} - -#[derive(Default)] -pub(crate) struct DispatchNode { - pub key_listeners: Vec, - pub action_listeners: Vec, - pub modifiers_changed_listeners: Vec, - pub context: Option, - pub focus_id: Option, - view_id: Option, - parent: Option, -} - -pub(crate) struct ReusedSubtree { - old_range: Range, - new_range: Range, - contains_focus: bool, -} - -impl ReusedSubtree { - pub fn refresh_node_id(&self, node_id: DispatchNodeId) -> DispatchNodeId { - debug_assert!( - self.old_range.contains(&node_id.0), - "node {} was not part of the reused subtree {:?}", - node_id.0, - self.old_range - ); - DispatchNodeId((node_id.0 - self.old_range.start) + self.new_range.start) - } - - pub fn contains_focus(&self) -> bool { - self.contains_focus - } -} - -#[derive(Default, Debug)] -pub(crate) struct Replay { - pub(crate) keystroke: Keystroke, - pub(crate) bindings: SmallVec<[KeyBinding; 1]>, -} - -#[derive(Default, Debug)] -pub(crate) struct DispatchResult { - pub(crate) pending: SmallVec<[Keystroke; 1]>, - pub(crate) pending_has_binding: bool, - pub(crate) bindings: SmallVec<[KeyBinding; 1]>, - pub(crate) to_replay: SmallVec<[Replay; 1]>, - pub(crate) context_stack: Vec, -} - -type KeyListener = Rc; -type ModifiersChangedListener = Rc; - -#[derive(Clone)] -pub(crate) struct DispatchActionListener { - pub(crate) action_type: TypeId, - pub(crate) listener: Rc, -} - -impl DispatchTree { - pub fn new(keymap: Rc>, action_registry: Rc) -> Self { - Self { - node_stack: Vec::new(), - context_stack: Vec::new(), - view_stack: Vec::new(), - nodes: Vec::new(), - focusable_node_ids: FxHashMap::default(), - view_node_ids: FxHashMap::default(), - keymap, - action_registry, - } - } - - pub fn clear(&mut self) { - self.node_stack.clear(); - self.context_stack.clear(); - self.view_stack.clear(); - self.nodes.clear(); - self.focusable_node_ids.clear(); - self.view_node_ids.clear(); - } - - pub fn len(&self) -> usize { - self.nodes.len() - } - - pub fn push_node(&mut self) -> DispatchNodeId { - let parent = self.node_stack.last().copied(); - let node_id = DispatchNodeId(self.nodes.len()); - - self.nodes.push(DispatchNode { - parent, - ..Default::default() - }); - self.node_stack.push(node_id); - node_id - } - - pub fn set_active_node(&mut self, node_id: DispatchNodeId) { - let next_node_parent = self.nodes[node_id.0].parent; - while self.node_stack.last().copied() != next_node_parent && !self.node_stack.is_empty() { - self.pop_node(); - } - - if self.node_stack.last().copied() == next_node_parent { - self.node_stack.push(node_id); - let active_node = &self.nodes[node_id.0]; - if let Some(view_id) = active_node.view_id { - self.view_stack.push(view_id) - } - if let Some(context) = active_node.context.clone() { - self.context_stack.push(context); - } - } else { - debug_assert_eq!(self.node_stack.len(), 0); - - let mut current_node_id = Some(node_id); - while let Some(node_id) = current_node_id { - let node = &self.nodes[node_id.0]; - if let Some(context) = node.context.clone() { - self.context_stack.push(context); - } - if let Some(view_id) = node.view_id { - self.view_stack.push(view_id); - } - self.node_stack.push(node_id); - current_node_id = node.parent; - } - - self.context_stack.reverse(); - self.view_stack.reverse(); - self.node_stack.reverse(); - } - } - - pub fn set_key_context(&mut self, context: KeyContext) { - self.active_node().context = Some(context.clone()); - self.context_stack.push(context); - } - - pub fn set_focus_id(&mut self, focus_id: FocusId) { - let node_id = *self.node_stack.last().unwrap(); - self.nodes[node_id.0].focus_id = Some(focus_id); - self.focusable_node_ids.insert(focus_id, node_id); - } - - pub fn set_view_id(&mut self, view_id: EntityId) { - if self.view_stack.last().copied() != Some(view_id) { - let node_id = *self.node_stack.last().unwrap(); - self.nodes[node_id.0].view_id = Some(view_id); - self.view_node_ids.insert(view_id, node_id); - self.view_stack.push(view_id); - } - } - - pub fn pop_node(&mut self) { - let node = &self.nodes[self.active_node_id().unwrap().0]; - if node.context.is_some() { - self.context_stack.pop(); - } - if node.view_id.is_some() { - self.view_stack.pop(); - } - self.node_stack.pop(); - } - - fn move_node(&mut self, source: &mut DispatchNode) { - self.push_node(); - if let Some(context) = source.context.clone() { - self.set_key_context(context); - } - if let Some(focus_id) = source.focus_id { - self.set_focus_id(focus_id); - } - if let Some(view_id) = source.view_id { - self.set_view_id(view_id); - } - - let target = self.active_node(); - target.key_listeners = mem::take(&mut source.key_listeners); - target.action_listeners = mem::take(&mut source.action_listeners); - target.modifiers_changed_listeners = mem::take(&mut source.modifiers_changed_listeners); - } - - pub fn reuse_subtree( - &mut self, - old_range: Range, - source: &mut Self, - focus: Option, - ) -> ReusedSubtree { - let new_range = self.nodes.len()..self.nodes.len() + old_range.len(); - - let mut contains_focus = false; - let mut source_stack = vec![]; - for (source_node_id, source_node) in source - .nodes - .iter_mut() - .enumerate() - .skip(old_range.start) - .take(old_range.len()) - { - let source_node_id = DispatchNodeId(source_node_id); - while let Some(source_ancestor) = source_stack.last() { - if source_node.parent == Some(*source_ancestor) { - break; - } else { - source_stack.pop(); - self.pop_node(); - } - } - - source_stack.push(source_node_id); - if source_node.focus_id.is_some() && source_node.focus_id == focus { - contains_focus = true; - } - self.move_node(source_node); - } - - while !source_stack.is_empty() { - source_stack.pop(); - self.pop_node(); - } - - ReusedSubtree { - old_range, - new_range, - contains_focus, - } - } - - pub fn truncate(&mut self, index: usize) { - for node in &self.nodes[index..] { - if let Some(focus_id) = node.focus_id { - self.focusable_node_ids.remove(&focus_id); - } - - if let Some(view_id) = node.view_id { - self.view_node_ids.remove(&view_id); - } - } - self.nodes.truncate(index); - } - - pub fn on_key_event(&mut self, listener: KeyListener) { - self.active_node().key_listeners.push(listener); - } - - pub fn on_modifiers_changed(&mut self, listener: ModifiersChangedListener) { - self.active_node() - .modifiers_changed_listeners - .push(listener); - } - - pub fn on_action( - &mut self, - action_type: TypeId, - listener: Rc, - ) { - self.active_node() - .action_listeners - .push(DispatchActionListener { - action_type, - listener, - }); - } - - pub fn focus_contains(&self, parent: FocusId, child: FocusId) -> bool { - if parent == child { - return true; - } - - if let Some(parent_node_id) = self.focusable_node_ids.get(&parent) { - let mut current_node_id = self.focusable_node_ids.get(&child).copied(); - while let Some(node_id) = current_node_id { - if node_id == *parent_node_id { - return true; - } - current_node_id = self.nodes[node_id.0].parent; - } - } - false - } - - pub fn available_actions(&self, target: DispatchNodeId) -> Vec> { - let mut actions = Vec::>::new(); - for node_id in self.dispatch_path(target) { - let node = &self.nodes[node_id.0]; - for DispatchActionListener { action_type, .. } in &node.action_listeners { - if let Err(ix) = actions.binary_search_by_key(action_type, |a| a.as_any().type_id()) - { - // Intentionally silence these errors without logging. - // If an action cannot be built by default, it's not available. - let action = self.action_registry.build_action_type(action_type).ok(); - if let Some(action) = action { - actions.insert(ix, action); - } - } - } - } - actions - } - - pub fn is_action_available(&self, action: &dyn Action, target: DispatchNodeId) -> bool { - for node_id in self.dispatch_path(target) { - let node = &self.nodes[node_id.0]; - if node - .action_listeners - .iter() - .any(|listener| listener.action_type == action.as_any().type_id()) - { - return true; - } - } - false - } - - /// Returns key bindings that invoke an action on the currently focused element. Bindings are - /// returned in the order they were added. For display, the last binding should take precedence. - /// - /// Bindings are only included if they are the highest precedence match for their keystrokes, so - /// shadowed bindings are not included. - pub fn bindings_for_action( - &self, - action: &dyn Action, - context_stack: &[KeyContext], - ) -> Vec { - // Ideally this would return a `DoubleEndedIterator` to avoid `highest_precedence_*` - // methods, but this can't be done very cleanly since keymap must be borrowed. - let keymap = self.keymap.borrow(); - keymap - .bindings_for_action(action) - .filter(|binding| { - Self::binding_matches_predicate_and_not_shadowed(&keymap, binding, context_stack) - }) - .cloned() - .collect() - } - - /// Returns the highest precedence binding for the given action and context stack. This is the - /// same as the last result of `bindings_for_action`, but more efficient than getting all bindings. - pub fn highest_precedence_binding_for_action( - &self, - action: &dyn Action, - context_stack: &[KeyContext], - ) -> Option { - let keymap = self.keymap.borrow(); - keymap - .bindings_for_action(action) - .rev() - .find(|binding| { - Self::binding_matches_predicate_and_not_shadowed(&keymap, binding, context_stack) - }) - .cloned() - } - - fn binding_matches_predicate_and_not_shadowed( - keymap: &Keymap, - binding: &KeyBinding, - context_stack: &[KeyContext], - ) -> bool { - let (bindings, _) = keymap.bindings_for_input(&binding.keystrokes, context_stack); - if let Some(found) = bindings.iter().next() { - found.action.partial_eq(binding.action.as_ref()) - } else { - false - } - } - - fn bindings_for_input( - &self, - input: &[Keystroke], - dispatch_path: &SmallVec<[DispatchNodeId; 32]>, - ) -> (SmallVec<[KeyBinding; 1]>, bool, Vec) { - let context_stack: Vec = dispatch_path - .iter() - .filter_map(|node_id| self.node(*node_id).context.clone()) - .collect(); - - let (bindings, partial) = self - .keymap - .borrow() - .bindings_for_input(input, &context_stack); - (bindings, partial, context_stack) - } - - /// Find the bindings that can follow the current input sequence. - pub fn possible_next_bindings_for_input( - &self, - input: &[Keystroke], - context_stack: &[KeyContext], - ) -> Vec { - self.keymap - .borrow() - .possible_next_bindings_for_input(input, context_stack) - } - - /// dispatch_key processes the keystroke - /// input should be set to the value of `pending` from the previous call to dispatch_key. - /// This returns three instructions to the input handler: - /// - bindings: any bindings to execute before processing this keystroke - /// - pending: the new set of pending keystrokes to store - /// - to_replay: any keystroke that had been pushed to pending, but are no-longer matched, - /// these should be replayed first. - pub fn dispatch_key( - &mut self, - mut input: SmallVec<[Keystroke; 1]>, - keystroke: Keystroke, - dispatch_path: &SmallVec<[DispatchNodeId; 32]>, - ) -> DispatchResult { - input.push(keystroke.clone()); - let (bindings, pending, context_stack) = self.bindings_for_input(&input, dispatch_path); - - if pending { - return DispatchResult { - pending: input, - pending_has_binding: !bindings.is_empty(), - context_stack, - ..Default::default() - }; - } else if !bindings.is_empty() { - return DispatchResult { - bindings, - context_stack, - ..Default::default() - }; - } else if input.len() == 1 { - return DispatchResult { - context_stack, - ..Default::default() - }; - } - input.pop(); - - let (suffix, mut to_replay) = self.replay_prefix(input, dispatch_path); - - let mut result = self.dispatch_key(suffix, keystroke, dispatch_path); - to_replay.extend(result.to_replay); - result.to_replay = to_replay; - result - } - - /// If the user types a matching prefix of a binding and then waits for a timeout - /// flush_dispatch() converts any previously pending input to replay events. - pub fn flush_dispatch( - &mut self, - input: SmallVec<[Keystroke; 1]>, - dispatch_path: &SmallVec<[DispatchNodeId; 32]>, - ) -> SmallVec<[Replay; 1]> { - let (suffix, mut to_replay) = self.replay_prefix(input, dispatch_path); - - if !suffix.is_empty() { - to_replay.extend(self.flush_dispatch(suffix, dispatch_path)) - } - - to_replay - } - - /// Converts the longest prefix of input to a replay event and returns the rest. - fn replay_prefix( - &self, - mut input: SmallVec<[Keystroke; 1]>, - dispatch_path: &SmallVec<[DispatchNodeId; 32]>, - ) -> (SmallVec<[Keystroke; 1]>, SmallVec<[Replay; 1]>) { - let mut to_replay: SmallVec<[Replay; 1]> = Default::default(); - for last in (0..input.len()).rev() { - let (bindings, _, _) = self.bindings_for_input(&input[0..=last], dispatch_path); - if !bindings.is_empty() { - to_replay.push(Replay { - keystroke: input.drain(0..=last).next_back().unwrap(), - bindings, - }); - break; - } - } - if to_replay.is_empty() { - to_replay.push(Replay { - keystroke: input.remove(0), - ..Default::default() - }); - } - (input, to_replay) - } - - pub fn dispatch_path(&self, target: DispatchNodeId) -> SmallVec<[DispatchNodeId; 32]> { - let mut dispatch_path: SmallVec<[DispatchNodeId; 32]> = SmallVec::new(); - let mut current_node_id = Some(target); - while let Some(node_id) = current_node_id { - dispatch_path.push(node_id); - current_node_id = self.nodes.get(node_id.0).and_then(|node| node.parent); - } - dispatch_path.reverse(); // Reverse the path so it goes from the root to the focused node. - dispatch_path - } - - pub fn focus_path(&self, focus_id: FocusId) -> SmallVec<[FocusId; 8]> { - let mut focus_path: SmallVec<[FocusId; 8]> = SmallVec::new(); - let mut current_node_id = self.focusable_node_ids.get(&focus_id).copied(); - while let Some(node_id) = current_node_id { - let node = self.node(node_id); - if let Some(focus_id) = node.focus_id { - focus_path.push(focus_id); - } - current_node_id = node.parent; - } - focus_path.reverse(); // Reverse the path so it goes from the root to the focused node. - focus_path - } - - pub fn view_path_reversed(&self, view_id: EntityId) -> impl Iterator { - let mut current_node_id = self.view_node_ids.get(&view_id).copied(); - - std::iter::successors( - current_node_id.map(|node_id| self.node(node_id)), - |node_id| Some(self.node(node_id.parent?)), - ) - .filter_map(|node| node.view_id) - } - - pub fn node(&self, node_id: DispatchNodeId) -> &DispatchNode { - &self.nodes[node_id.0] - } - - fn active_node(&mut self) -> &mut DispatchNode { - let active_node_id = self.active_node_id().unwrap(); - &mut self.nodes[active_node_id.0] - } - - pub fn focusable_node_id(&self, target: FocusId) -> Option { - self.focusable_node_ids.get(&target).copied() - } - - pub fn root_node_id(&self) -> DispatchNodeId { - debug_assert!(!self.nodes.is_empty()); - DispatchNodeId(0) - } - - pub fn active_node_id(&self) -> Option { - self.node_stack.last().copied() - } -} - -#[cfg(test)] -mod tests { - use crate::{ - self as gpui, AppContext, DispatchResult, Element, ElementId, GlobalElementId, - InspectorElementId, Keystroke, LayoutId, Style, - }; - use core::panic; - use smallvec::SmallVec; - use std::{ - cell::{Cell, RefCell}, - ops::Range, - rc::Rc, - }; - - use crate::{ - ActionRegistry, App, Bounds, Context, DispatchPhase, DispatchTree, FocusHandle, - InputHandler, IntoElement, KeyBinding, KeyContext, Keymap, Pixels, PlatformWindow, Point, - Render, Subscription, TestAppContext, UTF16Selection, Unbind, VisualContext, - VisualTestContext, Window, - }; - - actions!(dispatch_test, [TestAction, SecondaryTestAction]); - - fn test_dispatch_tree(bindings: Vec) -> DispatchTree { - let registry = ActionRegistry::default(); - - DispatchTree::new( - Rc::new(RefCell::new(Keymap::new(bindings))), - Rc::new(registry), - ) - } - - struct PendingInputTestView { - focus_handle: FocusHandle, - action_count: Rc>, - secondary_action_count: Rc>, - } - - #[derive(Clone)] - struct PendingTextInputTestView { - focus_handle: FocusHandle, - text: Rc>, - action_count: Rc>, - } - - impl PendingTextInputTestView { - fn new(cx: &mut Context) -> Self { - Self { - focus_handle: cx.focus_handle(), - text: Rc::default(), - action_count: Rc::default(), - } - } - } - - impl Element for PendingTextInputTestView { - type RequestLayoutState = (); - type PrepaintState = (); - - fn id(&self) -> Option { - Some("pending-text-input-test".into()) - } - - fn source_location(&self) -> Option<&'static panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _: Option<&GlobalElementId>, - _: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - (window.request_layout(Style::default(), [], cx), ()) - } - - fn prepaint( - &mut self, - _: Option<&GlobalElementId>, - _: Option<&InspectorElementId>, - _: Bounds, - _: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Self::PrepaintState { - window.set_focus_handle(&self.focus_handle, cx); - } - - fn paint( - &mut self, - _: Option<&GlobalElementId>, - _: Option<&InspectorElementId>, - _: Bounds, - _: &mut Self::RequestLayoutState, - _: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - let mut key_context = KeyContext::default(); - key_context.add("Terminal"); - window.set_key_context(key_context); - window.handle_input(&self.focus_handle, self.clone(), cx); - let action_count = self.action_count.clone(); - window.on_action( - std::any::TypeId::of::(), - move |_, phase, _, _| { - if phase == DispatchPhase::Bubble { - action_count.set(action_count.get() + 1); - } - }, - ); - } - } - - impl IntoElement for PendingTextInputTestView { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } - } - - impl InputHandler for PendingTextInputTestView { - fn selected_text_range( - &mut self, - _: bool, - _: &mut Window, - _: &mut App, - ) -> Option { - None - } - - fn marked_text_range(&mut self, _: &mut Window, _: &mut App) -> Option> { - None - } - - fn text_for_range( - &mut self, - _: Range, - _: &mut Option>, - _: &mut Window, - _: &mut App, - ) -> Option { - None - } - - fn replace_text_in_range( - &mut self, - replacement_range: Option>, - text: &str, - _: &mut Window, - _: &mut App, - ) { - if replacement_range.is_some() { - unimplemented!() - } - self.text.borrow_mut().push_str(text) - } - - fn replace_and_mark_text_in_range( - &mut self, - replacement_range: Option>, - new_text: &str, - _: Option>, - _: &mut Window, - _: &mut App, - ) { - if replacement_range.is_some() { - unimplemented!() - } - self.text.borrow_mut().push_str(new_text) - } - - fn unmark_text(&mut self, _: &mut Window, _: &mut App) {} - - fn prefers_ime_for_printable_keys(&mut self, _: &mut Window, _: &mut App) -> bool { - true - } - - fn bounds_for_range( - &mut self, - _: Range, - _: &mut Window, - _: &mut App, - ) -> Option> { - None - } - - fn character_index_for_point( - &mut self, - _: Point, - _: &mut Window, - _: &mut App, - ) -> Option { - None - } - } - - impl Render for PendingTextInputTestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - self.clone() - } - } - - struct PendingInputTimeoutPauseOwner; - - impl Render for PendingInputTestView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - use crate::{InteractiveElement as _, Styled as _}; - let action_count = self.action_count.clone(); - let secondary_action_count = self.secondary_action_count.clone(); - crate::div() - .key_context("Terminal") - .track_focus(&self.focus_handle) - .size_full() - .on_action(move |_: &TestAction, _, _| { - action_count.set(action_count.get() + 1); - }) - .on_action(move |_: &SecondaryTestAction, _, _| { - secondary_action_count.set(secondary_action_count.get() + 1); - }) - } - } - - fn setup_pending_input_test( - cx: &mut TestAppContext, - bindings: impl IntoIterator, - ) -> (&mut VisualTestContext, Rc>, Rc>) { - cx.update(|cx| cx.bind_keys(bindings)); - - let action_count = Rc::new(Cell::new(0)); - let secondary_action_count = Rc::new(Cell::new(0)); - let (view, cx) = cx.add_window_view(|_, cx| PendingInputTestView { - focus_handle: cx.focus_handle(), - action_count: action_count.clone(), - secondary_action_count: secondary_action_count.clone(), - }); - let focus_handle = cx.update(|_, cx| view.read(cx).focus_handle.clone()); - cx.update(|window, cx| { - window.focus(&focus_handle, cx); - window.activate_window(); - }); - - (cx, action_count, secondary_action_count) - } - - fn setup_pending_input_timeout_test( - cx: &mut TestAppContext, - ) -> (&mut VisualTestContext, Rc>, Rc>) { - setup_pending_input_test( - cx, - [ - KeyBinding::new("ctrl-b", TestAction, Some("Terminal")), - KeyBinding::new("ctrl-b h", SecondaryTestAction, Some("Terminal")), - KeyBinding::new("ctrl-b h j", TestAction, Some("Terminal")), - ], - ) - } - - fn query_prefers_ime_for_printable_keys(cx: &mut VisualTestContext) -> Option { - let mut platform_window = cx.test_window(cx.window_handle()); - let mut input_handler = platform_window.take_input_handler()?; - let prefers_ime = input_handler.query_prefers_ime_for_printable_keys(); - platform_window.set_input_handler(input_handler); - Some(prefers_ime) - } - - fn simulate_pending_binding(cx: &mut VisualTestContext) { - cx.simulate_modifiers_change(crate::Modifiers::control()); - cx.simulate_keystrokes("ctrl-b"); - cx.simulate_modifiers_change(crate::Modifiers::default()); - } - - #[test] - fn test_keybinding_for_action_bounds() { - let tree = test_dispatch_tree(vec![KeyBinding::new( - "cmd-n", - TestAction, - Some("ProjectPanel"), - )]); - - let contexts = vec![ - KeyContext::parse("Workspace").unwrap(), - KeyContext::parse("ProjectPanel").unwrap(), - ]; - - let keybinding = tree.bindings_for_action(&TestAction, &contexts); - - assert!(keybinding[0].action.partial_eq(&TestAction)) - } - - #[test] - fn test_bindings_for_action_hides_targeted_unbind_in_active_context() { - let tree = test_dispatch_tree(vec![ - KeyBinding::new("tab", TestAction, Some("Editor")), - KeyBinding::new( - "tab", - Unbind("dispatch_test::TestAction".into()), - Some("Editor && edit_prediction"), - ), - KeyBinding::new( - "tab", - SecondaryTestAction, - Some("Editor && showing_completions"), - ), - ]); - - let contexts = vec![ - KeyContext::parse("Workspace").unwrap(), - KeyContext::parse("Editor showing_completions edit_prediction").unwrap(), - ]; - - let bindings = tree.bindings_for_action(&TestAction, &contexts); - assert!(bindings.is_empty()); - - let highest = tree.highest_precedence_binding_for_action(&TestAction, &contexts); - assert!(highest.is_none()); - - let fallback_bindings = tree.bindings_for_action(&SecondaryTestAction, &contexts); - assert_eq!(fallback_bindings.len(), 1); - assert!(fallback_bindings[0].action.partial_eq(&SecondaryTestAction)); - } - - #[test] - fn test_bindings_for_action_keeps_targeted_binding_outside_unbind_context() { - let tree = test_dispatch_tree(vec![ - KeyBinding::new("tab", TestAction, Some("Editor")), - KeyBinding::new( - "tab", - Unbind("dispatch_test::TestAction".into()), - Some("Editor && edit_prediction"), - ), - KeyBinding::new( - "tab", - SecondaryTestAction, - Some("Editor && showing_completions"), - ), - ]); - - let contexts = vec![ - KeyContext::parse("Workspace").unwrap(), - KeyContext::parse("Editor").unwrap(), - ]; - - let bindings = tree.bindings_for_action(&TestAction, &contexts); - assert_eq!(bindings.len(), 1); - assert!(bindings[0].action.partial_eq(&TestAction)); - - let highest = tree.highest_precedence_binding_for_action(&TestAction, &contexts); - assert!(highest.is_some_and(|binding| binding.action.partial_eq(&TestAction))); - } - - /// Models the picker preview footer scenario: a picker action is bound in - /// `Picker > Editor`, but a base keymap binds the same chord to an editor - /// action in `Editor`. `Picker > Editor` and `Editor` resolve at the same - /// context depth, so at equal depth precedence is decided purely by load - /// order (later wins). Because base keymaps load after the default keymap, - /// the picker binding is shadowed unless it is (re)bound by an overlay that - /// loads after the base keymap - which is exactly what - /// `keymaps/specific-overrides*.json` does. - #[test] - fn test_overlay_after_base_restores_shadowed_picker_binding() { - // SecondaryTestAction stands in for the editor/base action (e.g. - // editor::AddSelectionBelow), TestAction for the picker action. - let contexts = vec![ - KeyContext::parse("Picker").unwrap(), - KeyContext::parse("Editor").unwrap(), - ]; - - // Default keymap (picker binding) followed by a base keymap that binds - // the same chord to an editor action: the base binding wins and the - // picker action is shadowed, so its footer tooltip renders no shortcut. - let shadowed = test_dispatch_tree(vec![ - KeyBinding::new("ctrl-alt-down", TestAction, Some("Picker > Editor")), - KeyBinding::new("ctrl-alt-down", SecondaryTestAction, Some("Editor")), - ]); - let highest = shadowed.highest_precedence_binding_for_action(&TestAction, &contexts); - assert!( - highest.is_none(), - "picker binding should be shadowed by the base editor binding" - ); - - // Re-binding the picker action in an overlay loaded after the base keymap - // restores it as the resolved binding. - let fixed = test_dispatch_tree(vec![ - KeyBinding::new("ctrl-alt-down", TestAction, Some("Picker > Editor")), - KeyBinding::new("ctrl-alt-down", SecondaryTestAction, Some("Editor")), - // overlay loaded last: - KeyBinding::new("ctrl-alt-down", TestAction, Some("Picker > Editor")), - ]); - let highest = fixed.highest_precedence_binding_for_action(&TestAction, &contexts); - assert!( - highest.is_some_and(|binding| binding.action.partial_eq(&TestAction)), - "overlay loaded after base should restore the picker binding" - ); - - // Conversely, putting the override in the default keymap (i.e. before the - // base keymap) does NOT help: the later base binding still wins at equal - // depth. This is why the overlay must be loaded after the base keymap. - let override_before_base = test_dispatch_tree(vec![ - KeyBinding::new("ctrl-alt-down", TestAction, Some("Picker > Editor")), - KeyBinding::new("ctrl-alt-down", TestAction, Some("Picker > Editor")), - KeyBinding::new("ctrl-alt-down", SecondaryTestAction, Some("Editor")), - ]); - let highest = - override_before_base.highest_precedence_binding_for_action(&TestAction, &contexts); - assert!( - highest.is_none(), - "an override loaded before the base binding cannot win the equal-depth tie" - ); - } - - #[test] - fn test_pending_has_binding_state() { - let bindings = vec![ - KeyBinding::new("ctrl-b h", TestAction, None), - KeyBinding::new("space", TestAction, Some("ContextA")), - KeyBinding::new("space f g", TestAction, Some("ContextB")), - ]; - let mut tree = test_dispatch_tree(bindings); - - type DispatchPath = SmallVec<[super::DispatchNodeId; 32]>; - fn dispatch( - tree: &mut DispatchTree, - pending: SmallVec<[Keystroke; 1]>, - key: &str, - path: &DispatchPath, - ) -> DispatchResult { - tree.dispatch_key(pending, Keystroke::parse(key).unwrap(), path) - } - - let dispatch_path: DispatchPath = SmallVec::new(); - let result = dispatch(&mut tree, SmallVec::new(), "ctrl-b", &dispatch_path); - assert_eq!(result.pending.len(), 1); - assert!(!result.pending_has_binding); - - let result = dispatch(&mut tree, result.pending, "h", &dispatch_path); - assert_eq!(result.pending.len(), 0); - assert_eq!(result.bindings.len(), 1); - assert!(!result.pending_has_binding); - - let node_id = tree.push_node(); - tree.set_key_context(KeyContext::parse("ContextB").unwrap()); - tree.pop_node(); - - let dispatch_path = tree.dispatch_path(node_id); - let result = dispatch(&mut tree, SmallVec::new(), "space", &dispatch_path); - - assert_eq!(result.pending.len(), 1); - assert!(!result.pending_has_binding); - } - - #[crate::test] - fn test_pending_input_observers_notified_on_focus_change_and_blur(cx: &mut TestAppContext) { - #[derive(Clone)] - struct CustomElement { - focus_handle: FocusHandle, - text: Rc>, - } - - impl CustomElement { - fn new(cx: &mut Context) -> Self { - Self { - focus_handle: cx.focus_handle(), - text: Rc::default(), - } - } - } - - impl Element for CustomElement { - type RequestLayoutState = (); - - type PrepaintState = (); - - fn id(&self) -> Option { - Some("custom".into()) - } - - fn source_location(&self) -> Option<&'static panic::Location<'static>> { - None - } - - fn request_layout( - &mut self, - _: Option<&GlobalElementId>, - _: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - (window.request_layout(Style::default(), [], cx), ()) - } - - fn prepaint( - &mut self, - _: Option<&GlobalElementId>, - _: Option<&InspectorElementId>, - _: Bounds, - _: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Self::PrepaintState { - window.set_focus_handle(&self.focus_handle, cx); - } - - fn paint( - &mut self, - _: Option<&GlobalElementId>, - _: Option<&InspectorElementId>, - _: Bounds, - _: &mut Self::RequestLayoutState, - _: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - let mut key_context = KeyContext::default(); - key_context.add("Terminal"); - window.set_key_context(key_context); - window.handle_input(&self.focus_handle, self.clone(), cx); - window.on_action(std::any::TypeId::of::(), |_, _, _, _| {}); - } - } - - impl IntoElement for CustomElement { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } - } - - impl InputHandler for CustomElement { - fn selected_text_range( - &mut self, - _: bool, - _: &mut Window, - _: &mut App, - ) -> Option { - None - } - - fn marked_text_range(&mut self, _: &mut Window, _: &mut App) -> Option> { - None - } - - fn text_for_range( - &mut self, - _: Range, - _: &mut Option>, - _: &mut Window, - _: &mut App, - ) -> Option { - None - } - - fn replace_text_in_range( - &mut self, - replacement_range: Option>, - text: &str, - _: &mut Window, - _: &mut App, - ) { - if replacement_range.is_some() { - unimplemented!() - } - self.text.borrow_mut().push_str(text) - } - - fn replace_and_mark_text_in_range( - &mut self, - replacement_range: Option>, - new_text: &str, - _: Option>, - _: &mut Window, - _: &mut App, - ) { - if replacement_range.is_some() { - unimplemented!() - } - self.text.borrow_mut().push_str(new_text) - } - - fn unmark_text(&mut self, _: &mut Window, _: &mut App) {} - - fn bounds_for_range( - &mut self, - _: Range, - _: &mut Window, - _: &mut App, - ) -> Option> { - None - } - - fn character_index_for_point( - &mut self, - _: Point, - _: &mut Window, - _: &mut App, - ) -> Option { - None - } - } - - impl Render for CustomElement { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - self.clone() - } - } - - cx.update(|cx| { - cx.bind_keys([KeyBinding::new("ctrl-b", TestAction, Some("Terminal"))]); - cx.bind_keys([KeyBinding::new("ctrl-b h", TestAction, Some("Terminal"))]); - cx.bind_keys([KeyBinding::new("ctrl-d", TestAction, None)]); - cx.bind_keys([KeyBinding::new("ctrl-d h", TestAction, None)]); - }); - - let (test, cx) = cx.add_window_view(|_, cx| CustomElement::new(cx)); - let focus_handle = test.update(cx, |test, _| test.focus_handle.clone()); - - let pending_input_changed_count = Rc::new(RefCell::new(0usize)); - let pending_input_changed_count_for_observer = pending_input_changed_count.clone(); - - struct PendingInputObserver { - _subscription: Subscription, - } - - let _observer = cx.update(|window, cx| { - cx.new(|cx| PendingInputObserver { - _subscription: cx.observe_pending_input(window, move |_, _, _| { - *pending_input_changed_count_for_observer.borrow_mut() += 1; - }), - }) - }); - - cx.update(|window, cx| { - window.focus(&focus_handle, cx); - window.activate_window(); - }); - - cx.simulate_keystrokes("ctrl-b"); - - let count_after_pending = Rc::new(RefCell::new(0usize)); - let count_after_pending_for_assertion = count_after_pending.clone(); - - cx.update(|window, cx| { - assert!(window.has_pending_keystrokes()); - *count_after_pending.borrow_mut() = *pending_input_changed_count.borrow(); - assert!(*count_after_pending.borrow() > 0); - - window.focus(&cx.focus_handle(), cx); - - assert!(!window.has_pending_keystrokes()); - }); - - // Focus-triggered pending-input notifications are deferred to the end of the current - // effect cycle, so the observer callback should run after the focus update completes. - cx.update(|_, _| { - let count_after_focus_change = *pending_input_changed_count.borrow(); - assert!(count_after_focus_change > *count_after_pending_for_assertion.borrow()); - }); - - cx.update(|window, cx| window.focus(&focus_handle, cx)); - cx.simulate_keystrokes("ctrl-b"); - let count_before_blur = *pending_input_changed_count.borrow(); - - cx.update(|window, cx| { - assert!(window.has_pending_keystrokes()); - window.blur(cx); - assert!(!window.has_pending_keystrokes()); - assert!(window.pending_input_is_none()); - }); - - cx.update(|_, _| { - assert!(*pending_input_changed_count.borrow() > count_before_blur); - }); - - cx.update(|window, cx| window.disable_focus(cx)); - cx.simulate_keystrokes("ctrl-d"); - - cx.update(|window, cx| { - assert!(window.has_pending_keystrokes()); - window.blur(cx); - assert!(window.pending_input_is_none()); - }); - } - - #[crate::test] - fn test_printable_pending_input_replays_on_timeout(cx: &mut TestAppContext) { - cx.update(|cx| { - cx.bind_keys([KeyBinding::new("j k", TestAction, Some("Terminal"))]); - }); - let (test, cx) = cx.add_window_view(|_, cx| PendingTextInputTestView::new(cx)); - let focus_handle = test.update(cx, |test, _| test.focus_handle.clone()); - cx.update(|window, cx| { - window.focus(&focus_handle, cx); - window.activate_window(); - }); - - cx.simulate_keystrokes("j"); - - cx.update(|window, _| { - let pending_input = window.pending_input().expect("pending input"); - assert_eq!(pending_input.keystrokes().len(), 1); - assert!(pending_input.timeout().is_some()); - }); - test.update(cx, |test, _| { - assert_eq!(test.action_count.get(), 0); - assert_eq!(test.text.borrow().as_str(), ""); - }); - - cx.executor().advance_clock(crate::PENDING_INPUT_TIMEOUT); - cx.run_until_parked(); - - cx.update(|window, _| assert!(!window.has_pending_keystrokes())); - test.update(cx, |test, _| { - assert_eq!(test.action_count.get(), 0); - assert_eq!(test.text.borrow().as_str(), "j"); - }); - } - - #[crate::test] - fn test_pending_input_timeout_dispatches_shorter_binding(cx: &mut TestAppContext) { - let (cx, action_count, secondary_action_count) = setup_pending_input_timeout_test(cx); - simulate_pending_binding(cx); - cx.update(|window, _| { - assert_eq!( - window - .pending_input() - .map(|pending_input| pending_input.keystrokes().len()), - Some(1) - ); - assert_eq!( - window - .pending_input() - .and_then(|pending_input| pending_input.timeout()) - .map(|timeout| timeout.duration()), - Some(crate::PENDING_INPUT_TIMEOUT) - ); - }); - assert_eq!(action_count.get(), 0); - - // Emulate a countdown indicator re-rendering the window while waiting for the timeout. - for _ in 0..10 { - cx.executor() - .advance_clock(crate::PENDING_INPUT_TIMEOUT / 10); - cx.update(|window, _| window.refresh()); - cx.run_until_parked(); - } - - cx.update(|window, _| assert!(!window.has_pending_keystrokes())); - assert_eq!(action_count.get(), 1); - assert_eq!(secondary_action_count.get(), 0); - } - - #[crate::test] - fn test_running_pending_input_timeout_resets_when_binding_advances(cx: &mut TestAppContext) { - let (cx, action_count, secondary_action_count) = setup_pending_input_timeout_test(cx); - simulate_pending_binding(cx); - - cx.executor() - .advance_clock(crate::PENDING_INPUT_TIMEOUT * 4 / 5); - cx.run_until_parked(); - cx.simulate_keystrokes("h"); - cx.run_until_parked(); - - cx.update(|window, cx| { - let pending_input = window.pending_input().expect("pending input"); - let timeout = pending_input.timeout().expect("pending input timeout"); - assert_eq!(pending_input.keystrokes().len(), 2); - assert!(!timeout.is_paused()); - assert_eq!(timeout.remaining(cx), crate::PENDING_INPUT_TIMEOUT); - }); - - cx.executor() - .advance_clock(crate::PENDING_INPUT_TIMEOUT / 5); - cx.run_until_parked(); - cx.update(|window, _| assert!(window.has_pending_keystrokes())); - assert_eq!(action_count.get(), 0); - assert_eq!(secondary_action_count.get(), 0); - - cx.executor() - .advance_clock(crate::PENDING_INPUT_TIMEOUT * 4 / 5); - cx.run_until_parked(); - cx.update(|window, _| assert!(!window.has_pending_keystrokes())); - assert_eq!(action_count.get(), 0); - assert_eq!(secondary_action_count.get(), 1); - } - - #[crate::test] - fn test_pending_input_timeout_starts_when_binding_becomes_ambiguous(cx: &mut TestAppContext) { - let (cx, action_count, secondary_action_count) = setup_pending_input_test( - cx, - [ - KeyBinding::new("ctrl-b h", SecondaryTestAction, Some("Terminal")), - KeyBinding::new("ctrl-b h j", TestAction, Some("Terminal")), - ], - ); - simulate_pending_binding(cx); - - cx.update(|window, _| { - let pending_input = window.pending_input().expect("pending input"); - assert_eq!(pending_input.keystrokes().len(), 1); - assert!(pending_input.timeout().is_none()); - }); - - cx.simulate_keystrokes("h"); - cx.run_until_parked(); - cx.update(|window, cx| { - let pending_input = window.pending_input().expect("pending input"); - let timeout = pending_input.timeout().expect("pending input timeout"); - assert_eq!(pending_input.keystrokes().len(), 2); - assert_eq!(timeout.remaining(cx), crate::PENDING_INPUT_TIMEOUT); - }); - - cx.executor().advance_clock(crate::PENDING_INPUT_TIMEOUT); - cx.run_until_parked(); - cx.update(|window, _| assert!(!window.has_pending_keystrokes())); - assert_eq!(action_count.get(), 0); - assert_eq!(secondary_action_count.get(), 1); - } - - #[crate::test] - fn test_invalid_continuation_while_timeout_paused_replays_pending_input( - cx: &mut TestAppContext, - ) { - let (cx, action_count, secondary_action_count) = setup_pending_input_test( - cx, - [ - KeyBinding::new("ctrl-b", TestAction, Some("Terminal")), - KeyBinding::new("ctrl-b h", SecondaryTestAction, Some("Terminal")), - KeyBinding::new("x", SecondaryTestAction, Some("Terminal")), - ], - ); - simulate_pending_binding(cx); - let pause_owner = cx.update(|_, cx| cx.new(|_| PendingInputTimeoutPauseOwner)); - cx.update(|window, cx| { - assert!(window.set_pending_input_timeout_paused(&pause_owner, true, cx)); - }); - cx.run_until_parked(); - - cx.simulate_keystrokes("x"); - cx.run_until_parked(); - - cx.update(|window, _| assert!(!window.has_pending_keystrokes())); - assert_eq!(action_count.get(), 1); - assert_eq!(secondary_action_count.get(), 1); - - drop(pause_owner); - cx.update(|_, _| {}); - cx.run_until_parked(); - cx.executor().advance_clock(crate::PENDING_INPUT_TIMEOUT); - cx.run_until_parked(); - - cx.update(|window, _| assert!(window.pending_input_is_none())); - assert_eq!(action_count.get(), 1); - assert_eq!(secondary_action_count.get(), 1); - } - - #[crate::test] - fn test_pending_input_timeout_pauses_and_resumes(cx: &mut TestAppContext) { - let (cx, action_count, secondary_action_count) = setup_pending_input_timeout_test(cx); - simulate_pending_binding(cx); - - cx.executor() - .advance_clock(crate::PENDING_INPUT_TIMEOUT * 7 / 10); - cx.run_until_parked(); - - let pause_owner = cx.update(|_, cx| cx.new(|_| PendingInputTimeoutPauseOwner)); - let other_owner = cx.update(|_, cx| cx.new(|_| PendingInputTimeoutPauseOwner)); - cx.update(|window, cx| { - assert!(window.set_pending_input_timeout_paused(&pause_owner, true, cx)); - assert!(!window.set_pending_input_timeout_paused(&pause_owner, true, cx)); - assert!(!window.set_pending_input_timeout_paused(&other_owner, false, cx)); - }); - cx.run_until_parked(); - cx.update(|window, cx| { - let timeout = window - .pending_input() - .and_then(|pending_input| pending_input.timeout()) - .expect("pending input timeout"); - assert!(timeout.is_paused()); - assert_eq!(timeout.remaining(cx), crate::PENDING_INPUT_TIMEOUT * 3 / 10); - }); - - cx.executor() - .advance_clock(crate::PENDING_INPUT_TIMEOUT * 2); - cx.run_until_parked(); - cx.update(|window, _| assert!(window.has_pending_keystrokes())); - assert_eq!(action_count.get(), 0); - - cx.update(|window, cx| { - assert!(window.set_pending_input_timeout_paused(&pause_owner, false, cx)); - }); - cx.run_until_parked(); - cx.update(|window, cx| { - let timeout = window - .pending_input() - .and_then(|pending_input| pending_input.timeout()) - .expect("pending input timeout"); - assert!(!timeout.is_paused()); - assert_eq!(timeout.remaining(cx), crate::PENDING_INPUT_TIMEOUT * 3 / 10); - }); - - cx.executor() - .advance_clock(crate::PENDING_INPUT_TIMEOUT * 3 / 10); - cx.run_until_parked(); - - cx.update(|window, _| assert!(!window.has_pending_keystrokes())); - assert_eq!(action_count.get(), 1); - assert_eq!(secondary_action_count.get(), 0); - } - - #[crate::test] - fn test_pending_input_timeout_resumes_when_owner_is_released(cx: &mut TestAppContext) { - let (cx, action_count, secondary_action_count) = setup_pending_input_timeout_test(cx); - simulate_pending_binding(cx); - - cx.executor() - .advance_clock(crate::PENDING_INPUT_TIMEOUT * 7 / 10); - cx.run_until_parked(); - - let pause_owner = cx.update(|_, cx| cx.new(|_| PendingInputTimeoutPauseOwner)); - cx.update(|window, cx| { - assert!(window.set_pending_input_timeout_paused(&pause_owner, true, cx)); - }); - cx.run_until_parked(); - - drop(pause_owner); - cx.update(|_, _| {}); - cx.run_until_parked(); - cx.update(|window, cx| { - let timeout = window - .pending_input() - .and_then(|pending_input| pending_input.timeout()) - .expect("pending input timeout"); - assert!(!timeout.is_paused()); - assert_eq!(timeout.remaining(cx), crate::PENDING_INPUT_TIMEOUT * 3 / 10); - }); - - cx.executor() - .advance_clock(crate::PENDING_INPUT_TIMEOUT * 3 / 10); - cx.run_until_parked(); - - cx.update(|window, _| assert!(!window.has_pending_keystrokes())); - assert_eq!(action_count.get(), 1); - assert_eq!(secondary_action_count.get(), 0); - } - - #[crate::test] - fn test_pending_input_timeout_resets_when_binding_advances(cx: &mut TestAppContext) { - let (cx, action_count, secondary_action_count) = setup_pending_input_timeout_test(cx); - simulate_pending_binding(cx); - cx.executor() - .advance_clock(crate::PENDING_INPUT_TIMEOUT / 2); - cx.run_until_parked(); - let pause_owner = cx.update(|_, cx| cx.new(|_| PendingInputTimeoutPauseOwner)); - cx.update(|window, cx| { - assert!(window.set_pending_input_timeout_paused(&pause_owner, true, cx)); - }); - cx.run_until_parked(); - cx.update(|window, cx| { - let timeout = window - .pending_input() - .and_then(|pending_input| pending_input.timeout()) - .expect("pending input timeout"); - assert_eq!(timeout.remaining(cx), crate::PENDING_INPUT_TIMEOUT / 2); - }); - - cx.simulate_keystrokes("h"); - cx.run_until_parked(); - cx.update(|window, cx| { - let pending_input = window.pending_input().expect("pending input"); - let timeout = pending_input.timeout().expect("pending input timeout"); - assert_eq!(pending_input.keystrokes().len(), 2); - assert!(timeout.is_paused()); - assert_eq!(timeout.remaining(cx), crate::PENDING_INPUT_TIMEOUT); - }); - - cx.executor() - .advance_clock(crate::PENDING_INPUT_TIMEOUT * 2); - cx.run_until_parked(); - cx.update(|window, _| assert!(window.has_pending_keystrokes())); - assert_eq!(action_count.get(), 0); - assert_eq!(secondary_action_count.get(), 0); - - cx.update(|window, cx| { - assert!(window.set_pending_input_timeout_paused(&pause_owner, false, cx)); - }); - cx.run_until_parked(); - cx.executor().advance_clock(crate::PENDING_INPUT_TIMEOUT); - cx.run_until_parked(); - - cx.update(|window, _| assert!(!window.has_pending_keystrokes())); - assert_eq!(action_count.get(), 0); - assert_eq!(secondary_action_count.get(), 1); - } - - #[crate::test] - fn test_clearing_pending_input_invalidates_timeout_pause(cx: &mut TestAppContext) { - let (cx, action_count, secondary_action_count) = setup_pending_input_timeout_test(cx); - simulate_pending_binding(cx); - let pause_owner = cx.update(|_, cx| cx.new(|_| PendingInputTimeoutPauseOwner)); - cx.update(|window, cx| { - assert!(window.set_pending_input_timeout_paused(&pause_owner, true, cx)); - window.focus(&cx.focus_handle(), cx); - assert!(!window.has_pending_keystrokes()); - }); - - drop(pause_owner); - cx.update(|_, _| {}); - cx.run_until_parked(); - cx.executor().advance_clock(crate::PENDING_INPUT_TIMEOUT); - cx.run_until_parked(); - - cx.update(|window, _| assert!(window.pending_input_is_none())); - assert_eq!(action_count.get(), 0); - assert_eq!(secondary_action_count.get(), 0); - } - - #[crate::test] - fn test_input_handler_pending(cx: &mut TestAppContext) { - cx.update(|cx| { - cx.bind_keys([KeyBinding::new("ctrl-b", TestAction, Some("Terminal"))]); - cx.bind_keys([KeyBinding::new("ctrl-b h", TestAction, Some("Terminal"))]); - cx.bind_keys([KeyBinding::new("ctrl-x k", TestAction, Some("Terminal"))]); - }); - let (test, cx) = cx.add_window_view(|_, cx| PendingTextInputTestView::new(cx)); - let focus_handle = test.update(cx, |test, _| test.focus_handle.clone()); - cx.update(|window, cx| { - window.focus(&focus_handle, cx); - window.activate_window(); - }); - - assert_eq!(query_prefers_ime_for_printable_keys(cx), Some(true)); - cx.simulate_keystrokes("ctrl-x"); - cx.update(|window, _| assert!(window.has_pending_keystrokes())); - assert_eq!(query_prefers_ime_for_printable_keys(cx), Some(false)); - - let prefers_ime_after_blur = { - let mut platform_window = cx.test_window(cx.window_handle()); - let mut input_handler = platform_window.take_input_handler(); - cx.update(|window, cx| { - window.blur(cx); - assert!(!window.has_pending_keystrokes()); - assert!(window.pending_input_is_none()); - }); - let prefers_ime = input_handler - .as_mut() - .map(|input_handler| input_handler.query_prefers_ime_for_printable_keys()); - if let Some(input_handler) = input_handler { - platform_window.set_input_handler(input_handler); - } - prefers_ime - }; - assert_eq!(prefers_ime_after_blur, Some(true)); - cx.update(|window, cx| window.focus(&focus_handle, cx)); - - cx.simulate_keystrokes("ctrl-x"); - assert_eq!(query_prefers_ime_for_printable_keys(cx), Some(false)); - cx.simulate_keystrokes("k"); - cx.update(|window, _| assert!(!window.has_pending_keystrokes())); - assert_eq!(query_prefers_ime_for_printable_keys(cx), Some(true)); - test.update(cx, |test, _| { - assert_eq!(test.action_count.get(), 1); - assert_eq!(test.text.borrow().as_str(), ""); - }); - - cx.simulate_keystrokes("ctrl-b ["); - test.update(cx, |test, _| assert_eq!(test.text.borrow().as_str(), "[")) - } -} diff --git a/crates/gpui_pre/src/keymap.rs b/crates/gpui_pre/src/keymap.rs deleted file mode 100644 index bef0d03a..00000000 --- a/crates/gpui_pre/src/keymap.rs +++ /dev/null @@ -1,927 +0,0 @@ -mod binding; -mod context; - -pub use binding::*; -pub use context::*; - -use crate::{Action, AsKeystroke, Keystroke, Unbind, is_no_action, is_unbind}; -use collections::{HashSet, TypeIdHashMap}; -use smallvec::SmallVec; - -/// An opaque identifier of which version of the keymap is currently active. -/// The keymap's version is changed whenever bindings are added or removed. -#[derive(Copy, Clone, Eq, PartialEq, Default)] -pub struct KeymapVersion(usize); - -/// A collection of key bindings for the user's application. -#[derive(Default)] -pub struct Keymap { - bindings: Vec, - binding_indices_by_action_id: TypeIdHashMap>, - disabled_binding_indices: Vec, - version: KeymapVersion, -} - -/// Index of a binding within a keymap. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] -pub struct BindingIndex(usize); - -fn disabled_binding_matches_context(disabled_binding: &KeyBinding, binding: &KeyBinding) -> bool { - match ( - &disabled_binding.context_predicate, - &binding.context_predicate, - ) { - (None, _) => true, - (Some(_), None) => false, - (Some(disabled_predicate), Some(predicate)) => disabled_predicate.is_superset(predicate), - } -} - -fn binding_is_unbound(disabled_binding: &KeyBinding, binding: &KeyBinding) -> bool { - disabled_binding.keystrokes == binding.keystrokes - && disabled_binding - .action() - .as_any() - .downcast_ref::() - .is_some_and(|unbind| unbind.0.as_ref() == binding.action.name()) -} - -impl Keymap { - /// Create a new keymap with the given bindings. - pub fn new(bindings: Vec) -> Self { - let mut this = Self::default(); - this.add_bindings(bindings); - this - } - - /// Get the current version of the keymap. - pub fn version(&self) -> KeymapVersion { - self.version - } - - /// Add more bindings to the keymap. - pub fn add_bindings>(&mut self, bindings: T) { - for binding in bindings { - let action_id = binding.action().as_any().type_id(); - if is_no_action(&*binding.action) || is_unbind(&*binding.action) { - self.disabled_binding_indices.push(self.bindings.len()); - } else { - self.binding_indices_by_action_id - .entry(action_id) - .or_default() - .push(self.bindings.len()); - } - self.bindings.push(binding); - } - - self.version.0 += 1; - } - - /// Reset this keymap to its initial state. - pub fn clear(&mut self) { - self.bindings.clear(); - self.binding_indices_by_action_id.clear(); - self.disabled_binding_indices.clear(); - self.version.0 += 1; - } - - /// Iterate over all bindings, in the order they were added. - pub fn bindings(&self) -> impl DoubleEndedIterator + ExactSizeIterator { - self.bindings.iter() - } - - /// Iterate over all bindings for the given action, in the order they were added. For display, - /// the last binding should take precedence. - pub fn bindings_for_action<'a>( - &'a self, - action: &'a dyn Action, - ) -> impl 'a + DoubleEndedIterator { - let action_id = action.type_id(); - let binding_indices = self - .binding_indices_by_action_id - .get(&action_id) - .map_or(&[] as _, SmallVec::as_slice) - .iter(); - - binding_indices.filter_map(|ix| { - let binding = &self.bindings[*ix]; - if !binding.action().partial_eq(action) { - return None; - } - - for disabled_ix in &self.disabled_binding_indices { - if disabled_ix > ix { - let disabled_binding = &self.bindings[*disabled_ix]; - if disabled_binding.keystrokes != binding.keystrokes { - continue; - } - - if is_no_action(&*disabled_binding.action) { - if disabled_binding_matches_context(disabled_binding, binding) { - return None; - } - } else if is_unbind(&*disabled_binding.action) - && disabled_binding_matches_context(disabled_binding, binding) - && binding_is_unbound(disabled_binding, binding) - { - return None; - } - } - } - - Some(binding) - }) - } - - /// Returns all bindings that might match the input without checking context. The bindings - /// returned in precedence order (reverse of the order they were added to the keymap). - pub fn all_bindings_for_input(&self, input: &[Keystroke]) -> Vec { - self.bindings() - .rev() - .filter(|binding| { - binding - .match_keystrokes(input) - .is_some_and(|pending| !pending) - }) - .cloned() - .collect() - } - - /// Returns a list of bindings that match the given input, and a boolean indicating whether or - /// not more bindings might match if the input was longer. Bindings are returned in precedence - /// order (higher precedence first, reverse of the order they were added to the keymap). - /// - /// Precedence is defined by the depth in the tree (matches on the Editor take precedence over - /// matches on the Pane, then the Workspace, etc.). Bindings with no context are treated as the - /// same as the deepest context. - /// - /// In the case of multiple bindings at the same depth, the ones added to the keymap later take - /// precedence. User bindings are added after built-in bindings so that they take precedence. - /// - /// If a binding has been disabled with `"x": null` it will not be returned. Disabled bindings - /// are evaluated with the same precedence rules so you can disable a rule in a given context - /// only. A disabled binding only suppresses bindings from sources with equal or weaker - /// precedence: a base keymap null hides default bindings, but user bindings still apply. - pub fn bindings_for_input( - &self, - input: &[impl AsKeystroke], - context_stack: &[KeyContext], - ) -> (SmallVec<[KeyBinding; 1]>, bool) { - let mut matched_bindings = SmallVec::<[(usize, BindingIndex, &KeyBinding); 1]>::new(); - let mut pending_bindings = SmallVec::<[(BindingIndex, &KeyBinding); 1]>::new(); - - for (ix, binding) in self.bindings().enumerate().rev() { - let Some(depth) = self.binding_enabled(binding, context_stack) else { - continue; - }; - let Some(pending) = binding.match_keystrokes(input) else { - continue; - }; - - if !pending { - matched_bindings.push((depth, BindingIndex(ix), binding)); - } else { - pending_bindings.push((BindingIndex(ix), binding)); - } - } - - matched_bindings.sort_by(|(depth_a, ix_a, _), (depth_b, ix_b, _)| { - depth_b.cmp(depth_a).then(ix_b.cmp(ix_a)) - }); - - let mut bindings: SmallVec<[_; 1]> = SmallVec::new(); - let mut first_binding_index = None; - let mut unbound_bindings: Vec<&KeyBinding> = Vec::new(); - // A `NoAction` binding suppresses out-ranked bindings from sources with - // equal or weaker precedence, while bindings from stronger sources (a - // smaller meta, e.g. a user binding vs a base keymap null) still apply. - // Bindings without a meta are treated as user bindings. - let mut no_action_meta: Option = None; - - for (_, ix, binding) in matched_bindings { - let meta = binding.meta.map_or(0, |meta| meta.0); - if is_no_action(&*binding.action) { - no_action_meta = Some(no_action_meta.map_or(meta, |existing| existing.min(meta))); - continue; - } - - if no_action_meta.is_some_and(|no_action_meta| meta >= no_action_meta) { - continue; - } - - if is_unbind(&*binding.action) { - unbound_bindings.push(binding); - continue; - } - - if unbound_bindings - .iter() - .any(|disabled_binding| binding_is_unbound(disabled_binding, binding)) - { - continue; - } - - bindings.push(binding.clone()); - first_binding_index.get_or_insert(ix); - } - - let mut pending = HashSet::default(); - for (ix, binding) in pending_bindings.into_iter().rev() { - if let Some(binding_ix) = first_binding_index - && binding_ix > ix - { - continue; - } - if is_no_action(&*binding.action) || is_unbind(&*binding.action) { - pending.remove(&&binding.keystrokes); - continue; - } - pending.insert(&binding.keystrokes); - } - - (bindings, !pending.is_empty()) - } - /// Check if the given binding is enabled, given a certain key context. - /// Returns the deepest depth at which the binding matches, or None if it doesn't match. - fn binding_enabled(&self, binding: &KeyBinding, contexts: &[KeyContext]) -> Option { - if let Some(predicate) = &binding.context_predicate { - predicate.depth_of(contexts) - } else { - Some(contexts.len()) - } - } - - /// Find the bindings that can follow the current input sequence. - pub fn possible_next_bindings_for_input( - &self, - input: &[Keystroke], - context_stack: &[KeyContext], - ) -> Vec { - let mut bindings = self - .bindings() - .enumerate() - .rev() - .filter_map(|(ix, binding)| { - let depth = self.binding_enabled(binding, context_stack)?; - let pending = binding.match_keystrokes(input); - match pending { - None => None, - Some(is_pending) => { - if !is_pending - || is_no_action(&*binding.action) - || is_unbind(&*binding.action) - { - return None; - } - Some((depth, BindingIndex(ix), binding)) - } - } - }) - .collect::>(); - - bindings.sort_by(|(depth_a, ix_a, _), (depth_b, ix_b, _)| { - depth_b.cmp(depth_a).then(ix_b.cmp(ix_a)) - }); - - bindings - .into_iter() - .map(|(_, _, binding)| binding.clone()) - .collect::>() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate as gpui; - use gpui::{NoAction, Unbind}; - - actions!( - test_only, - [ActionAlpha, ActionBeta, ActionGamma, ActionDelta,] - ); - - #[test] - fn test_keymap() { - let bindings = [ - KeyBinding::new("ctrl-a", ActionAlpha {}, None), - KeyBinding::new("ctrl-a", ActionBeta {}, Some("pane")), - KeyBinding::new("ctrl-a", ActionGamma {}, Some("editor && mode==full")), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings.clone()); - - // global bindings are enabled in all contexts - assert_eq!(keymap.binding_enabled(&bindings[0], &[]), Some(0)); - assert_eq!( - keymap.binding_enabled(&bindings[0], &[KeyContext::parse("terminal").unwrap()]), - Some(1) - ); - - // contextual bindings are enabled in contexts that match their predicate - assert_eq!( - keymap.binding_enabled(&bindings[1], &[KeyContext::parse("barf x=y").unwrap()]), - None - ); - assert_eq!( - keymap.binding_enabled(&bindings[1], &[KeyContext::parse("pane x=y").unwrap()]), - Some(1) - ); - - assert_eq!( - keymap.binding_enabled(&bindings[2], &[KeyContext::parse("editor").unwrap()]), - None - ); - assert_eq!( - keymap.binding_enabled( - &bindings[2], - &[KeyContext::parse("editor mode=full").unwrap()] - ), - Some(1) - ); - } - - #[test] - fn test_depth_precedence() { - let bindings = [ - KeyBinding::new("ctrl-a", ActionBeta {}, Some("pane")), - KeyBinding::new("ctrl-a", ActionGamma {}, Some("editor")), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - let (result, pending) = keymap.bindings_for_input( - &[Keystroke::parse("ctrl-a").unwrap()], - &[ - KeyContext::parse("pane").unwrap(), - KeyContext::parse("editor").unwrap(), - ], - ); - - assert!(!pending); - assert_eq!(result.len(), 2); - assert!(result[0].action.partial_eq(&ActionGamma {})); - assert!(result[1].action.partial_eq(&ActionBeta {})); - } - - #[test] - fn test_keymap_disabled() { - let bindings = [ - KeyBinding::new("ctrl-a", ActionAlpha {}, Some("editor")), - KeyBinding::new("ctrl-b", ActionAlpha {}, Some("editor")), - KeyBinding::new("ctrl-a", NoAction {}, Some("editor && mode==full")), - KeyBinding::new("ctrl-b", NoAction {}, None), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - // binding is only enabled in a specific context - assert!( - keymap - .bindings_for_input( - &[Keystroke::parse("ctrl-a").unwrap()], - &[KeyContext::parse("barf").unwrap()], - ) - .0 - .is_empty() - ); - assert!( - !keymap - .bindings_for_input( - &[Keystroke::parse("ctrl-a").unwrap()], - &[KeyContext::parse("editor").unwrap()], - ) - .0 - .is_empty() - ); - - // binding is disabled in a more specific context - assert!( - keymap - .bindings_for_input( - &[Keystroke::parse("ctrl-a").unwrap()], - &[KeyContext::parse("editor mode=full").unwrap()], - ) - .0 - .is_empty() - ); - - // binding is globally disabled - assert!( - keymap - .bindings_for_input( - &[Keystroke::parse("ctrl-b").unwrap()], - &[KeyContext::parse("barf").unwrap()], - ) - .0 - .is_empty() - ); - } - - #[test] - /// Tests for https://github.com/zed-industries/zed/issues/30259 - fn test_multiple_keystroke_binding_disabled() { - let bindings = [ - KeyBinding::new("space w w", ActionAlpha {}, Some("workspace")), - KeyBinding::new("space w w", NoAction {}, Some("editor")), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - let space = || Keystroke::parse("space").unwrap(); - let w = || Keystroke::parse("w").unwrap(); - - let space_w = [space(), w()]; - let space_w_w = [space(), w(), w()]; - - let workspace_context = || [KeyContext::parse("workspace").unwrap()]; - - let editor_workspace_context = || { - [ - KeyContext::parse("workspace").unwrap(), - KeyContext::parse("editor").unwrap(), - ] - }; - - // Ensure `space` results in pending input on the workspace, but not editor - let space_workspace = keymap.bindings_for_input(&[space()], &workspace_context()); - assert!(space_workspace.0.is_empty()); - assert!(space_workspace.1); - - let space_editor = keymap.bindings_for_input(&[space()], &editor_workspace_context()); - assert!(space_editor.0.is_empty()); - assert!(!space_editor.1); - - // Ensure `space w` results in pending input on the workspace, but not editor - let space_w_workspace = keymap.bindings_for_input(&space_w, &workspace_context()); - assert!(space_w_workspace.0.is_empty()); - assert!(space_w_workspace.1); - - let space_w_editor = keymap.bindings_for_input(&space_w, &editor_workspace_context()); - assert!(space_w_editor.0.is_empty()); - assert!(!space_w_editor.1); - - // Ensure `space w w` results in the binding in the workspace, but not in the editor - let space_w_w_workspace = keymap.bindings_for_input(&space_w_w, &workspace_context()); - assert!(!space_w_w_workspace.0.is_empty()); - assert!(!space_w_w_workspace.1); - - let space_w_w_editor = keymap.bindings_for_input(&space_w_w, &editor_workspace_context()); - assert!(space_w_w_editor.0.is_empty()); - assert!(!space_w_w_editor.1); - - // Now test what happens if we have another binding defined AFTER the NoAction - // that should result in pending - let bindings = [ - KeyBinding::new("space w w", ActionAlpha {}, Some("workspace")), - KeyBinding::new("space w w", NoAction {}, Some("editor")), - KeyBinding::new("space w x", ActionAlpha {}, Some("editor")), - ]; - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - let space_editor = keymap.bindings_for_input(&[space()], &editor_workspace_context()); - assert!(space_editor.0.is_empty()); - assert!(space_editor.1); - - // Now test what happens if we have another binding defined BEFORE the NoAction - // that should result in pending - let bindings = [ - KeyBinding::new("space w w", ActionAlpha {}, Some("workspace")), - KeyBinding::new("space w x", ActionAlpha {}, Some("editor")), - KeyBinding::new("space w w", NoAction {}, Some("editor")), - ]; - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - let space_editor = keymap.bindings_for_input(&[space()], &editor_workspace_context()); - assert!(space_editor.0.is_empty()); - assert!(space_editor.1); - - // Now test what happens if we have another binding defined at a higher context - // that should result in pending - let bindings = [ - KeyBinding::new("space w w", ActionAlpha {}, Some("workspace")), - KeyBinding::new("space w x", ActionAlpha {}, Some("workspace")), - KeyBinding::new("space w w", NoAction {}, Some("editor")), - ]; - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - let space_editor = keymap.bindings_for_input(&[space()], &editor_workspace_context()); - assert!(space_editor.0.is_empty()); - assert!(space_editor.1); - } - - #[test] - fn test_override_multikey() { - let bindings = [ - KeyBinding::new("ctrl-w left", ActionAlpha {}, Some("editor")), - KeyBinding::new("ctrl-w", NoAction {}, Some("editor")), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - // Ensure `space` results in pending input on the workspace, but not editor - let (result, pending) = keymap.bindings_for_input( - &[Keystroke::parse("ctrl-w").unwrap()], - &[KeyContext::parse("editor").unwrap()], - ); - assert!(result.is_empty()); - assert!(pending); - - let bindings = [ - KeyBinding::new("ctrl-w left", ActionAlpha {}, Some("editor")), - KeyBinding::new("ctrl-w", ActionBeta {}, Some("editor")), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - // Ensure `space` results in pending input on the workspace, but not editor - let (result, pending) = keymap.bindings_for_input( - &[Keystroke::parse("ctrl-w").unwrap()], - &[KeyContext::parse("editor").unwrap()], - ); - assert_eq!(result.len(), 1); - assert!(!pending); - } - - #[test] - fn test_simple_disable() { - let bindings = [ - KeyBinding::new("ctrl-x", ActionAlpha {}, Some("editor")), - KeyBinding::new("ctrl-x", NoAction {}, Some("editor")), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - // Ensure `space` results in pending input on the workspace, but not editor - let (result, pending) = keymap.bindings_for_input( - &[Keystroke::parse("ctrl-x").unwrap()], - &[KeyContext::parse("editor").unwrap()], - ); - assert!(result.is_empty()); - assert!(!pending); - } - - #[test] - fn test_disable_weaker_sources_only() { - const USER: KeyBindingMetaIndex = KeyBindingMetaIndex(0); - const VIM: KeyBindingMetaIndex = KeyBindingMetaIndex(1); - const BASE: KeyBindingMetaIndex = KeyBindingMetaIndex(2); - const DEFAULT: KeyBindingMetaIndex = KeyBindingMetaIndex(3); - - let editor_context = || [KeyContext::parse("editor").unwrap()]; - let ctrl_x = || [Keystroke::parse("ctrl-x").unwrap()]; - - // A base keymap null disables a default binding in the same context. - let mut keymap = Keymap::default(); - keymap.add_bindings([ - KeyBinding::new("ctrl-x", ActionAlpha {}, Some("editor")).with_meta(DEFAULT), - KeyBinding::new("ctrl-x", NoAction {}, Some("editor")).with_meta(BASE), - ]); - let (result, _) = keymap.bindings_for_input(&ctrl_x(), &editor_context()); - assert!(result.is_empty()); - - // A user binding is not affected by base keymap or default nulls. - let mut keymap = Keymap::default(); - keymap.add_bindings([ - KeyBinding::new("ctrl-x", NoAction {}, Some("editor")).with_meta(DEFAULT), - KeyBinding::new("ctrl-x", NoAction {}, Some("editor")).with_meta(BASE), - KeyBinding::new("ctrl-x", ActionBeta {}, None).with_meta(USER), - ]); - let (result, _) = keymap.bindings_for_input(&ctrl_x(), &editor_context()); - assert_eq!(result.len(), 1); - assert!(result[0].action.partial_eq(&ActionBeta {})); - - // A user binding at a shallower context is not disabled by a deeper - // base keymap null. - let mut keymap = Keymap::default(); - keymap.add_bindings([ - KeyBinding::new("ctrl-x", NoAction {}, Some("editor")).with_meta(BASE), - KeyBinding::new("ctrl-x", ActionBeta {}, Some("workspace")).with_meta(USER), - ]); - let (result, _) = keymap.bindings_for_input( - &ctrl_x(), - &[ - KeyContext::parse("workspace").unwrap(), - KeyContext::parse("editor").unwrap(), - ], - ); - assert_eq!(result.len(), 1); - assert!(result[0].action.partial_eq(&ActionBeta {})); - - // A vim binding survives a base keymap null, and a user null disables - // everything. - let mut keymap = Keymap::default(); - keymap.add_bindings([ - KeyBinding::new("ctrl-x", ActionAlpha {}, Some("editor")).with_meta(DEFAULT), - KeyBinding::new("ctrl-x", NoAction {}, Some("editor")).with_meta(BASE), - KeyBinding::new("ctrl-x", ActionGamma {}, Some("editor")).with_meta(VIM), - ]); - let (result, _) = keymap.bindings_for_input(&ctrl_x(), &editor_context()); - assert_eq!(result.len(), 1); - assert!(result[0].action.partial_eq(&ActionGamma {})); - - let mut keymap = Keymap::default(); - keymap.add_bindings([ - KeyBinding::new("ctrl-x", ActionAlpha {}, Some("editor")).with_meta(DEFAULT), - KeyBinding::new("ctrl-x", ActionGamma {}, Some("editor")).with_meta(VIM), - KeyBinding::new("ctrl-x", NoAction {}, Some("editor")).with_meta(USER), - ]); - let (result, _) = keymap.bindings_for_input(&ctrl_x(), &editor_context()); - assert!(result.is_empty()); - } - - #[test] - fn test_fail_to_disable() { - // disabled at the wrong level - let bindings = [ - KeyBinding::new("ctrl-x", ActionAlpha {}, Some("editor")), - KeyBinding::new("ctrl-x", NoAction {}, Some("workspace")), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - // Ensure `space` results in pending input on the workspace, but not editor - let (result, pending) = keymap.bindings_for_input( - &[Keystroke::parse("ctrl-x").unwrap()], - &[ - KeyContext::parse("workspace").unwrap(), - KeyContext::parse("editor").unwrap(), - ], - ); - assert_eq!(result.len(), 1); - assert!(!pending); - } - - #[test] - fn test_disable_deeper() { - let bindings = [ - KeyBinding::new("ctrl-x", ActionAlpha {}, Some("workspace")), - KeyBinding::new("ctrl-x", NoAction {}, Some("editor")), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - // Ensure `space` results in pending input on the workspace, but not editor - let (result, pending) = keymap.bindings_for_input( - &[Keystroke::parse("ctrl-x").unwrap()], - &[ - KeyContext::parse("workspace").unwrap(), - KeyContext::parse("editor").unwrap(), - ], - ); - assert_eq!(result.len(), 0); - assert!(!pending); - } - - #[test] - fn test_pending_match_enabled() { - let bindings = [ - KeyBinding::new("ctrl-x", ActionBeta, Some("vim_mode == normal")), - KeyBinding::new("ctrl-x 0", ActionAlpha, Some("Workspace")), - ]; - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - let matched = keymap.bindings_for_input( - &[Keystroke::parse("ctrl-x")].map(Result::unwrap), - &[ - KeyContext::parse("Workspace"), - KeyContext::parse("Pane"), - KeyContext::parse("Editor vim_mode=normal"), - ] - .map(Result::unwrap), - ); - assert_eq!(matched.0.len(), 1); - assert!(matched.0[0].action.partial_eq(&ActionBeta)); - assert!(matched.1); - } - - #[test] - fn test_pending_match_enabled_extended() { - let bindings = [ - KeyBinding::new("ctrl-x", ActionBeta, Some("vim_mode == normal")), - KeyBinding::new("ctrl-x 0", NoAction, Some("Workspace")), - ]; - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - let matched = keymap.bindings_for_input( - &[Keystroke::parse("ctrl-x")].map(Result::unwrap), - &[ - KeyContext::parse("Workspace"), - KeyContext::parse("Pane"), - KeyContext::parse("Editor vim_mode=normal"), - ] - .map(Result::unwrap), - ); - assert_eq!(matched.0.len(), 1); - assert!(matched.0[0].action.partial_eq(&ActionBeta)); - assert!(!matched.1); - let bindings = [ - KeyBinding::new("ctrl-x", ActionBeta, Some("Workspace")), - KeyBinding::new("ctrl-x 0", NoAction, Some("vim_mode == normal")), - ]; - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - let matched = keymap.bindings_for_input( - &[Keystroke::parse("ctrl-x")].map(Result::unwrap), - &[ - KeyContext::parse("Workspace"), - KeyContext::parse("Pane"), - KeyContext::parse("Editor vim_mode=normal"), - ] - .map(Result::unwrap), - ); - assert_eq!(matched.0.len(), 1); - assert!(matched.0[0].action.partial_eq(&ActionBeta)); - assert!(!matched.1); - } - - #[test] - fn test_overriding_prefix() { - let bindings = [ - KeyBinding::new("ctrl-x 0", ActionAlpha, Some("Workspace")), - KeyBinding::new("ctrl-x", ActionBeta, Some("vim_mode == normal")), - ]; - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - let matched = keymap.bindings_for_input( - &[Keystroke::parse("ctrl-x")].map(Result::unwrap), - &[ - KeyContext::parse("Workspace"), - KeyContext::parse("Pane"), - KeyContext::parse("Editor vim_mode=normal"), - ] - .map(Result::unwrap), - ); - assert_eq!(matched.0.len(), 1); - assert!(matched.0[0].action.partial_eq(&ActionBeta)); - assert!(!matched.1); - } - - #[test] - fn test_context_precedence_with_same_source() { - // Test case: User has both Workspace and Editor bindings for the same key - // Editor binding should take precedence over Workspace binding - let bindings = [ - KeyBinding::new("cmd-r", ActionAlpha {}, Some("Workspace")), - KeyBinding::new("cmd-r", ActionBeta {}, Some("Editor")), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - // Test with context stack: [Workspace, Editor] (Editor is deeper) - let (result, _) = keymap.bindings_for_input( - &[Keystroke::parse("cmd-r").unwrap()], - &[ - KeyContext::parse("Workspace").unwrap(), - KeyContext::parse("Editor").unwrap(), - ], - ); - - // Both bindings should be returned, but Editor binding should be first (highest precedence) - assert_eq!(result.len(), 2); - assert!(result[0].action.partial_eq(&ActionBeta {})); // Editor binding first - assert!(result[1].action.partial_eq(&ActionAlpha {})); // Workspace binding second - } - - #[test] - fn test_bindings_for_action() { - let bindings = [ - KeyBinding::new("ctrl-a", ActionAlpha {}, Some("pane")), - KeyBinding::new("ctrl-b", ActionBeta {}, Some("editor && mode == full")), - KeyBinding::new("ctrl-c", ActionGamma {}, Some("workspace")), - KeyBinding::new("ctrl-a", NoAction {}, Some("pane && active")), - KeyBinding::new("ctrl-b", NoAction {}, Some("editor")), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - assert_bindings(&keymap, &ActionAlpha {}, &["ctrl-a"]); - assert_bindings(&keymap, &ActionBeta {}, &[]); - assert_bindings(&keymap, &ActionGamma {}, &["ctrl-c"]); - - #[track_caller] - fn assert_bindings(keymap: &Keymap, action: &dyn Action, expected: &[&str]) { - let actual = keymap - .bindings_for_action(action) - .map(|binding| binding.keystrokes[0].inner().unparse()) - .collect::>(); - assert_eq!(actual, expected, "{:?}", action); - } - } - - #[test] - fn test_targeted_unbind_ignores_target_context() { - let bindings = [ - KeyBinding::new("tab", ActionAlpha {}, Some("Editor")), - KeyBinding::new("tab", ActionBeta {}, Some("Editor && showing_completions")), - KeyBinding::new( - "tab", - Unbind("test_only::ActionAlpha".into()), - Some("Editor && edit_prediction"), - ), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - let (result, pending) = keymap.bindings_for_input( - &[Keystroke::parse("tab").unwrap()], - &[KeyContext::parse("Editor showing_completions edit_prediction").unwrap()], - ); - - assert!(!pending); - assert_eq!(result.len(), 1); - assert!(result[0].action.partial_eq(&ActionBeta {})); - } - - #[test] - fn test_bindings_for_action_keeps_binding_for_narrower_targeted_unbind() { - let bindings = [ - KeyBinding::new("tab", ActionAlpha {}, Some("Editor")), - KeyBinding::new( - "tab", - Unbind("test_only::ActionAlpha".into()), - Some("Editor && edit_prediction"), - ), - KeyBinding::new("tab", ActionBeta {}, Some("Editor && showing_completions")), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - assert_bindings(&keymap, &ActionAlpha {}, &["tab"]); - assert_bindings(&keymap, &ActionBeta {}, &["tab"]); - - #[track_caller] - fn assert_bindings(keymap: &Keymap, action: &dyn Action, expected: &[&str]) { - let actual = keymap - .bindings_for_action(action) - .map(|binding| binding.keystrokes[0].inner().unparse()) - .collect::>(); - assert_eq!(actual, expected, "{:?}", action); - } - } - - #[test] - fn test_bindings_for_action_removes_binding_for_broader_targeted_unbind() { - let bindings = [ - KeyBinding::new("tab", ActionAlpha {}, Some("Editor && edit_prediction")), - KeyBinding::new( - "tab", - Unbind("test_only::ActionAlpha".into()), - Some("Editor"), - ), - ]; - - let mut keymap = Keymap::default(); - keymap.add_bindings(bindings); - - assert!(keymap.bindings_for_action(&ActionAlpha {}).next().is_none()); - } - - #[test] - fn test_source_precedence_sorting() { - // KeybindSource precedence: User (0) > Vim (1) > Base (2) > Default (3) - // Test that user keymaps take precedence over default keymaps at the same context depth - let mut keymap = Keymap::default(); - - // Add a default keymap binding first - let mut default_binding = KeyBinding::new("cmd-r", ActionAlpha {}, Some("Editor")); - default_binding.set_meta(KeyBindingMetaIndex(3)); // Default source - keymap.add_bindings([default_binding]); - - // Add a user keymap binding - let mut user_binding = KeyBinding::new("cmd-r", ActionBeta {}, Some("Editor")); - user_binding.set_meta(KeyBindingMetaIndex(0)); // User source - keymap.add_bindings([user_binding]); - - // Test with Editor context stack - let (result, _) = keymap.bindings_for_input( - &[Keystroke::parse("cmd-r").unwrap()], - &[KeyContext::parse("Editor").unwrap()], - ); - - // User binding should take precedence over default binding - assert_eq!(result.len(), 2); - assert!(result[0].action.partial_eq(&ActionBeta {})); - assert!(result[1].action.partial_eq(&ActionAlpha {})); - } -} diff --git a/crates/gpui_pre/src/keymap/binding.rs b/crates/gpui_pre/src/keymap/binding.rs deleted file mode 100644 index fc4b3294..00000000 --- a/crates/gpui_pre/src/keymap/binding.rs +++ /dev/null @@ -1,143 +0,0 @@ -use std::rc::Rc; - -use crate::{ - Action, AsKeystroke, DummyKeyboardMapper, InvalidKeystrokeError, KeyBindingContextPredicate, - KeybindingKeystroke, Keystroke, PlatformKeyboardMapper, SharedString, -}; -use smallvec::SmallVec; - -/// A keybinding and its associated metadata, from the keymap. -pub struct KeyBinding { - pub(crate) action: Box, - pub(crate) keystrokes: SmallVec<[KeybindingKeystroke; 2]>, - pub(crate) context_predicate: Option>, - pub(crate) meta: Option, - /// The json input string used when building the keybinding, if any - pub(crate) action_input: Option, -} - -impl Clone for KeyBinding { - fn clone(&self) -> Self { - KeyBinding { - action: self.action.boxed_clone(), - keystrokes: self.keystrokes.clone(), - context_predicate: self.context_predicate.clone(), - meta: self.meta, - action_input: self.action_input.clone(), - } - } -} - -impl KeyBinding { - /// Construct a new keybinding from the given data. Panics on parse error. - pub fn new(keystrokes: &str, action: A, context: Option<&str>) -> Self { - let context_predicate = - context.map(|context| KeyBindingContextPredicate::parse(context).unwrap().into()); - Self::load( - keystrokes, - Box::new(action), - context_predicate, - false, - None, - &DummyKeyboardMapper, - ) - .unwrap() - } - - /// Load a keybinding from the given raw data. - pub fn load( - keystrokes: &str, - action: Box, - context_predicate: Option>, - use_key_equivalents: bool, - action_input: Option, - keyboard_mapper: &dyn PlatformKeyboardMapper, - ) -> std::result::Result { - let keystrokes: SmallVec<[KeybindingKeystroke; 2]> = keystrokes - .split_whitespace() - .map(|source| { - let keystroke = Keystroke::parse(source)?; - Ok(KeybindingKeystroke::new_with_mapper( - keystroke, - use_key_equivalents, - keyboard_mapper, - )) - }) - .collect::>()?; - - Ok(Self { - keystrokes, - action, - context_predicate, - meta: None, - action_input, - }) - } - - /// Set the metadata for this binding. - pub fn with_meta(mut self, meta: KeyBindingMetaIndex) -> Self { - self.meta = Some(meta); - self - } - - /// Set the metadata for this binding. - pub fn set_meta(&mut self, meta: KeyBindingMetaIndex) { - self.meta = Some(meta); - } - - /// Check if the given keystrokes match this binding. - pub fn match_keystrokes(&self, typed: &[impl AsKeystroke]) -> Option { - if self.keystrokes.len() < typed.len() { - return None; - } - - for (target, typed) in self.keystrokes.iter().zip(typed.iter()) { - if !typed.as_keystroke().should_match(target) { - return None; - } - } - - Some(self.keystrokes.len() > typed.len()) - } - - /// Get the keystrokes associated with this binding - pub fn keystrokes(&self) -> &[KeybindingKeystroke] { - self.keystrokes.as_slice() - } - - /// Get the action associated with this binding - pub fn action(&self) -> &dyn Action { - self.action.as_ref() - } - - /// Get the predicate used to match this binding - pub fn predicate(&self) -> Option> { - self.context_predicate.as_ref().map(|rc| rc.clone()) - } - - /// Get the metadata for this binding - pub fn meta(&self) -> Option { - self.meta - } - - /// Get the action input associated with the action for this binding - pub fn action_input(&self) -> Option { - self.action_input.clone() - } -} - -impl std::fmt::Debug for KeyBinding { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("KeyBinding") - .field("keystrokes", &self.keystrokes) - .field("context_predicate", &self.context_predicate) - .field("action", &self.action.name()) - .finish() - } -} - -/// A unique identifier for retrieval of metadata associated with a key binding. -/// Intended to be used as an index or key into a user-defined store of metadata -/// associated with the binding, such as the source of the binding. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct KeyBindingMetaIndex(pub u32); diff --git a/crates/gpui_pre/src/keymap/context.rs b/crates/gpui_pre/src/keymap/context.rs deleted file mode 100644 index 27f361bb..00000000 --- a/crates/gpui_pre/src/keymap/context.rs +++ /dev/null @@ -1,891 +0,0 @@ -use crate::SharedString; -use anyhow::{Context as _, Result}; -use std::fmt; - -/// A datastructure for resolving whether an action should be dispatched -/// at this point in the element tree. Contains a set of identifiers -/// and/or key value pairs representing the current context for the -/// keymap. -#[derive(Clone, Default, Eq, PartialEq, Hash)] -pub struct KeyContext(Vec); - -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -/// An entry in a KeyContext -pub struct ContextEntry { - /// The key (or name if no value) - pub key: SharedString, - /// The value - pub value: Option, -} - -impl<'a> TryFrom<&'a str> for KeyContext { - type Error = anyhow::Error; - - fn try_from(value: &'a str) -> Result { - Self::parse(value) - } -} - -impl KeyContext { - /// Initialize a new [`KeyContext`] that contains an `os` key set to either `macos`, `linux`, `windows` or `unknown`. - pub fn new_with_defaults() -> Self { - let mut context = Self::default(); - #[cfg(target_os = "macos")] - context.set("os", "macos"); - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - context.set("os", "linux"); - #[cfg(target_os = "windows")] - context.set("os", "windows"); - #[cfg(not(any( - target_os = "macos", - target_os = "linux", - target_os = "freebsd", - target_os = "windows" - )))] - context.set("os", "unknown"); - context - } - - /// Returns the primary context entry (usually the name of the component) - pub fn primary(&self) -> Option<&ContextEntry> { - self.0.iter().find(|p| p.value.is_none()) - } - - /// Returns everything except the primary context entry. - pub fn secondary(&self) -> impl Iterator { - let primary = self.primary(); - self.0.iter().filter(move |&p| Some(p) != primary) - } - - /// Parse a key context from a string. - /// The key context format is very simple: - /// - either a single identifier, such as `StatusBar` - /// - or a key value pair, such as `mode = visible` - /// - separated by whitespace, such as `StatusBar mode = visible` - pub fn parse(source: &str) -> Result { - let mut context = Self::default(); - let source = skip_whitespace(source); - Self::parse_expr(source, &mut context)?; - Ok(context) - } - - fn parse_expr(mut source: &str, context: &mut Self) -> Result<()> { - if source.is_empty() { - return Ok(()); - } - - let key = source - .chars() - .take_while(|c| is_identifier_char(*c)) - .collect::(); - source = skip_whitespace(&source[key.len()..]); - if let Some(suffix) = source.strip_prefix('=') { - source = skip_whitespace(suffix); - let value = source - .chars() - .take_while(|c| is_identifier_char(*c)) - .collect::(); - source = skip_whitespace(&source[value.len()..]); - context.set(key, value); - } else { - context.add(key); - } - - Self::parse_expr(source, context) - } - - /// Check if this context is empty. - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Clear this context. - pub fn clear(&mut self) { - self.0.clear(); - } - - /// Extend this context with another context. - pub fn extend(&mut self, other: &Self) { - for entry in &other.0 { - if !self.contains(&entry.key) { - self.0.push(entry.clone()); - } - } - } - - /// Add an identifier to this context, if it's not already in this context. - pub fn add>(&mut self, identifier: I) { - let key = identifier.into(); - - if !self.contains(&key) { - self.0.push(ContextEntry { key, value: None }) - } - } - - /// Set a key value pair in this context, if it's not already set. - pub fn set, S2: Into>(&mut self, key: S1, value: S2) { - let key = key.into(); - if !self.contains(&key) { - self.0.push(ContextEntry { - key, - value: Some(value.into()), - }) - } - } - - /// Check if this context contains a given identifier or key. - pub fn contains(&self, key: &str) -> bool { - self.0.iter().any(|entry| entry.key.as_ref() == key) - } - - /// Get the associated value for a given identifier or key. - pub fn get(&self, key: &str) -> Option<&SharedString> { - self.0 - .iter() - .find(|entry| entry.key.as_ref() == key)? - .value - .as_ref() - } -} - -impl fmt::Debug for KeyContext { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut entries = self.0.iter().peekable(); - while let Some(entry) = entries.next() { - if let Some(ref value) = entry.value { - write!(f, "{}={}", entry.key, value)?; - } else { - write!(f, "{}", entry.key)?; - } - if entries.peek().is_some() { - write!(f, " ")?; - } - } - Ok(()) - } -} - -/// A datastructure for resolving whether an action should be dispatched -/// Representing a small language for describing which contexts correspond -/// to which actions. -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub enum KeyBindingContextPredicate { - /// A predicate that will match a given identifier. - Identifier(SharedString), - /// A predicate that will match a given key-value pair. - Equal(SharedString, SharedString), - /// A predicate that will match a given key-value pair not being present. - NotEqual(SharedString, SharedString), - /// A predicate that will match a given predicate appearing below another predicate. - /// in the element tree - Descendant( - Box, - Box, - ), - /// Predicate that will invert another predicate. - Not(Box), - /// A predicate that will match if both of its children match. - And( - Box, - Box, - ), - /// A predicate that will match if either of its children match. - Or( - Box, - Box, - ), -} - -impl fmt::Display for KeyBindingContextPredicate { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Identifier(name) => write!(f, "{name}"), - Self::Equal(left, right) => write!(f, "{left} == {right}"), - Self::NotEqual(left, right) => write!(f, "{left} != {right}"), - Self::Descendant(parent, child) => write!(f, "{parent} > {child}"), - Self::Not(pred) => match pred.as_ref() { - Self::Identifier(name) => write!(f, "!{name}"), - _ => write!(f, "!({pred})"), - }, - Self::And(..) => self.fmt_joined(f, " && ", LogicalOperator::And, |node| { - matches!(node, Self::Or(..)) - }), - Self::Or(..) => self.fmt_joined(f, " || ", LogicalOperator::Or, |node| { - matches!(node, Self::And(..)) - }), - } - } -} - -impl KeyBindingContextPredicate { - /// Parse a string in the same format as the keymap's context field. - /// - /// A basic equivalence check against a set of identifiers can performed by - /// simply writing a string: - /// - /// `StatusBar` -> A predicate that will match a context with the identifier `StatusBar` - /// - /// You can also specify a key-value pair: - /// - /// `mode == visible` -> A predicate that will match a context with the key `mode` - /// with the value `visible` - /// - /// And a logical operations combining these two checks: - /// - /// `StatusBar && mode == visible` -> A predicate that will match a context with the - /// identifier `StatusBar` and the key `mode` - /// with the value `visible` - /// - /// - /// There is also a special child `>` operator that will match a predicate that is - /// below another predicate: - /// - /// `StatusBar > mode == visible` -> A predicate that will match a context identifier `StatusBar` - /// and a child context that has the key `mode` with the - /// value `visible` - /// - /// This syntax supports `!=`, `||` and `&&` as logical operators. - /// You can also preface an operation or check with a `!` to negate it. - pub fn parse(source: &str) -> Result { - let source = skip_whitespace(source); - let (predicate, rest) = Self::parse_expr(source, 0)?; - if let Some(next) = rest.chars().next() { - anyhow::bail!("unexpected character '{next:?}'"); - } else { - Ok(predicate) - } - } - - /// Find the deepest depth at which the predicate matches. - pub fn depth_of(&self, contexts: &[KeyContext]) -> Option { - for depth in (0..=contexts.len()).rev() { - let context_slice = &contexts[0..depth]; - if self.eval_inner(context_slice, contexts) { - return Some(depth); - } - } - None - } - - /// Eval a predicate against a set of contexts, arranged from lowest to highest. - #[allow(unused)] - pub fn eval(&self, contexts: &[KeyContext]) -> bool { - self.eval_inner(contexts, contexts) - } - - /// Eval a predicate against a set of contexts, arranged from lowest to highest. - pub fn eval_inner(&self, contexts: &[KeyContext], all_contexts: &[KeyContext]) -> bool { - let Some(context) = contexts.last() else { - return false; - }; - match self { - Self::Identifier(name) => context.contains(name), - Self::Equal(left, right) => context - .get(left) - .map(|value| value == right) - .unwrap_or(false), - Self::NotEqual(left, right) => context - .get(left) - .map(|value| value != right) - .unwrap_or(true), - Self::Not(pred) => { - for i in 0..all_contexts.len() { - if pred.eval_inner(&all_contexts[..=i], all_contexts) { - return false; - } - } - true - } - // Workspace > Pane > Editor - // - // Pane > (Pane > Editor) // should match? - // (Pane > Pane) > Editor // should not match? - // Pane > !Workspace <-- should match? - // !Workspace <-- shouldn't match? - Self::Descendant(parent, child) => { - for i in 0..contexts.len() - 1 { - // [Workspace > Pane], [Editor] - if parent.eval_inner(&contexts[..=i], all_contexts) { - if !child.eval_inner(&contexts[i + 1..], &contexts[i + 1..]) { - return false; - } - return true; - } - } - false - } - Self::And(left, right) => { - left.eval_inner(contexts, all_contexts) && right.eval_inner(contexts, all_contexts) - } - Self::Or(left, right) => { - left.eval_inner(contexts, all_contexts) || right.eval_inner(contexts, all_contexts) - } - } - } - - /// Returns whether or not this predicate matches all possible contexts matched by - /// the other predicate. - pub fn is_superset(&self, other: &Self) -> bool { - if self == other { - return true; - } - - if let KeyBindingContextPredicate::Or(left, right) = self { - return left.is_superset(other) || right.is_superset(other); - } - - match other { - KeyBindingContextPredicate::Descendant(_, child) => self.is_superset(child), - KeyBindingContextPredicate::And(left, right) => { - self.is_superset(left) || self.is_superset(right) - } - KeyBindingContextPredicate::Identifier(_) => false, - KeyBindingContextPredicate::Equal(_, _) => false, - KeyBindingContextPredicate::NotEqual(_, _) => false, - KeyBindingContextPredicate::Not(_) => false, - KeyBindingContextPredicate::Or(_, _) => false, - } - } - - fn parse_expr(mut source: &str, min_precedence: u32) -> anyhow::Result<(Self, &str)> { - type Op = fn( - KeyBindingContextPredicate, - KeyBindingContextPredicate, - ) -> Result; - - 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 = 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().context("unexpected end")?; - match next { - '(' => { - source = skip_whitespace(&source[1..]); - let (predicate, rest) = Self::parse_expr(source, 0)?; - let stripped = rest.strip_prefix(')').context("expected a ')'")?; - source = skip_whitespace(stripped); - Ok((predicate, source)) - } - '!' => { - let source = skip_whitespace(&source[1..]); - let (predicate, source) = Self::parse_expr(source, PRECEDENCE_NOT)?; - Ok((KeyBindingContextPredicate::Not(Box::new(predicate)), source)) - } - _ if is_identifier_char(next) => { - let len = source - .find(|c: char| !is_identifier_char(c) && !is_vim_operator_char(c)) - .unwrap_or(source.len()); - let (identifier, rest) = source.split_at(len); - source = skip_whitespace(rest); - Ok(( - KeyBindingContextPredicate::Identifier(identifier.to_string().into()), - source, - )) - } - _ if is_vim_operator_char(next) => { - let (operator, rest) = source.split_at(1); - source = skip_whitespace(rest); - Ok(( - KeyBindingContextPredicate::Identifier(operator.to_string().into()), - source, - )) - } - _ => anyhow::bail!("unexpected character '{next:?}'"), - } - } - - fn new_or(self, other: Self) -> Result { - Ok(Self::Or(Box::new(self), Box::new(other))) - } - - fn new_and(self, other: Self) -> Result { - Ok(Self::And(Box::new(self), Box::new(other))) - } - - fn new_child(self, other: Self) -> Result { - Ok(Self::Descendant(Box::new(self), Box::new(other))) - } - - fn new_eq(self, other: Self) -> Result { - if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) { - Ok(Self::Equal(left, right)) - } else { - anyhow::bail!("operands of == must be identifiers"); - } - } - - fn new_neq(self, other: Self) -> Result { - if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) { - Ok(Self::NotEqual(left, right)) - } else { - anyhow::bail!("operands of != must be identifiers"); - } - } - - fn fmt_joined( - &self, - f: &mut fmt::Formatter<'_>, - separator: &str, - operator: LogicalOperator, - needs_parens: impl Fn(&Self) -> bool + Copy, - ) -> fmt::Result { - let mut first = true; - self.fmt_joined_inner(f, separator, operator, needs_parens, &mut first) - } - - fn fmt_joined_inner( - &self, - f: &mut fmt::Formatter<'_>, - separator: &str, - operator: LogicalOperator, - needs_parens: impl Fn(&Self) -> bool + Copy, - first: &mut bool, - ) -> fmt::Result { - match (operator, self) { - (LogicalOperator::And, Self::And(left, right)) - | (LogicalOperator::Or, Self::Or(left, right)) => { - left.fmt_joined_inner(f, separator, operator, needs_parens, first)?; - right.fmt_joined_inner(f, separator, operator, needs_parens, first) - } - (_, node) => { - if !*first { - f.write_str(separator)?; - } - *first = false; - - if needs_parens(node) { - write!(f, "({node})") - } else { - write!(f, "{node}") - } - } - } - } -} - -#[derive(Clone, Copy)] -enum LogicalOperator { - And, - Or, -} - -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; - -fn is_identifier_char(c: char) -> bool { - c.is_alphanumeric() || c == '_' || c == '-' -} - -fn is_vim_operator_char(c: char) -> bool { - c == '>' || c == '<' || c == '~' || c == '"' || c == '?' -} - -fn skip_whitespace(source: &str) -> &str { - let len = source - .find(|c: char| !c.is_whitespace()) - .unwrap_or(source.len()); - &source[len..] -} - -#[cfg(test)] -mod tests { - use core::slice; - - use super::*; - use crate as gpui; - use KeyBindingContextPredicate::*; - - #[test] - fn test_actions_definition() { - { - actions!(test_only, [A, B, C, D, E, F, G]); - } - - { - actions!( - test_only, - [ - H, I, J, K, L, M, N, // Don't wrap, test the trailing comma - ] - ); - } - } - - #[test] - fn test_parse_context() { - let mut expected = KeyContext::default(); - expected.add("baz"); - expected.set("foo", "bar"); - assert_eq!(KeyContext::parse("baz foo=bar").unwrap(), expected); - assert_eq!(KeyContext::parse("baz foo = bar").unwrap(), expected); - assert_eq!( - KeyContext::parse(" baz foo = bar baz").unwrap(), - expected - ); - assert_eq!(KeyContext::parse(" baz foo = bar").unwrap(), expected); - } - - #[test] - fn test_parse_identifiers() { - // Identifiers - assert_eq!( - KeyBindingContextPredicate::parse("abc12").unwrap(), - Identifier("abc12".into()) - ); - assert_eq!( - KeyBindingContextPredicate::parse("_1a").unwrap(), - Identifier("_1a".into()) - ); - } - - #[test] - fn test_parse_negations() { - assert_eq!( - KeyBindingContextPredicate::parse("!abc").unwrap(), - Not(Box::new(Identifier("abc".into()))) - ); - assert_eq!( - KeyBindingContextPredicate::parse(" ! ! abc").unwrap(), - Not(Box::new(Not(Box::new(Identifier("abc".into()))))) - ); - } - - #[test] - fn test_parse_equality_operators() { - assert_eq!( - KeyBindingContextPredicate::parse("a == b").unwrap(), - Equal("a".into(), "b".into()) - ); - assert_eq!( - KeyBindingContextPredicate::parse("c!=d").unwrap(), - NotEqual("c".into(), "d".into()) - ); - assert_eq!( - KeyBindingContextPredicate::parse("c == !d") - .unwrap_err() - .to_string(), - "operands of == must be identifiers" - ); - } - - #[test] - fn test_parse_boolean_operators() { - assert_eq!( - KeyBindingContextPredicate::parse("a || b").unwrap(), - Or( - Box::new(Identifier("a".into())), - Box::new(Identifier("b".into())) - ) - ); - assert_eq!( - KeyBindingContextPredicate::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!( - KeyBindingContextPredicate::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!( - KeyBindingContextPredicate::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!( - KeyBindingContextPredicate::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!( - KeyBindingContextPredicate::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!( - KeyBindingContextPredicate::parse(" ( a || b ) ").unwrap(), - Or( - Box::new(Identifier("a".into())), - Box::new(Identifier("b".into())), - ) - ); - } - - #[test] - fn test_is_superset() { - assert_is_superset("editor", "editor", true); - assert_is_superset("editor", "workspace", false); - - assert_is_superset("editor", "editor && vim_mode", true); - assert_is_superset("editor", "mode == full && editor", true); - assert_is_superset("editor && mode == full", "editor", false); - - assert_is_superset("editor", "something > editor", true); - assert_is_superset("editor", "editor > menu", false); - - assert_is_superset("foo || bar || baz", "bar", true); - assert_is_superset("foo || bar || baz", "quux", false); - - #[track_caller] - fn assert_is_superset(a: &str, b: &str, result: bool) { - let a = KeyBindingContextPredicate::parse(a).unwrap(); - let b = KeyBindingContextPredicate::parse(b).unwrap(); - assert_eq!(a.is_superset(&b), result, "({a:?}).is_superset({b:?})"); - } - } - - #[test] - fn test_child_operator() { - let predicate = KeyBindingContextPredicate::parse("parent > child").unwrap(); - - let parent_context = KeyContext::try_from("parent").unwrap(); - let child_context = KeyContext::try_from("child").unwrap(); - - let contexts = vec![parent_context.clone(), child_context.clone()]; - assert!(predicate.eval(&contexts)); - - let grandparent_context = KeyContext::try_from("grandparent").unwrap(); - - let contexts = vec![ - grandparent_context, - parent_context.clone(), - child_context.clone(), - ]; - assert!(predicate.eval(&contexts)); - - let other_context = KeyContext::try_from("other").unwrap(); - - let contexts = vec![other_context.clone(), child_context.clone()]; - assert!(!predicate.eval(&contexts)); - - let contexts = vec![parent_context.clone(), other_context, child_context.clone()]; - assert!(predicate.eval(&contexts)); - - assert!(!predicate.eval(&[])); - assert!(!predicate.eval(slice::from_ref(&child_context))); - assert!(!predicate.eval(&[parent_context])); - - let zany_predicate = KeyBindingContextPredicate::parse("child > child").unwrap(); - assert!(!zany_predicate.eval(slice::from_ref(&child_context))); - assert!(zany_predicate.eval(&[child_context.clone(), child_context])); - } - - #[test] - fn test_not_operator() { - let not_predicate = KeyBindingContextPredicate::parse("!editor").unwrap(); - let editor_context = KeyContext::try_from("editor").unwrap(); - let workspace_context = KeyContext::try_from("workspace").unwrap(); - let parent_context = KeyContext::try_from("parent").unwrap(); - let child_context = KeyContext::try_from("child").unwrap(); - - assert!(not_predicate.eval(slice::from_ref(&workspace_context))); - assert!(!not_predicate.eval(slice::from_ref(&editor_context))); - assert!(!not_predicate.eval(&[editor_context.clone(), workspace_context.clone()])); - assert!(!not_predicate.eval(&[workspace_context.clone(), editor_context.clone()])); - - let complex_not = KeyBindingContextPredicate::parse("!editor && workspace").unwrap(); - assert!(complex_not.eval(slice::from_ref(&workspace_context))); - assert!(!complex_not.eval(&[editor_context.clone(), workspace_context.clone()])); - - let not_mode_predicate = KeyBindingContextPredicate::parse("!(mode == full)").unwrap(); - let mut mode_context = KeyContext::default(); - mode_context.set("mode", "full"); - assert!(!not_mode_predicate.eval(&[mode_context.clone()])); - - let mut other_mode_context = KeyContext::default(); - other_mode_context.set("mode", "partial"); - assert!(not_mode_predicate.eval(&[other_mode_context])); - - let not_descendant = KeyBindingContextPredicate::parse("!(parent > child)").unwrap(); - assert!(not_descendant.eval(slice::from_ref(&parent_context))); - assert!(not_descendant.eval(slice::from_ref(&child_context))); - assert!(!not_descendant.eval(&[parent_context.clone(), child_context.clone()])); - - let not_descendant = KeyBindingContextPredicate::parse("parent > !child").unwrap(); - assert!(!not_descendant.eval(slice::from_ref(&parent_context))); - assert!(!not_descendant.eval(slice::from_ref(&child_context))); - assert!(!not_descendant.eval(&[parent_context, child_context])); - - let double_not = KeyBindingContextPredicate::parse("!!editor").unwrap(); - assert!(double_not.eval(slice::from_ref(&editor_context))); - assert!(!double_not.eval(slice::from_ref(&workspace_context))); - - // Test complex descendant cases - let workspace_context = KeyContext::try_from("Workspace").unwrap(); - let pane_context = KeyContext::try_from("Pane").unwrap(); - let editor_context = KeyContext::try_from("Editor").unwrap(); - - // Workspace > Pane > Editor - let workspace_pane_editor = vec![ - workspace_context.clone(), - pane_context.clone(), - editor_context.clone(), - ]; - - // Pane > (Pane > Editor) - should not match - let pane_pane_editor = KeyBindingContextPredicate::parse("Pane > (Pane > Editor)").unwrap(); - assert!(!pane_pane_editor.eval(&workspace_pane_editor)); - - let workspace_pane_editor_predicate = - KeyBindingContextPredicate::parse("Workspace > Pane > Editor").unwrap(); - assert!(workspace_pane_editor_predicate.eval(&workspace_pane_editor)); - - // (Pane > Pane) > Editor - should not match - let pane_pane_then_editor = - KeyBindingContextPredicate::parse("(Pane > Pane) > Editor").unwrap(); - assert!(!pane_pane_then_editor.eval(&workspace_pane_editor)); - - // Pane > !Workspace - should match - let pane_not_workspace = KeyBindingContextPredicate::parse("Pane > !Workspace").unwrap(); - assert!(pane_not_workspace.eval(&[pane_context.clone(), editor_context.clone()])); - assert!(!pane_not_workspace.eval(&[pane_context.clone(), workspace_context.clone()])); - - // !Workspace - shouldn't match when Workspace is in the context - let not_workspace = KeyBindingContextPredicate::parse("!Workspace").unwrap(); - assert!(!not_workspace.eval(slice::from_ref(&workspace_context))); - assert!(not_workspace.eval(slice::from_ref(&pane_context))); - assert!(not_workspace.eval(slice::from_ref(&editor_context))); - assert!(!not_workspace.eval(&workspace_pane_editor)); - } - - // MARK: - Display - - #[test] - fn test_context_display() { - fn ident(s: &str) -> Box { - Box::new(Identifier(SharedString::new(s))) - } - fn eq(a: &str, b: &str) -> Box { - Box::new(Equal(SharedString::new(a), SharedString::new(b))) - } - fn not_eq(a: &str, b: &str) -> Box { - Box::new(NotEqual(SharedString::new(a), SharedString::new(b))) - } - fn and( - a: Box, - b: Box, - ) -> Box { - Box::new(And(a, b)) - } - fn or( - a: Box, - b: Box, - ) -> Box { - Box::new(Or(a, b)) - } - fn descendant( - a: Box, - b: Box, - ) -> Box { - Box::new(Descendant(a, b)) - } - fn not(a: Box) -> Box { - Box::new(Not(a)) - } - - let test_cases = [ - (ident("a"), "a"), - (eq("a", "b"), "a == b"), - (not_eq("a", "b"), "a != b"), - (descendant(ident("a"), ident("b")), "a > b"), - (not(ident("a")), "!a"), - (not_eq("a", "b"), "a != b"), - (descendant(ident("a"), ident("b")), "a > b"), - (not(and(ident("a"), ident("b"))), "!(a && b)"), - (not(or(ident("a"), ident("b"))), "!(a || b)"), - (and(ident("a"), ident("b")), "a && b"), - (and(and(ident("a"), ident("b")), ident("c")), "a && b && c"), - (or(ident("a"), ident("b")), "a || b"), - (or(or(ident("a"), ident("b")), ident("c")), "a || b || c"), - (or(ident("a"), and(ident("b"), ident("c"))), "a || (b && c)"), - ( - and( - and( - and(ident("a"), eq("b", "c")), - not(descendant(ident("d"), ident("e"))), - ), - eq("f", "g"), - ), - "a && b == c && !(d > e) && f == g", - ), - ( - and(and(ident("a"), or(ident("b"), ident("c"))), ident("d")), - "a && (b || c) && d", - ), - ( - or(or(ident("a"), and(ident("b"), ident("c"))), ident("d")), - "a || (b && c) || d", - ), - ]; - - for (predicate, expected) in test_cases { - let actual = predicate.to_string(); - assert_eq!(actual, expected); - let parsed = KeyBindingContextPredicate::parse(&actual).unwrap(); - assert_eq!(parsed, *predicate); - } - } -} diff --git a/crates/gpui_pre/src/path_builder.rs b/crates/gpui_pre/src/path_builder.rs deleted file mode 100644 index 40a6e71e..00000000 --- a/crates/gpui_pre/src/path_builder.rs +++ /dev/null @@ -1,347 +0,0 @@ -use anyhow::Error; -use etagere::euclid::{Point2D, Vector2D}; -use lyon::geom::Angle; -use lyon::math::{Vector, vector}; -use lyon::path::traits::SvgPathBuilder; -use lyon::path::{ArcFlags, Polygon}; -use lyon::tessellation::{ - BuffersBuilder, FillTessellator, FillVertex, StrokeTessellator, StrokeVertex, VertexBuffers, -}; - -pub use lyon::math::Transform; -pub use lyon::tessellation::{FillOptions, FillRule, StrokeOptions}; - -use crate::{Path, Pixels, Point, point, px}; - -/// Style of the PathBuilder -pub enum PathStyle { - /// Stroke style - Stroke(StrokeOptions), - /// Fill style - Fill(FillOptions), -} - -/// A [`Path`] builder. -pub struct PathBuilder { - raw: lyon::path::builder::WithSvg, - transform: Option, - /// PathStyle of the PathBuilder - pub style: PathStyle, - dash_array: Option>, -} - -impl From for PathBuilder { - fn from(builder: lyon::path::Builder) -> Self { - Self { - raw: builder.with_svg(), - ..Default::default() - } - } -} - -impl From> for PathBuilder { - fn from(raw: lyon::path::builder::WithSvg) -> Self { - Self { - raw, - ..Default::default() - } - } -} - -impl From for Point { - fn from(p: lyon::math::Point) -> Self { - point(px(p.x), px(p.y)) - } -} - -impl From> for lyon::math::Point { - fn from(p: Point) -> Self { - lyon::math::point(p.x.0, p.y.0) - } -} - -impl From> for Vector { - fn from(p: Point) -> Self { - vector(p.x.0, p.y.0) - } -} - -impl From> for Point2D { - fn from(p: Point) -> Self { - Point2D::new(p.x.0, p.y.0) - } -} - -impl Default for PathBuilder { - fn default() -> Self { - Self { - raw: lyon::path::Path::builder().with_svg(), - style: PathStyle::Fill(FillOptions::default()), - transform: None, - dash_array: None, - } - } -} - -impl PathBuilder { - /// Creates a new [`PathBuilder`] to build a Stroke path. - pub fn stroke(width: Pixels) -> Self { - Self { - style: PathStyle::Stroke(StrokeOptions::default().with_line_width(width.0)), - ..Self::default() - } - } - - /// Creates a new [`PathBuilder`] to build a Fill path. - pub fn fill() -> Self { - Self::default() - } - - /// Sets the style of the [`PathBuilder`]. - pub fn with_style(self, style: PathStyle) -> Self { - Self { style, ..self } - } - - /// Sets the dash array of the [`PathBuilder`]. - /// - /// [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/stroke-dasharray) - pub fn dash_array(mut self, dash_array: &[Pixels]) -> Self { - // If an odd number of values is provided, then the list of values is repeated to yield an even number of values. - // Thus, 5,3,2 is equivalent to 5,3,2,5,3,2. - let array = if dash_array.len() % 2 == 1 { - let mut new_dash_array = dash_array.to_vec(); - new_dash_array.extend_from_slice(dash_array); - new_dash_array - } else { - dash_array.to_vec() - }; - - self.dash_array = Some(array); - self - } - - /// Move the current point to the given point. - #[inline] - pub fn move_to(&mut self, to: Point) { - self.raw.move_to(to.into()); - } - - /// Draw a straight line from the current point to the given point. - #[inline] - pub fn line_to(&mut self, to: Point) { - self.raw.line_to(to.into()); - } - - /// Draw a curve from the current point to the given point, using the given control point. - #[inline] - pub fn curve_to(&mut self, to: Point, ctrl: Point) { - self.raw.quadratic_bezier_to(ctrl.into(), to.into()); - } - - /// Adds a cubic Bézier to the [`Path`] given its two control points - /// and its end point. - #[inline] - pub fn cubic_bezier_to( - &mut self, - to: Point, - control_a: Point, - control_b: Point, - ) { - self.raw - .cubic_bezier_to(control_a.into(), control_b.into(), to.into()); - } - - /// Adds an elliptical arc. - pub fn arc_to( - &mut self, - radii: Point, - x_rotation: Pixels, - large_arc: bool, - sweep: bool, - to: Point, - ) { - self.raw.arc_to( - radii.into(), - Angle::degrees(x_rotation.into()), - ArcFlags { large_arc, sweep }, - to.into(), - ); - } - - /// Equivalent to `arc_to` in relative coordinates. - pub fn relative_arc_to( - &mut self, - radii: Point, - x_rotation: Pixels, - large_arc: bool, - sweep: bool, - to: Point, - ) { - self.raw.relative_arc_to( - radii.into(), - Angle::degrees(x_rotation.into()), - ArcFlags { large_arc, sweep }, - to.into(), - ); - } - - /// Adds a polygon. - pub fn add_polygon(&mut self, points: &[Point], closed: bool) { - let points = points.iter().copied().map(|p| p.into()).collect::>(); - self.raw.add_polygon(Polygon { - points: points.as_ref(), - closed, - }); - } - - /// Close the current sub-path. - #[inline] - pub fn close(&mut self) { - self.raw.close(); - } - - /// Applies a transform to the path. - #[inline] - pub fn transform(&mut self, transform: Transform) { - self.transform = Some(transform); - } - - /// Applies a translation to the path. - #[inline] - pub fn translate(&mut self, to: Point) { - if let Some(transform) = self.transform { - self.transform = Some(transform.then_translate(Vector2D::new(to.x.0, to.y.0))); - } else { - self.transform = Some(Transform::translation(to.x.0, to.y.0)) - } - } - - /// Applies a scale to the path. - #[inline] - pub fn scale(&mut self, scale: f32) { - if let Some(transform) = self.transform { - self.transform = Some(transform.then_scale(scale, scale)); - } else { - self.transform = Some(Transform::scale(scale, scale)); - } - } - - /// Applies a rotation to the path. - /// - /// The `angle` is in degrees value in the range 0.0 to 360.0. - #[inline] - pub fn rotate(&mut self, angle: f32) { - let radians = angle.to_radians(); - if let Some(transform) = self.transform { - self.transform = Some(transform.then_rotate(Angle::radians(radians))); - } else { - self.transform = Some(Transform::rotation(Angle::radians(radians))); - } - } - - /// Builds into a [`Path`]. - #[inline] - pub fn build(self) -> Result, Error> { - let path = if let Some(transform) = self.transform { - self.raw.build().transformed(&transform) - } else { - self.raw.build() - }; - - match self.style { - PathStyle::Stroke(options) => Self::tessellate_stroke(self.dash_array, &path, &options), - PathStyle::Fill(options) => Self::tessellate_fill(&path, &options), - } - } - - fn tessellate_fill( - path: &lyon::path::Path, - options: &FillOptions, - ) -> Result, Error> { - // Will contain the result of the tessellation. - let mut buf: VertexBuffers = VertexBuffers::new(); - let mut tessellator = FillTessellator::new(); - - // Compute the tessellation. - tessellator.tessellate_path( - path, - options, - &mut BuffersBuilder::new(&mut buf, |vertex: FillVertex| vertex.position()), - )?; - - Ok(Self::build_path(buf)) - } - - fn tessellate_stroke( - dash_array: Option>, - path: &lyon::path::Path, - options: &StrokeOptions, - ) -> Result, Error> { - let path = if let Some(dash_array) = dash_array { - let measurements = lyon::algorithms::measure::PathMeasurements::from_path(path, 0.01); - let mut sampler = measurements - .create_sampler(path, lyon::algorithms::measure::SampleType::Normalized); - let mut builder = lyon::path::Path::builder(); - - let total_length = sampler.length(); - let dash_array_len = dash_array.len(); - let mut pos = 0.; - let mut dash_index = 0; - while pos < total_length { - let dash_length = dash_array[dash_index % dash_array_len].0; - let next_pos = (pos + dash_length).min(total_length); - if dash_index % 2 == 0 { - let start = pos / total_length; - let end = next_pos / total_length; - sampler.split_range(start..end, &mut builder); - } - pos = next_pos; - dash_index += 1; - } - - &builder.build() - } else { - path - }; - - // Will contain the result of the tessellation. - let mut buf: VertexBuffers = VertexBuffers::new(); - let mut tessellator = StrokeTessellator::new(); - - // Compute the tessellation. - tessellator.tessellate_path( - path, - options, - &mut BuffersBuilder::new(&mut buf, |vertex: StrokeVertex| vertex.position()), - )?; - - Ok(Self::build_path(buf)) - } - - /// Builds a [`Path`] from a [`lyon::tessellation::VertexBuffers`]. - pub fn build_path(buf: VertexBuffers) -> Path { - if buf.vertices.is_empty() { - return Path::new(Point::default()); - } - - let first_point = buf.vertices[0]; - - let mut path = Path::new(first_point.into()); - for i in 0..buf.indices.len() / 3 { - let i0 = buf.indices[i * 3] as usize; - let i1 = buf.indices[i * 3 + 1] as usize; - let i2 = buf.indices[i * 3 + 2] as usize; - - let v0 = buf.vertices[i0]; - let v1 = buf.vertices[i1]; - let v2 = buf.vertices[i2]; - - path.push_triangle( - (v0.into(), v1.into(), v2.into()), - (point(0., 1.), point(0., 1.), point(0., 1.)), - ); - } - - path - } -} diff --git a/crates/gpui_pre/src/platform.rs b/crates/gpui_pre/src/platform.rs deleted file mode 100644 index eeba999d..00000000 --- a/crates/gpui_pre/src/platform.rs +++ /dev/null @@ -1,3113 +0,0 @@ -mod app_menu; -mod keyboard; -mod keystroke; - -#[cfg(all(target_os = "linux", feature = "wayland"))] -#[expect(missing_docs)] -pub mod layer_shell; - -/// Types for configuring parent-anchored popup windows such as menus, dropdowns and tooltips. -pub mod popup; - -#[cfg(any(test, feature = "test-support", feature = "bench-support"))] -mod threaded_dispatcher; - -#[cfg(any(test, feature = "test-support", feature = "bench-support"))] -mod test; - -#[cfg(all(target_os = "macos", any(test, feature = "test-support")))] -mod visual_test; - -#[cfg(all( - feature = "screen-capture", - any(target_os = "windows", target_os = "linux", target_os = "freebsd",) -))] -pub mod scap_screen_capture; - -#[cfg(all( - any(target_os = "windows", target_os = "linux"), - feature = "screen-capture" -))] -pub(crate) type PlatformScreenCaptureFrame = scap::frame::Frame; -#[cfg(not(feature = "screen-capture"))] -pub(crate) type PlatformScreenCaptureFrame = (); -#[cfg(all(target_os = "macos", feature = "screen-capture"))] -pub(crate) type PlatformScreenCaptureFrame = core_video::image_buffer::CVImageBuffer; - -use crate::{ - Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds, - DEFAULT_WINDOW_SIZE, DevicePixels, DispatchEventResult, Edges, ExternalDragPayload, Font, - FontId, FontMetrics, FontRun, ForegroundExecutor, GlyphId, GpuSpecs, Hsla, ImageSource, Keymap, - LineLayout, Pixels, PlatformGestures, PlatformInput, Point, Priority, RenderGlyphParams, - RenderImage, RenderImageParams, RenderSvgParams, Scene, ShapedGlyph, ShapedRun, SharedString, - Size, SvgRenderer, SystemWindowTab, Task, Window, WindowControlArea, hash, point, px, size, -}; -#[cfg(any(target_os = "linux", target_os = "freebsd"))] -use anyhow::bail; -use anyhow::{Context as _, Result}; -use async_task::Runnable; -use futures::channel::oneshot; -#[cfg(any(test, feature = "test-support", feature = "bench-support"))] -use image::RgbaImage; -use image::codecs::gif::GifDecoder; -use image::{AnimationDecoder as _, DynamicImage, Frame}; -use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; -use scheduler::Instant; -pub use scheduler::RunnableMeta; -use schemars::JsonSchema; -use seahash::SeaHasher; -use serde::{Deserialize, Serialize}; -use smallvec::SmallVec; -use std::borrow::Cow; -use std::hash::{Hash, Hasher}; -use std::io::Cursor; -use std::ops; -use std::time::Duration; -use std::{ - ffi::OsString, - fmt::{self, Debug}, - ops::Range, - path::{Path, PathBuf}, - rc::Rc, - sync::Arc, -}; -use strum::EnumIter; -use uuid::Uuid; - -pub use app_menu::*; -pub use keyboard::*; -pub use keystroke::*; - -#[cfg(any(test, feature = "test-support", feature = "bench-support"))] -pub(crate) use test::*; - -#[cfg(any(test, feature = "test-support"))] -pub use test::{TestDispatcher, TestScreenCaptureSource, TestScreenCaptureStream}; - -#[cfg(any(test, feature = "test-support", feature = "bench-support"))] -pub use threaded_dispatcher::ThreadedDispatcher; - -#[cfg(all(target_os = "macos", any(test, feature = "test-support")))] -pub use visual_test::VisualTestPlatform; - -// TODO(jk): return an enum instead of a string -/// Return which compositor we're guessing we'll use. -/// Does not attempt to connect to the given compositor. -#[cfg(any(target_os = "linux", target_os = "freebsd"))] -#[inline] -pub fn guess_compositor() -> &'static str { - if std::env::var_os("ZED_HEADLESS").is_some() { - return "Headless"; - } - - #[cfg(feature = "wayland")] - let wayland_display = std::env::var_os("WAYLAND_DISPLAY"); - #[cfg(not(feature = "wayland"))] - let wayland_display: Option = None; - - #[cfg(feature = "x11")] - let x11_display = std::env::var_os("DISPLAY"); - #[cfg(not(feature = "x11"))] - let x11_display: Option = None; - - let use_wayland = wayland_display.is_some_and(|display| !display.is_empty()); - let use_x11 = x11_display.is_some_and(|display| !display.is_empty()); - - if use_wayland { - "Wayland" - } else if use_x11 { - "X11" - } else { - "Headless" - } -} - -#[expect(missing_docs)] -pub trait Platform: 'static { - fn background_executor(&self) -> BackgroundExecutor; - fn foreground_executor(&self) -> ForegroundExecutor; - fn text_system(&self) -> Arc; - - fn run(&self, on_finish_launching: Box); - fn quit(&self); - fn restart(&self, binary_path: Option, arguments: Vec); - fn activate(&self, ignoring_other_apps: bool); - fn hide(&self); - fn hide_other_apps(&self); - fn unhide_other_apps(&self); - - fn displays(&self) -> Vec>; - fn primary_display(&self) -> Option>; - fn active_window(&self) -> Option; - fn window_stack(&self) -> Option> { - None - } - - fn is_screen_capture_supported(&self) -> bool { - false - } - - fn screen_capture_sources( - &self, - ) -> oneshot::Receiver>>> { - let (sources_tx, sources_rx) = oneshot::channel(); - sources_tx - .send(Err(anyhow::anyhow!( - "gpui was compiled without the screen-capture feature" - ))) - .ok(); - sources_rx - } - - fn open_window( - &self, - handle: AnyWindowHandle, - options: WindowParams, - ) -> anyhow::Result>; - - /// Returns the appearance of the application's windows. - fn window_appearance(&self) -> WindowAppearance; - - /// Overrides the appearance (light/dark) applied to the app's windows, independent - /// of the OS-wide setting. Pass `None` to clear the override and follow the system - /// again. The override is reflected by [`Platform::window_appearance`]. - /// - /// Currently only implemented on macOS, where it sets `NSApplication.appearance` so - /// the native window chrome (the window border and titlebar) of every window matches - /// a dark app theme even when the system is in light mode (or vice versa). A no-op on - /// other platforms. - fn set_window_appearance(&self, _appearance: Option) {} - - /// Returns the window button layout configuration when supported. - fn button_layout(&self) -> Option { - None - } - - fn open_url(&self, url: &str); - fn on_open_urls(&self, callback: Box)>); - fn register_url_scheme(&self, url: &str) -> Task>; - - fn prompt_for_paths( - &self, - options: PathPromptOptions, - ) -> oneshot::Receiver>>>; - fn prompt_for_new_path( - &self, - directory: &Path, - suggested_name: Option<&str>, - ) -> oneshot::Receiver>>; - fn can_select_mixed_files_and_dirs(&self) -> bool; - fn reveal_path(&self, path: &Path); - fn open_with_system(&self, path: &Path); - - fn on_quit(&self, callback: Box bool>); - fn on_reopen(&self, callback: Box); - fn on_system_wake(&self, callback: Box); - - // Mobile platform methods. On mobile the OS owns the application - // lifecycle: apps are backgrounded, foregrounded, and killed at the - // system's discretion, and must react rather than decide. - - /// Registers a callback invoked whenever the application's lifecycle - /// phase changes. See [`AppLifecyclePhase`] for the phase vocabulary and - /// its mapping onto iOS and Android. - /// - /// Desktop platforms never invoke this. - fn on_app_lifecycle(&self, _callback: Box) {} - - /// Registers a callback invoked when the OS signals memory pressure - /// (iOS `didReceiveMemoryWarning`, Android `onTrimMemory`). - /// - /// Desktop platforms never invoke this. - fn on_memory_warning(&self, _callback: Box) {} - - /// The platform's gesture recognition services, if it provides any - /// beyond gpui's portable recognizers. See - /// [`PlatformGestures`](crate::PlatformGestures). - fn gestures(&self) -> Option> { - None - } - - fn set_menus(&self, menus: Vec, keymap: &Keymap); - fn get_menus(&self) -> Option> { - None - } - - fn set_dock_menu(&self, menu: Vec, keymap: &Keymap); - fn perform_dock_menu_action(&self, _action: usize) {} - fn add_recent_document(&self, _path: &Path) {} - fn update_jump_list( - &self, - _menus: Vec, - _entries: Vec>, - ) -> Task>> { - Task::ready(Vec::new()) - } - fn on_app_menu_action(&self, callback: Box); - fn on_will_open_app_menu(&self, callback: Box); - fn on_validate_app_menu_command(&self, callback: Box bool>); - - fn thermal_state(&self) -> ThermalState; - fn on_thermal_state_change(&self, callback: Box); - - /// Sets the application's process-wide identity and user-visible name. - /// - /// The identifier is used for platform identity mechanisms such as the - /// Windows AppUserModelID. The name is used wherever the operating system - /// presents the application to the user. Call this once, early in startup, - /// before opening windows or posting notifications. - fn set_app_identity(&self, identifier: &str, name: &str) { - _ = (identifier, name); - } - - /// Posts a notification to the operating system's notification center. - /// - /// Posting a notification whose [`SystemNotification::tag`] matches an - /// earlier one replaces that notification where the platform supports it. - /// No-op on platforms without notification support, or when delivery is - /// unavailable (e.g. authorization was denied). - fn show_system_notification(&self, notification: SystemNotification) { - _ = notification; - } - - /// Removes the delivered or pending notification with this tag. - /// - /// Best-effort: some platforms cannot retract a notification once shown, - /// in which case it ages out of the notification center on its own. - fn dismiss_system_notification(&self, tag: &str) { - _ = tag; - } - - /// Registers the callback invoked when the user activates a system - /// notification, either by clicking its body or one of its action - /// buttons. - /// - /// Implementations must invoke the callback on the main thread. - fn on_system_notification_response( - &self, - callback: Box, - ) { - _ = callback; - } - - fn compositor_name(&self) -> &'static str { - "" - } - fn app_path(&self) -> Result; - fn path_for_auxiliary_executable(&self, name: &str) -> Result; - - fn set_cursor_style(&self, style: CursorStyle); - - /// Hides the mouse cursor until the user moves the mouse over one of - /// this application's windows. - fn hide_cursor_until_mouse_moves(&self); - - /// Returns whether the mouse cursor is currently visible. - fn is_cursor_visible(&self) -> bool; - - fn should_auto_hide_scrollbars(&self) -> bool; - - fn read_from_clipboard(&self) -> Option; - fn write_to_clipboard(&self, item: ClipboardItem); - - /// Reads the clipboard, resolving once its contents are available. - /// - /// Most platforms read synchronously and return a ready task. Platforms - /// whose clipboard access is inherently asynchronous and permission-gated - /// (e.g. the browser's async clipboard API) override this method; on those - /// platforms [`Platform::read_from_clipboard`] cannot return the clipboard - /// contents, so callers that can await should prefer this method. - fn read_from_clipboard_async(&self) -> Task, ClipboardReadError>> { - Task::ready(Ok(self.read_from_clipboard())) - } - - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - fn read_from_primary(&self) -> Option; - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - fn write_to_primary(&self, item: ClipboardItem); - - #[cfg(target_os = "macos")] - fn read_from_find_pasteboard(&self) -> Option; - #[cfg(target_os = "macos")] - fn write_to_find_pasteboard(&self, item: ClipboardItem); - - fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task>; - fn read_credentials(&self, url: &str) -> Task)>>>; - fn delete_credentials(&self, url: &str) -> Task>; - - fn keyboard_layout(&self) -> Box; - fn keyboard_mapper(&self) -> Rc; - fn on_keyboard_layout_change(&self, callback: Box); -} - -/// A handle to a platform's display, e.g. a monitor or laptop screen. -pub trait PlatformDisplay: Debug { - /// Get the ID for this display - fn id(&self) -> DisplayId; - - /// Returns a stable identifier for this display that can be persisted and used - /// across system restarts. - fn uuid(&self) -> Result; - - /// Get the bounds for this display - fn bounds(&self) -> Bounds; - - /// Get the visible bounds for this display, excluding taskbar/dock areas. - /// This is the usable area where windows can be placed without being obscured. - /// Defaults to the full display bounds if not overridden. - fn visible_bounds(&self) -> Bounds { - self.bounds() - } - - /// Get the default bounds for this display to place a window - fn default_bounds(&self) -> Bounds { - let bounds = self.bounds(); - let center = bounds.center(); - let clipped_window_size = DEFAULT_WINDOW_SIZE.min(&bounds.size); - - let offset = clipped_window_size / 2.0; - let origin = point(center.x - offset.width, center.y - offset.height); - Bounds::new(origin, clipped_window_size) - } -} - -/// A notification posted to the operating system's notification center, -/// rather than rendered as in-app UI. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct SystemNotification { - /// Stable identity for the notification. Posting a new notification with - /// the same tag replaces the previous one where the platform supports it, - /// and responses carry the tag back to the application. - pub tag: SharedString, - /// The notification's headline. - pub title: SharedString, - /// Additional text displayed below the title. - pub body: SharedString, - /// Buttons offered on the notification. Platforms that cannot display - /// action buttons show the notification without them. - pub actions: Vec, -} - -/// A button offered on a [`SystemNotification`]. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct SystemNotificationAction { - /// Identifies the action in [`SystemNotificationResponse::action_id`] - /// when the user presses this button. - pub id: SharedString, - /// The button's user-visible label. - pub label: SharedString, -} - -/// The user's activation of a [`SystemNotification`]. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct SystemNotificationResponse { - /// The [`SystemNotification::tag`] of the activated notification. - pub tag: SharedString, - /// The pressed action button's [`SystemNotificationAction::id`], or - /// `None` when the user activated the notification body itself. - pub action_id: Option, -} - -/// Thermal state of the system -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ThermalState { - /// System has no thermal constraints - Nominal, - /// System is slightly constrained, reduce discretionary work - Fair, - /// System is moderately constrained, reduce CPU/GPU intensive work - Serious, - /// System is critically constrained, minimize all resource usage - Critical, -} - -/// Metadata for a given [ScreenCaptureSource] -#[derive(Clone)] -pub struct SourceMetadata { - /// Opaque identifier of this screen. - pub id: u64, - /// Human-readable label for this source. - pub label: Option, - /// Whether this source is the main display. - pub is_main: Option, - /// Video resolution of this source. - pub resolution: Size, -} - -/// A source of on-screen video content that can be captured. -pub trait ScreenCaptureSource { - /// Returns metadata for this source. - fn metadata(&self) -> Result; - - /// Start capture video from this source, invoking the given callback - /// with each frame. - fn stream( - &self, - foreground_executor: &ForegroundExecutor, - frame_callback: Box, - ) -> oneshot::Receiver>>; -} - -/// A video stream captured from a screen. -pub trait ScreenCaptureStream { - /// Returns metadata for this source. - fn metadata(&self) -> Result; -} - -/// A frame of video captured from a screen. -pub struct ScreenCaptureFrame(pub PlatformScreenCaptureFrame); - -/// An opaque identifier for a hardware display -#[derive(PartialEq, Eq, Hash, Copy, Clone)] -pub struct DisplayId(pub(crate) u64); - -impl DisplayId { - /// Create a new `DisplayId` from a raw platform display identifier. - pub fn new(id: u64) -> Self { - Self(id) - } -} - -impl From for DisplayId { - fn from(id: u64) -> Self { - Self(id) - } -} - -impl From for u64 { - fn from(id: DisplayId) -> Self { - id.0 - } -} - -impl Debug for DisplayId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "DisplayId({})", self.0) - } -} - -/// Which part of the window to resize -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ResizeEdge { - /// The top edge - Top, - /// The top right corner - TopRight, - /// The right edge - Right, - /// The bottom right corner - BottomRight, - /// The bottom edge - Bottom, - /// The bottom left corner - BottomLeft, - /// The left edge - Left, - /// The top left corner - TopLeft, -} - -/// A type to describe the appearance of a window -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)] -pub enum WindowDecorations { - #[default] - /// Server side decorations - Server, - /// Client side decorations - Client, -} - -/// A type to describe how this window is currently configured -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)] -pub enum Decorations { - /// The window is configured to use server side decorations - #[default] - Server, - /// The window is configured to use client side decorations - Client { - /// The edge tiling state - tiling: Tiling, - }, -} - -/// What window controls this platform supports -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct WindowControls { - /// Whether this platform supports fullscreen - pub fullscreen: bool, - /// Whether this platform supports maximize - pub maximize: bool, - /// Whether this platform supports minimize - pub minimize: bool, - /// Whether this platform supports a window menu - pub window_menu: bool, -} - -impl Default for WindowControls { - fn default() -> Self { - // Assume that we can do anything, unless told otherwise - Self { - fullscreen: true, - maximize: true, - minimize: true, - window_menu: true, - } - } -} - -/// A window control button type used in [`WindowButtonLayout`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum WindowButton { - /// The minimize button - Minimize, - /// The maximize button - Maximize, - /// The close button - Close, -} - -impl WindowButton { - /// Returns a stable element ID for rendering this button. - pub fn id(&self) -> &'static str { - match self { - WindowButton::Minimize => "minimize", - WindowButton::Maximize => "maximize", - WindowButton::Close => "close", - } - } - - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - fn index(&self) -> usize { - match self { - WindowButton::Minimize => 0, - WindowButton::Maximize => 1, - WindowButton::Close => 2, - } - } -} - -/// Maximum number of [`WindowButton`]s per side in the titlebar. -pub const MAX_BUTTONS_PER_SIDE: usize = 3; - -/// Describes which [`WindowButton`]s appear on each side of the titlebar. -/// -/// On Linux, this is read from the desktop environment's configuration -/// (e.g. GNOME's `gtk-decoration-layout` gsetting) via [`WindowButtonLayout::parse`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct WindowButtonLayout { - /// Buttons on the left side of the titlebar. - pub left: [Option; MAX_BUTTONS_PER_SIDE], - /// Buttons on the right side of the titlebar. - pub right: [Option; MAX_BUTTONS_PER_SIDE], -} - -#[cfg(any(target_os = "linux", target_os = "freebsd"))] -impl WindowButtonLayout { - /// Returns Zed's built-in fallback button layout for Linux titlebars. - pub fn linux_default() -> Self { - Self { - left: [None; MAX_BUTTONS_PER_SIDE], - right: [ - Some(WindowButton::Minimize), - Some(WindowButton::Maximize), - Some(WindowButton::Close), - ], - } - } - - /// Parses a GNOME-style `button-layout` string (e.g. `"close,minimize:maximize"`). - pub fn parse(layout_string: &str) -> Result { - fn parse_side( - s: &str, - seen_buttons: &mut [bool; MAX_BUTTONS_PER_SIDE], - unrecognized: &mut Vec, - ) -> [Option; MAX_BUTTONS_PER_SIDE] { - let mut result = [None; MAX_BUTTONS_PER_SIDE]; - let mut i = 0; - for name in s.split(',') { - let trimmed = name.trim(); - if trimmed.is_empty() { - continue; - } - let button = match trimmed { - "minimize" => Some(WindowButton::Minimize), - "maximize" => Some(WindowButton::Maximize), - "close" => Some(WindowButton::Close), - other => { - unrecognized.push(other.to_string()); - None - } - }; - if let Some(button) = button { - if seen_buttons[button.index()] { - continue; - } - if let Some(slot) = result.get_mut(i) { - *slot = Some(button); - seen_buttons[button.index()] = true; - i += 1; - } - } - } - result - } - - let (left_str, right_str) = layout_string.split_once(':').unwrap_or(("", layout_string)); - let mut unrecognized = Vec::new(); - let mut seen_buttons = [false; MAX_BUTTONS_PER_SIDE]; - let layout = Self { - left: parse_side(left_str, &mut seen_buttons, &mut unrecognized), - right: parse_side(right_str, &mut seen_buttons, &mut unrecognized), - }; - - if !unrecognized.is_empty() - && layout.left.iter().all(Option::is_none) - && layout.right.iter().all(Option::is_none) - { - bail!( - "button layout string {:?} contains no valid buttons (unrecognized: {})", - layout_string, - unrecognized.join(", ") - ); - } - - Ok(layout) - } - - /// Formats the layout back into a GNOME-style `button-layout` string. - #[cfg(test)] - pub fn format(&self) -> String { - fn format_side(buttons: &[Option; MAX_BUTTONS_PER_SIDE]) -> String { - buttons - .iter() - .flatten() - .map(|button| match button { - WindowButton::Minimize => "minimize", - WindowButton::Maximize => "maximize", - WindowButton::Close => "close", - }) - .collect::>() - .join(",") - } - - format!("{}:{}", format_side(&self.left), format_side(&self.right)) - } -} - -/// A type to describe which sides of the window are currently tiled in some way -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)] -pub struct Tiling { - /// Whether the top edge is tiled - pub top: bool, - /// Whether the left edge is tiled - pub left: bool, - /// Whether the right edge is tiled - pub right: bool, - /// Whether the bottom edge is tiled - pub bottom: bool, -} - -impl Tiling { - /// Initializes a [`Tiling`] type with all sides tiled - pub fn tiled() -> Self { - Self { - top: true, - left: true, - right: true, - bottom: true, - } - } - - /// Whether any edge is tiled - pub fn is_tiled(&self) -> bool { - self.top || self.left || self.right || self.bottom - } -} - -/// Callbacks for the accessibility adapter. -pub struct A11yCallbacks { - /// Called when the adapter is activated (a screen reader connects). - pub activation: Box Option + Send + 'static>, - /// Called when an action is requested by the screen reader. - pub action: Box, - /// Called when the adapter is deactivated (screen reader disconnects). - pub deactivation: Box, -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] -#[expect(missing_docs)] -pub struct RequestFrameOptions { - /// Whether a presentation is required. - pub require_presentation: bool, - /// Force refresh of all rendering states when true. - pub force_render: bool, -} - -/// The application's lifecycle phase, as owned and reported by a mobile OS. -/// -/// `Inactive` means visible but not receiving input (a system dialog on -/// top), while `Background` means not visible at all, with process death -/// possible at any time thereafter. -/// -/// | Phase | iOS | Android | -/// |--------------|------------------------------|--------------| -/// | `Active` | `didBecomeActive` | `onResume` | -/// | `Inactive` | `willResignActive` | `onPause` | -/// | `Background` | `didEnterBackground` | `onStop` | -/// | `Foreground` | `willEnterForeground` | `onStart` | -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum AppLifecyclePhase { - /// Foreground and receiving input. - Active, - /// Foreground (visible) but not receiving input. - Inactive, - /// Not visible. The GPU surface may be destroyed while backgrounded and - /// the process may be killed without further notice. - Background, - /// Becoming visible again, before input is restored. - Foreground, -} - -/// Regions of a window that are obscured or reserved by the system. -/// -/// Mobile applications often share space in their window with system-specific -/// geometry, from keyboards to camera notches. In GPUI, all this is abstracted -/// into a single "inset" which should be overlaid on the window's bounds. -/// It is up to the application develop to determine how to handle these cases. -#[derive(Debug, Clone, Default, PartialEq)] -pub struct WindowInsets { - /// Regions covered by system UI or hardware: status bar, display - /// cutouts/notch, home indicator, navigation bars. - /// (iOS: `safeAreaInsets`. Android: `WindowInsets` of types - /// `systemBars() | displayCutout()`.) - pub safe_area: Edges, - /// The region covered by the keyboard, when present. - /// (iOS: derived from `keyboardWillShow`/frame-change notifications. - /// Android: `WindowInsets.Type.ime()`.) - pub ime: Edges, -} - -impl WindowInsets { - /// The combined inset content should avoid. - pub fn effective(&self) -> Edges { - Edges { - top: self.safe_area.top.max(self.ime.top), - right: self.safe_area.right.max(self.ime.right), - bottom: self.safe_area.bottom.max(self.ime.bottom), - left: self.safe_area.left.max(self.ime.left), - } - } -} - -/// A change in the state of the focused text input. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum TextInputStateChange { - /// An editable element gained focus. - FocusGained, - /// The focused editable element lost focus. - FocusLost, - /// The selection or caret moved - SelectionChanged, - /// The document content changed outside of platform-initiated edits. - ContentChanged, -} - -#[expect(missing_docs)] -pub trait PlatformWindow: HasWindowHandle + HasDisplayHandle { - fn bounds(&self) -> Bounds; - fn is_maximized(&self) -> bool; - fn window_bounds(&self) -> WindowBounds; - fn content_size(&self) -> Size; - fn resize(&mut self, size: Size); - fn scale_factor(&self) -> f32; - fn appearance(&self) -> WindowAppearance; - fn display(&self) -> Option>; - fn mouse_position(&self) -> Point; - fn modifiers(&self) -> Modifiers; - fn capslock(&self) -> Capslock; - fn set_input_handler(&mut self, input_handler: PlatformInputHandler); - fn take_input_handler(&mut self) -> Option; - /// Apply the focused text region's [`TextInputConfiguration`] to the - /// platform's text input session (e.g. attributes of the hidden editable - /// element on web). Called only when the configuration changes, because - /// reconfiguring a live input session can restart the IME connection. - fn set_text_input_configuration(&mut self, _configuration: TextInputConfiguration) {} - fn prompt( - &self, - level: PromptLevel, - msg: &str, - detail: Option<&str>, - answers: &[PromptButton], - ) -> Option>; - fn activate(&self); - /// Requests that the operating system draw attention to this window. - fn request_attention(&self) {} - fn is_active(&self) -> bool; - fn is_hovered(&self) -> bool; - fn background_appearance(&self) -> WindowBackgroundAppearance; - fn set_title(&mut self, title: &str); - fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance); - fn minimize(&self); - fn zoom(&self); - fn toggle_fullscreen(&self); - fn is_fullscreen(&self) -> bool; - fn frame_waker(&self) -> Option> { - None - } - fn on_request_frame(&self, callback: Box); - fn on_input(&self, callback: Box DispatchEventResult>); - fn on_active_status_change(&self, callback: Box); - fn on_hover_status_change(&self, callback: Box); - fn on_resize(&self, callback: Box, f32)>); - fn on_moved(&self, callback: Box); - fn on_should_close(&self, callback: Box bool>); - fn on_hit_test_window_control(&self, callback: Box Option>); - fn on_close(&self, callback: Box); - fn on_appearance_changed(&self, callback: Box); - fn on_button_layout_changed(&self, _callback: Box) {} - fn draw(&self, scene: &Scene); - fn schedule_frame(&self) {} - fn sprite_atlas(&self) -> Arc; - fn is_subpixel_rendering_supported(&self) -> bool; - - // macOS specific methods - fn get_title(&self) -> String { - String::new() - } - fn tabbed_windows(&self) -> Option> { - None - } - fn tab_bar_visible(&self) -> bool { - false - } - fn set_edited(&mut self, _edited: bool) {} - fn set_document_path(&self, _path: Option<&std::path::Path>) {} - fn toggle_simple_fullscreen(&self) {} - fn is_simple_fullscreen(&self) -> bool { - false - } - #[cfg(target_os = "macos")] - fn set_traffic_light_position(&self, _position: Point) {} - fn show_character_palette(&self) {} - fn titlebar_double_click(&self, _is_resizable: bool, _is_minimizable: bool) {} - fn on_move_tab_to_new_window(&self, _callback: Box) {} - fn on_merge_all_windows(&self, _callback: Box) {} - fn on_select_previous_tab(&self, _callback: Box) {} - fn on_select_next_tab(&self, _callback: Box) {} - fn on_toggle_tab_bar(&self, _callback: Box) {} - fn merge_all_windows(&self) {} - fn move_tab_to_new_window(&self) {} - fn toggle_window_tab_overview(&self) {} - fn set_tabbing_identifier(&self, _identifier: Option) {} - - #[cfg(target_os = "windows")] - fn get_raw_handle(&self) -> windows::Win32::Foundation::HWND; - - // Linux specific methods - fn inner_window_bounds(&self) -> WindowBounds { - self.window_bounds() - } - fn request_decorations(&self, _decorations: WindowDecorations) {} - fn show_window_menu(&self, _position: Point) {} - fn start_window_move(&self) {} - fn can_start_external_drag(&self) -> bool { - false - } - fn start_external_drag(&self, _payload: &ExternalDragPayload) -> bool { - false - } - fn start_window_resize(&self, _edge: ResizeEdge) {} - fn set_exclusive_zone(&self, _zone: Pixels) {} - #[cfg(all(target_os = "linux", feature = "wayland"))] - fn set_exclusive_edge(&self, _edge: layer_shell::Anchor) {} - fn set_input_region(&self, _region: Option<&[Bounds]>) {} - fn window_decorations(&self) -> Decorations { - Decorations::Server - } - fn set_app_id(&mut self, _app_id: &str) {} - fn map_window(&mut self) -> anyhow::Result<()> { - Ok(()) - } - fn window_controls(&self) -> WindowControls { - WindowControls::default() - } - fn set_client_inset(&self, _inset: Pixels) {} - fn gpu_specs(&self) -> Option; - - fn update_ime_position(&self, _bounds: Bounds); - - // Mobile platform methods. - - /// The regions of this window currently obscured or reserved by the - /// system. Zero on platforms without such regions. - fn insets(&self) -> WindowInsets { - WindowInsets::default() - } - - /// Registers a callback invoked whenever [`Self::insets`] change. - /// - /// Contract: fires continuously during animated transitions (Android - /// `WindowInsetsAnimation` progress; on iOS the platform interpolates - /// the keyboard animation curve on frame ticks) and is exact at rest. - fn on_insets_changed(&self, _callback: Box) {} - - /// Sets the handler for the system back action (Android back - /// button/gesture; no source on iOS or desktop). - fn set_back_handler(&self, _callback: Box) {} - - /// Declares whether the application would currently handle the system - /// back action (e.g. navigation depth > 0). - fn set_back_enabled(&self, _enabled: bool) {} - - /// Requests that the soft keyboard be shown. - fn show_soft_keyboard(&self) {} - - /// Requests that the soft keyboard be hidden. - fn hide_soft_keyboard(&self) {} - - /// Inform the operating system that the text input state has changed - fn text_input_state_changed(&self, _change: TextInputStateChange) {} - - fn play_system_bell(&self) {} - - /// Initialize the accessibility adapter with callbacks. - fn a11y_init(&self, _callbacks: A11yCallbacks) {} - - /// Provide a TreeUpdate to the accessibility adapter. - fn a11y_tree_update(&self, _tree_update: accesskit::TreeUpdate) {} - - /// Inform the adapter of updated window bounds. - fn a11y_update_window_bounds(&self) {} - - #[cfg(any(test, feature = "test-support", feature = "bench-support"))] - fn as_test(&mut self) -> Option<&mut TestWindow> { - None - } - - /// Renders the given scene to a texture and returns the pixel data as an RGBA image. - /// This does not present the frame to screen - useful for visual testing where we want - /// to capture what would be rendered without displaying it or requiring the window to be visible. - #[cfg(any(test, feature = "test-support"))] - fn render_to_image(&self, _scene: &Scene) -> Result { - anyhow::bail!("render_to_image not implemented for this platform") - } -} - -/// A renderer for headless windows that can produce real rendered output. -#[cfg(any(test, feature = "test-support", feature = "bench-support"))] -pub trait PlatformHeadlessRenderer { - /// Render a scene and return the result as an RGBA image. - fn render_scene_to_image( - &mut self, - scene: &Scene, - size: Size, - ) -> Result; - - /// Render a scene to an offscreen target without reading the result back. - /// - /// This is the headless analogue of presenting a frame: it performs the - /// same CPU-side scene encoding and GPU submission as drawing to a real - /// window, but doesn't block on GPU completion or copy pixels back. - fn render_scene(&mut self, scene: &Scene, size: Size) -> Result<()>; - - /// Returns the sprite atlas used by this renderer. - fn sprite_atlas(&self) -> Arc; -} - -/// Type alias for runnables with metadata. -/// Previously an enum with a single variant, now simplified to a direct type alias. -#[doc(hidden)] -pub type RunnableVariant = Runnable; - -#[doc(hidden)] -pub type TimerResolutionGuard = gpui_util::Deferred>; - -#[doc(hidden)] -pub enum TasksIncluded { - OnlyCompleted, - CompletedAndRunning, -} - -/// This type is public so that our test macro can generate and use it, but it should not -/// be considered part of our public API. -#[doc(hidden)] -pub trait PlatformDispatcher: Send + Sync { - fn is_main_thread(&self) -> bool; - fn dispatch(&self, runnable: RunnableVariant, priority: Priority); - fn dispatch_on_main_thread(&self, runnable: RunnableVariant, priority: Priority); - fn dispatch_after(&self, duration: Duration, runnable: RunnableVariant); - - fn dispatch_on_main_thread_when_idle( - &self, - runnable: RunnableVariant, - timeout: Option, - ) { - let _ = timeout; - self.dispatch_on_main_thread(runnable, Priority::Low); - } - - fn idle_time_remaining(&self) -> Option { - None - } - - fn spawn_realtime(&self, f: Box); - - fn now(&self) -> Instant { - Instant::now() - } - - fn increase_timer_resolution(&self) -> TimerResolutionGuard { - gpui_util::defer(Box::new(|| {})) - } - - #[cfg(any(test, feature = "test-support", feature = "bench-support"))] - fn as_test(&self) -> Option<&TestDispatcher> { - None - } - - // This cfg must match the `threaded_dispatcher` module's, which implements - // this method whenever it compiles. - #[cfg(any(test, feature = "test-support", feature = "bench-support"))] - fn as_threaded(&self) -> Option<&ThreadedDispatcher> { - None - } -} - -#[expect(missing_docs)] -pub trait PlatformTextSystem: Send + Sync { - fn add_fonts(&self, fonts: Vec>) -> Result<()>; - /// Get all available font names. - fn all_font_names(&self) -> Vec; - /// Get the font ID for a font descriptor. - fn font_id(&self, descriptor: &Font) -> Result; - /// Prewarm any system font caches needed to shape text. - fn prewarm_fonts(&self, _font_ids: &[FontId]) {} - /// Get metrics for a font. - fn font_metrics(&self, font_id: FontId) -> FontMetrics; - /// Get typographic bounds for a glyph. - fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; - /// Get the advance width for a glyph. - fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; - /// Get the glyph ID for a character. - fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option; - /// Get raster bounds for a glyph. - fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result>; - /// Rasterize a glyph. - fn rasterize_glyph( - &self, - params: &RenderGlyphParams, - raster_bounds: Bounds, - ) -> Result<(Size, Vec)>; - /// Layout a line of text with the given font runs. - fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout; - /// Returns the recommended text rendering mode for the given font and size. - fn recommended_rendering_mode(&self, _font_id: FontId, _font_size: Pixels) - -> TextRenderingMode; - /// Returns the dilation level to use for a glyph painted in the given color. - fn glyph_dilation_for_color(&self, _color: Hsla) -> u8 { - 0 - } -} - -#[expect(missing_docs)] -pub struct NoopTextSystem; - -#[expect(missing_docs)] -impl NoopTextSystem { - #[allow(dead_code)] - pub fn new() -> Self { - Self - } -} - -impl PlatformTextSystem for NoopTextSystem { - fn add_fonts(&self, _fonts: Vec>) -> Result<()> { - Ok(()) - } - - fn all_font_names(&self) -> Vec { - Vec::new() - } - - fn font_id(&self, _descriptor: &Font) -> Result { - Ok(FontId(1)) - } - - fn font_metrics(&self, _font_id: FontId) -> FontMetrics { - FontMetrics { - units_per_em: 1000, - ascent: 1025.0, - descent: -275.0, - line_gap: 0.0, - underline_position: -95.0, - underline_thickness: 60.0, - cap_height: 698.0, - x_height: 516.0, - bounding_box: Bounds { - origin: Point { - x: -260.0, - y: -245.0, - }, - size: Size { - width: 1501.0, - height: 1364.0, - }, - }, - } - } - - fn typographic_bounds(&self, _font_id: FontId, _glyph_id: GlyphId) -> Result> { - Ok(Bounds { - origin: Point { x: 54.0, y: 0.0 }, - size: size(392.0, 528.0), - }) - } - - fn advance(&self, _font_id: FontId, glyph_id: GlyphId) -> Result> { - Ok(size(600.0 * glyph_id.0 as f32, 0.0)) - } - - fn glyph_for_char(&self, _font_id: FontId, ch: char) -> Option { - Some(GlyphId(ch.len_utf16() as u32)) - } - - fn glyph_raster_bounds(&self, _params: &RenderGlyphParams) -> Result> { - Ok(Default::default()) - } - - fn rasterize_glyph( - &self, - _params: &RenderGlyphParams, - raster_bounds: Bounds, - ) -> Result<(Size, Vec)> { - Ok((raster_bounds.size, Vec::new())) - } - - fn layout_line(&self, text: &str, font_size: Pixels, _runs: &[FontRun]) -> LineLayout { - let mut position = px(0.); - let metrics = self.font_metrics(FontId(0)); - let em_width = font_size - * self - .advance(FontId(0), self.glyph_for_char(FontId(0), 'm').unwrap()) - .unwrap() - .width - / metrics.units_per_em as f32; - let mut glyphs = Vec::new(); - for (ix, c) in text.char_indices() { - if let Some(glyph) = self.glyph_for_char(FontId(0), c) { - glyphs.push(ShapedGlyph { - id: glyph, - position: point(position, px(0.)), - index: ix, - is_emoji: glyph.0 == 2, - }); - if glyph.0 == 2 { - position += em_width * 2.0; - } else { - position += em_width; - } - } else { - position += em_width - } - } - let mut runs = Vec::default(); - if !glyphs.is_empty() { - runs.push(ShapedRun { - font_id: FontId(0), - glyphs, - }); - } else { - position = px(0.); - } - - LineLayout { - font_size, - width: position, - ascent: font_size * (metrics.ascent / metrics.units_per_em as f32), - descent: font_size * (metrics.descent / metrics.units_per_em as f32), - runs, - len: text.len(), - } - } - - fn recommended_rendering_mode( - &self, - _font_id: FontId, - _font_size: Pixels, - ) -> TextRenderingMode { - TextRenderingMode::Grayscale - } -} - -// Adapted from https://github.com/microsoft/terminal/blob/1283c0f5b99a2961673249fa77c6b986efb5086c/src/renderer/atlas/dwrite.cpp -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -/// Compute gamma correction ratios for subpixel text rendering. -#[allow(dead_code)] -pub fn get_gamma_correction_ratios(gamma: f32) -> [f32; 4] { - const GAMMA_INCORRECT_TARGET_RATIOS: [[f32; 4]; 13] = [ - [0.0000 / 4.0, 0.0000 / 4.0, 0.0000 / 4.0, 0.0000 / 4.0], // gamma = 1.0 - [0.0166 / 4.0, -0.0807 / 4.0, 0.2227 / 4.0, -0.0751 / 4.0], // gamma = 1.1 - [0.0350 / 4.0, -0.1760 / 4.0, 0.4325 / 4.0, -0.1370 / 4.0], // gamma = 1.2 - [0.0543 / 4.0, -0.2821 / 4.0, 0.6302 / 4.0, -0.1876 / 4.0], // gamma = 1.3 - [0.0739 / 4.0, -0.3963 / 4.0, 0.8167 / 4.0, -0.2287 / 4.0], // gamma = 1.4 - [0.0933 / 4.0, -0.5161 / 4.0, 0.9926 / 4.0, -0.2616 / 4.0], // gamma = 1.5 - [0.1121 / 4.0, -0.6395 / 4.0, 1.1588 / 4.0, -0.2877 / 4.0], // gamma = 1.6 - [0.1300 / 4.0, -0.7649 / 4.0, 1.3159 / 4.0, -0.3080 / 4.0], // gamma = 1.7 - [0.1469 / 4.0, -0.8911 / 4.0, 1.4644 / 4.0, -0.3234 / 4.0], // gamma = 1.8 - [0.1627 / 4.0, -1.0170 / 4.0, 1.6051 / 4.0, -0.3347 / 4.0], // gamma = 1.9 - [0.1773 / 4.0, -1.1420 / 4.0, 1.7385 / 4.0, -0.3426 / 4.0], // gamma = 2.0 - [0.1908 / 4.0, -1.2652 / 4.0, 1.8650 / 4.0, -0.3476 / 4.0], // gamma = 2.1 - [0.2031 / 4.0, -1.3864 / 4.0, 1.9851 / 4.0, -0.3501 / 4.0], // gamma = 2.2 - ]; - - const NORM13: f32 = ((0x10000 as f64) / (255.0 * 255.0) * 4.0) as f32; - const NORM24: f32 = ((0x100 as f64) / (255.0) * 4.0) as f32; - - let index = ((gamma * 10.0).round() as usize).clamp(10, 22) - 10; - let ratios = GAMMA_INCORRECT_TARGET_RATIOS[index]; - - [ - ratios[0] * NORM13, - ratios[1] * NORM24, - ratios[2] * NORM13, - ratios[3] * NORM24, - ] -} - -#[derive(PartialEq, Eq, Hash, Clone)] -#[expect(missing_docs)] -pub enum AtlasKey { - Glyph(RenderGlyphParams), - Svg(RenderSvgParams), - Image(RenderImageParams), -} - -impl AtlasKey { - #[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) - )] - /// Returns the texture kind for this atlas key. - pub fn texture_kind(&self) -> AtlasTextureKind { - match self { - AtlasKey::Glyph(params) => { - if params.is_emoji { - AtlasTextureKind::Polychrome - } else if params.subpixel_rendering { - AtlasTextureKind::Subpixel - } else { - AtlasTextureKind::Monochrome - } - } - AtlasKey::Svg(_) => AtlasTextureKind::Monochrome, - AtlasKey::Image(_) => AtlasTextureKind::Polychrome, - } - } -} - -impl From for AtlasKey { - fn from(params: RenderGlyphParams) -> Self { - Self::Glyph(params) - } -} - -impl From for AtlasKey { - fn from(params: RenderSvgParams) -> Self { - Self::Svg(params) - } -} - -impl From for AtlasKey { - fn from(params: RenderImageParams) -> Self { - Self::Image(params) - } -} - -#[expect(missing_docs)] -pub trait PlatformAtlas { - fn get_or_insert_with<'a>( - &self, - key: &AtlasKey, - build: &mut dyn FnMut() -> Result, Cow<'a, [u8]>)>>, - ) -> Result>; - fn remove(&self, key: &AtlasKey); - - #[cfg(any(test, feature = "test-support", feature = "bench-support"))] - fn contains(&self, _key: &AtlasKey) -> bool { - false - } -} - -#[doc(hidden)] -pub struct AtlasTextureList { - pub textures: Vec>, - pub free_list: Vec, -} - -impl Default for AtlasTextureList { - fn default() -> Self { - Self { - textures: Vec::default(), - free_list: Vec::default(), - } - } -} - -impl ops::Index for AtlasTextureList { - type Output = Option; - - fn index(&self, index: usize) -> &Self::Output { - &self.textures[index] - } -} - -impl AtlasTextureList { - #[allow(unused)] - pub fn drain(&mut self) -> std::vec::Drain<'_, Option> { - self.free_list.clear(); - self.textures.drain(..) - } - - #[allow(dead_code)] - pub fn iter_mut(&mut self) -> impl DoubleEndedIterator { - self.textures.iter_mut().flatten() - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(C)] -#[expect(missing_docs)] -pub struct AtlasTile { - /// The texture this tile belongs to. - pub texture_id: AtlasTextureId, - /// The unique ID of this tile within its texture. - pub tile_id: TileId, - /// Padding around the tile content in pixels. - pub padding: u32, - /// The bounds of this tile within the texture. - pub bounds: Bounds, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[repr(C)] -#[expect(missing_docs)] -pub struct AtlasTextureId { - // We use u32 instead of usize for Metal Shader Language compatibility - /// The index of this texture in the atlas. - pub index: u32, - /// The kind of content stored in this texture. - pub kind: AtlasTextureKind, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[repr(C)] -#[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) -)] -#[expect(missing_docs)] -pub enum AtlasTextureKind { - Monochrome = 0, - Polychrome = 1, - Subpixel = 2, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -#[repr(C)] -#[expect(missing_docs)] -pub struct TileId(pub u32); - -impl From for TileId { - fn from(id: etagere::AllocId) -> Self { - Self(id.serialize()) - } -} - -impl From for etagere::AllocId { - fn from(id: TileId) -> Self { - Self::deserialize(id.0) - } -} - -#[expect(missing_docs)] -pub struct PlatformInputHandler { - cx: AsyncWindowContext, - handler: Box, -} - -#[expect(missing_docs)] -#[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) -)] -impl PlatformInputHandler { - pub fn new(cx: AsyncWindowContext, handler: Box) -> Self { - Self { cx, handler } - } - - pub fn selected_text_range(&mut self, ignore_disabled_input: bool) -> Option { - self.cx - .update(|window, cx| { - self.handler - .selected_text_range(ignore_disabled_input, window, cx) - }) - .ok() - .flatten() - } - - #[cfg_attr(target_os = "windows", allow(dead_code))] - pub fn marked_text_range(&mut self) -> Option> { - self.cx - .update(|window, cx| self.handler.marked_text_range(window, cx)) - .ok() - .flatten() - } - - #[cfg_attr( - any(target_os = "linux", target_os = "freebsd", target_os = "windows"), - allow(dead_code) - )] - pub fn text_for_range( - &mut self, - range_utf16: Range, - adjusted: &mut Option>, - ) -> Option { - self.cx - .update(|window, cx| { - self.handler - .text_for_range(range_utf16, adjusted, window, cx) - }) - .ok() - .flatten() - } - - pub fn replace_text_in_range(&mut self, replacement_range: Option>, text: &str) { - self.cx - .update(|window, cx| { - self.handler - .replace_text_in_range(replacement_range, text, window, cx); - }) - .ok(); - } - - pub fn replace_and_mark_text_in_range( - &mut self, - range_utf16: Option>, - new_text: &str, - new_selected_range: Option>, - ) { - self.cx - .update(|window, cx| { - self.handler.replace_and_mark_text_in_range( - range_utf16, - new_text, - new_selected_range, - window, - cx, - ) - }) - .ok(); - } - - #[cfg_attr(target_os = "windows", allow(dead_code))] - pub fn unmark_text(&mut self) { - self.cx - .update(|window, cx| self.handler.unmark_text(window, cx)) - .ok(); - } - - pub fn paste(&mut self, item: ClipboardItem) { - self.cx - .update(|window, cx| self.handler.paste(item, window, cx)) - .ok(); - } - - pub fn bounds_for_range(&mut self, range_utf16: Range) -> Option> { - self.cx - .update(|window, cx| self.handler.bounds_for_range(range_utf16, window, cx)) - .ok() - .flatten() - } - - #[allow(dead_code)] - pub fn apple_press_and_hold_enabled(&mut self) -> bool { - self.handler.apple_press_and_hold_enabled() - } - - pub fn dispatch_input(&mut self, input: &str, window: &mut Window, cx: &mut App) { - self.handler.replace_text_in_range(None, input, window, cx); - } - - pub fn compute_ime_candidate_bounds( - marked_range: Option>, - selection: &UTF16Selection, - mut bounds_for_range: impl FnMut(Range) -> Option>, - ) -> Option> { - if let Some(marked_range) = marked_range { - // Default to the start of the marked (composing) range. - let mut line_start = marked_range.start; - - // Walk backward from the caret looking for a line break. A change in - // the Y coordinate means we crossed into the previous visual line, so - // the line start is one position after the break point. - let caret = selection.range.end; - if let Some(caret_bounds) = bounds_for_range(caret..caret) { - for i in (marked_range.start..caret).rev() { - if let Some(b) = bounds_for_range(i..i) { - if (b.origin.y - caret_bounds.origin.y).abs() > px(0.1) { - line_start = i + 1; - break; - } - } - } - } - bounds_for_range(line_start..line_start) - } else { - // No active composition — use the selection endpoint. - let offset = if selection.reversed { - selection.range.start - } else { - selection.range.end - }; - bounds_for_range(offset..offset) - } - } - - pub fn selected_bounds(&mut self, window: &mut Window, cx: &mut App) -> Option> { - let marked_range = self.handler.marked_text_range(window, cx); - let selection = self.handler.selected_text_range(true, window, cx)?; - Self::compute_ime_candidate_bounds(marked_range, &selection, |range| { - self.handler.bounds_for_range(range, window, cx) - }) - } - - pub fn ime_candidate_bounds(&mut self) -> Option> { - let marked_range = self.marked_text_range(); - let selection = self.selected_text_range(true)?; - Self::compute_ime_candidate_bounds(marked_range, &selection, |range| { - self.bounds_for_range(range) - }) - } - - #[allow(unused)] - pub fn character_index_for_point(&mut self, point: Point) -> Option { - self.cx - .update(|window, cx| self.handler.character_index_for_point(point, window, cx)) - .ok() - .flatten() - } - - /// See [`InputHandler::set_selected_text_range`]. - pub fn set_selected_text_range(&mut self, range_utf16: Range) { - self.cx - .update(|window, cx| { - self.handler - .set_selected_text_range(range_utf16, window, cx) - }) - .ok(); - } - - /// See [`InputHandler::element_bounds`]. - pub fn element_bounds(&mut self) -> Option> { - self.cx - .update(|window, cx| self.handler.element_bounds(window, cx)) - .ok() - .flatten() - } - - /// See [`InputHandler::text_length_utf16`]. - pub fn text_length_utf16(&mut self) -> Option { - self.cx - .update(|window, cx| self.handler.text_length_utf16(window, cx)) - .ok() - .flatten() - } - - #[allow(dead_code)] - pub fn accepts_text_input(&mut self, window: &mut Window, cx: &mut App) -> bool { - self.handler.accepts_text_input(window, cx) - } - - #[allow(dead_code)] - pub fn query_accepts_text_input(&mut self) -> bool { - self.cx - .update(|window, cx| self.handler.accepts_text_input(window, cx)) - .unwrap_or(true) - } - - /// See [`InputHandler::prefers_ime_for_printable_keys`]. - /// - /// This is not a pure delegation to the handler: while a multi-stroke binding is pending this - /// returns `false` regardless of the handler's preference, because the next printable key may - /// complete a binding whose prefix already bypassed the IME. - pub fn query_prefers_ime_for_printable_keys(&mut self) -> bool { - self.cx - .update(|window, cx| { - // The next printable key may complete a chord whose prefix bypassed the IME. - !window.has_pending_keystrokes() - && self.handler.prefers_ime_for_printable_keys(window, cx) - }) - .unwrap_or(false) - } - - /// See [`InputHandler::text_input_configuration`]. - pub fn text_input_configuration( - &mut self, - window: &mut Window, - cx: &mut App, - ) -> TextInputConfiguration { - self.handler.text_input_configuration(window, cx) - } - - /// See [`InputHandler::text_input_editable_range`]. - pub fn text_input_editable_range(&mut self) -> Option> { - self.cx - .update(|window, cx| self.handler.text_input_editable_range(window, cx)) - .ok() - .flatten() - } -} - -/// A struct representing a selection in a text buffer, in UTF16 characters. -/// This is different from a range because the head may be before the tail. -#[derive(Debug)] -pub struct UTF16Selection { - /// The range of text in the document this selection corresponds to - /// in UTF16 characters. - pub range: Range, - /// Whether the head of this selection is at the start (true), or end (false) - /// of the range - pub reversed: bool, -} - -/// Zed's interface for handling text input from the platform's IME system -/// This is currently a 1:1 exposure of the NSTextInputClient API: -/// -/// -pub trait InputHandler: 'static { - /// Get the range of the user's currently selected text, if any - /// Corresponds to [selectedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438242-selectedrange) - /// - /// Return value is in terms of UTF-16 characters, from 0 to the length of the document - fn selected_text_range( - &mut self, - ignore_disabled_input: bool, - window: &mut Window, - cx: &mut App, - ) -> Option; - - /// Get the range of the currently marked text, if any - /// Corresponds to [markedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438250-markedrange) - /// - /// Return value is in terms of UTF-16 characters, from 0 to the length of the document - fn marked_text_range(&mut self, window: &mut Window, cx: &mut App) -> Option>; - - /// Get the text for the given document range in UTF-16 characters - /// Corresponds to [attributedSubstring(forProposedRange: actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438238-attributedsubstring) - /// - /// range_utf16 is in terms of UTF-16 characters - fn text_for_range( - &mut self, - range_utf16: Range, - adjusted_range: &mut Option>, - window: &mut Window, - cx: &mut App, - ) -> Option; - - /// Replace the text in the given document range with the given text - /// Corresponds to [insertText(_:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438258-inserttext) - /// - /// replacement_range is in terms of UTF-16 characters - fn replace_text_in_range( - &mut self, - replacement_range: Option>, - text: &str, - window: &mut Window, - cx: &mut App, - ); - - /// Replace the text in the given document range with the given text, - /// and mark the given text as part of an IME 'composing' state - /// Corresponds to [setMarkedText(_:selectedRange:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438246-setmarkedtext) - /// - /// range_utf16 is in terms of UTF-16 characters - /// new_selected_range is in terms of UTF-16 characters - fn replace_and_mark_text_in_range( - &mut self, - range_utf16: Option>, - new_text: &str, - new_selected_range: Option>, - window: &mut Window, - cx: &mut App, - ); - - /// Remove the IME 'composing' state from the document - /// Corresponds to [unmarkText()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438239-unmarktext) - fn unmark_text(&mut self, window: &mut Window, cx: &mut App); - - /// Insert a platform-initiated paste at the current selection. - /// - /// Platforms that deliver paste as an input event rather than through an - /// application-defined action (e.g. the DOM `paste` event on web) call - /// this with the full clipboard contents. The default implementation - /// inserts only the plain-text portion of the item. - fn paste(&mut self, item: ClipboardItem, window: &mut Window, cx: &mut App) { - if let Some(text) = item.text() { - self.replace_text_in_range(None, &text, window, cx); - } - } - - /// Get the bounds of the given document range in screen coordinates - /// Corresponds to [firstRect(forCharacterRange:actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438240-firstrect) - /// - /// This is used for positioning the IME candidate window - fn bounds_for_range( - &mut self, - range_utf16: Range, - window: &mut Window, - cx: &mut App, - ) -> Option>; - - /// Get the character offset for the given point in terms of UTF16 characters - /// - /// Corresponds to [characterIndexForPoint:](https://developer.apple.com/documentation/appkit/nstextinputclient/characterindex(for:)) - fn character_index_for_point( - &mut self, - point: Point, - window: &mut Window, - cx: &mut App, - ) -> Option; - - /// Set the range of the user's currently selected text. - /// - /// This is the reverse data-flow direction from [`Self::selected_text_range`]: - /// platforms call it when the system text machinery moves the selection on the - /// application's behalf — e.g. the user drags a system selection handle or - /// invokes Select All from system UI (iOS `UITextInput setSelectedTextRange:`, - /// Android `InputConnection.setSelection`). - /// - /// range_utf16 is in terms of UTF-16 characters, from 0 to the length of the document - fn set_selected_text_range( - &mut self, - _range_utf16: Range, - _window: &mut Window, - _cx: &mut App, - ) { - } - - /// Get the bounds of the focused text element in window coordinates, if known. - /// - /// This is the pull counterpart to the [`PlatformWindow::update_ime_position`] - /// push: mobile platforms ask for the focused element's geometry when they - /// need it (e.g. to frame system text-interaction UI overlaid on the focused - /// element). - fn element_bounds(&mut self, _window: &mut Window, _cx: &mut App) -> Option> { - None - } - - /// Get the length of the document in UTF-16 characters, if known. - fn text_length_utf16(&mut self, _window: &mut Window, _cx: &mut App) -> Option { - None - } - - /// Allows a given input context to opt into getting raw key repeats instead of - /// sending these to the platform. - /// TODO: Ideally we should be able to set ApplePressAndHoldEnabled in NSUserDefaults - /// (which is how iTerm does it) but it doesn't seem to work for me. - #[allow(dead_code)] - fn apple_press_and_hold_enabled(&mut self) -> bool { - true - } - - /// Returns whether this handler is accepting text input to be inserted. - fn accepts_text_input(&mut self, _window: &mut Window, _cx: &mut App) -> bool { - true - } - - /// The contiguous range of text, in UTF-16 code units, that platform text - /// input may read and edit around the current selection. - /// - /// Platforms that mirror document text into an IME-editable buffer clamp - /// the mirrored window to this range, so multi-step IME edit gestures - /// (word deletion, autocorrect rewrites, suggestion picks) cannot reach - /// content outside it. The range should contain the current selection; - /// when it cannot (a selection spanning a region boundary), platforms - /// degrade the mirrored IME context rather than widening the range. - /// `None` places no bound. - fn text_input_editable_range( - &mut self, - _window: &mut Window, - _cx: &mut App, - ) -> Option> { - None - } - - /// Returns whether printable keys should be routed to the IME before keybinding - /// matching when a non-ASCII input source (e.g. Japanese, Korean, Chinese IME) - /// is active. This prevents multi-stroke keybindings like `jj` from intercepting - /// keys that the IME should compose. - /// - /// Defaults to `false`. The editor overrides this based on whether it expects - /// character input (e.g. Vim insert mode returns `true`, normal mode returns `false`). - /// The terminal keeps the default `false` so that raw keys reach the terminal process. - fn prefers_ime_for_printable_keys(&mut self, _window: &mut Window, _cx: &mut App) -> bool { - false - } - - /// Get this handler's preferences for platform text assistance. - /// - /// GPUI re-queries this every frame and forwards it to the platform window - /// only when it changes, so implementations must be cheap and may vary the - /// result with application state (e.g. with the cursor's position). - fn text_input_configuration( - &mut self, - _window: &mut Window, - _cx: &mut App, - ) -> TextInputConfiguration { - TextInputConfiguration::default() - } -} - -/// Platform text-assistance preferences for the focused text region. -/// -/// Returned by [`InputHandler::text_input_configuration`] and forwarded to the -/// platform whenever it changes; the platform maps the fields onto its native -/// input-session attributes (on web, DOM attributes of the hidden editable -/// element such as `autocorrect` and `enterkeyhint`). -/// -/// The default disables all text assistance and requests no particular action -/// key presentation. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct TextInputConfiguration { - /// Whether the platform may automatically correct entered text. - pub autocorrect: bool, - /// How software keyboards automatically capitalize entered text. - pub autocapitalize: Autocapitalize, - /// Whether software keyboards may offer word suggestions and spellcheck. - pub suggestions: bool, - /// The action advertised on a software keyboard's confirm ("enter") key. - pub input_action: TextInputAction, -} - -/// Automatic capitalization applied by software keyboards. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum Autocapitalize { - /// No automatic capitalization. - #[default] - None, - /// Capitalize the first letter of each word. - Words, - /// Capitalize the first letter of each sentence. - Sentences, - /// Capitalize every letter. - Characters, -} - -/// The action a software keyboard advertises on its confirm ("enter") key. -/// -/// This affects only how the key is presented (icon or label); pressing it is -/// still delivered as ordinary input. -/// -/// The variants are the HTML `enterkeyhint` attribute's value set -/// (), -/// which also maps onto Android's `IME_ACTION_*` constants and iOS's -/// `UIReturnKeyType`; [`TextInputAction::Unspecified`] means "emit no hint". -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum TextInputAction { - /// Let the platform choose its default presentation. - #[default] - Unspecified, - /// Inserting a line break. - Enter, - /// Committing the field's value. - Done, - /// Navigating to the typed target. - Go, - /// Moving to the next field. - Next, - /// Moving to the previous field. - Previous, - /// Executing a search. - Search, - /// Sending a message. - Send, -} - -/// The variables that can be configured when creating a new window -#[derive(Debug)] -pub struct WindowOptions { - /// Specifies the state and bounds of the window in screen coordinates. - /// - `None`: Inherit the bounds. - /// - `Some(WindowBounds)`: Open a window with corresponding state and its restore size. - pub window_bounds: Option, - - /// The titlebar configuration of the window - pub titlebar: Option, - - /// Whether the window should be focused when created - pub focus: bool, - - /// Whether the window should be shown when created - pub show: bool, - - /// The kind of window to create - pub kind: WindowKind, - - /// Whether the window can be moved by the user. When `false`, the user cannot drag - /// the window (on macOS this sets `NSWindow.isMovable`, which also disables the - /// Window-menu tiling items); programmatic moves are still allowed. - pub is_movable: bool, - - /// Whether the application owns dragging of the (custom) titlebar, rather than - /// AppKit. Only has an effect on macOS. - /// - /// Set this to `true` for windows that draw their own titlebar and move the window - /// themselves via [`Window::start_window_move`]. It marks the whole content view as - /// app-owned titlebar content, so AppKit neither drags the window from the titlebar - /// nor delays titlebar clicks while disambiguating double-clicks (a delay first - /// observed on macOS 27). It is independent of `is_movable`, so such windows stay - /// user-movable (via their own drag) and keep the Window-menu tiling items enabled. - /// - /// Leave this `false` for windows that rely on AppKit's native titlebar dragging. - pub app_owns_titlebar_drag: bool, - - /// The minimum interval between animation frames while the window is inactive. - /// - /// Set to `None` to disable inactive-window animation frame throttling. - pub inactive_frame_interval: Option, - - /// Whether the window should be resizable by the user - pub is_resizable: bool, - - /// Whether the window should be minimized by the user - pub is_minimizable: bool, - - /// The display to create the window on, if this is None, - /// the window will be created on the main display - pub display_id: Option, - - /// The appearance of the window background. - pub window_background: WindowBackgroundAppearance, - - /// Application identifier of the window. Can by used by desktop environments to group applications together. - pub app_id: Option, - - /// Window minimum size - pub window_min_size: Option>, - - /// Whether to use client or server-side decorations on X11 and Wayland. - /// The platform may ignore requests it cannot satisfy. - pub window_decorations: Option, - - /// Icon image (X11 only) - pub icon: Option>, - - /// Tab group name, allows opening the window as a native tab on macOS 10.12+. Windows with the same tabbing identifier will be grouped together. - pub tabbing_identifier: Option, -} - -/// The variables that can be configured when creating a new window -#[derive(Debug)] -#[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) -)] -#[allow(missing_docs)] -pub struct WindowParams { - pub bounds: Bounds, - - /// The titlebar configuration of the window - #[cfg_attr(feature = "wayland", allow(dead_code))] - pub titlebar: Option, - - /// The kind of window to create - #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))] - pub kind: WindowKind, - - /// Whether the window should be movable by the user - #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))] - pub is_movable: bool, - - /// Whether the application owns dragging of the (custom) titlebar (macOS only) - #[cfg_attr( - any(target_os = "linux", target_os = "freebsd", target_os = "windows"), - allow(dead_code) - )] - pub app_owns_titlebar_drag: bool, - - /// Whether the window should be resizable by the user - #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))] - pub is_resizable: bool, - - /// Whether the window should be minimized by the user - #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))] - pub is_minimizable: bool, - - #[cfg_attr( - any(target_os = "linux", target_os = "freebsd", target_os = "windows"), - allow(dead_code) - )] - pub focus: bool, - - #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))] - pub show: bool, - - /// An image to set as the window icon (x11 only) - #[cfg_attr(feature = "wayland", allow(dead_code))] - pub icon: Option>, - - #[cfg_attr(feature = "wayland", allow(dead_code))] - pub display_id: Option, - - #[cfg_attr(feature = "wayland", allow(dead_code))] - pub app_id: Option, - - pub window_min_size: Option>, - - #[cfg(target_os = "macos")] - pub tabbing_identifier: Option, -} - -/// Represents the status of how a window should be opened. -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum WindowBounds { - /// Indicates that the window should open in a windowed state with the given bounds. - Windowed(Bounds), - /// Indicates that the window should open in a maximized state. - /// The bounds provided here represent the restore size of the window. - Maximized(Bounds), - /// Indicates that the window should open in fullscreen mode. - /// The bounds provided here represent the restore size of the window. - Fullscreen(Bounds), -} - -impl Default for WindowBounds { - fn default() -> Self { - WindowBounds::Windowed(Bounds::default()) - } -} - -impl WindowBounds { - /// Retrieve the inner bounds - pub fn get_bounds(&self) -> Bounds { - match self { - WindowBounds::Windowed(bounds) => *bounds, - WindowBounds::Maximized(bounds) => *bounds, - WindowBounds::Fullscreen(bounds) => *bounds, - } - } - - /// Creates a new window bounds that centers the window on the screen. - pub fn centered(size: Size, cx: &App) -> Self { - WindowBounds::Windowed(Bounds::centered(None, size, cx)) - } -} - -impl Default for WindowOptions { - fn default() -> Self { - Self { - window_bounds: None, - titlebar: Some(TitlebarOptions { - title: Default::default(), - appears_transparent: Default::default(), - traffic_light_position: Default::default(), - }), - focus: true, - show: true, - kind: WindowKind::Normal, - is_movable: true, - app_owns_titlebar_drag: false, - inactive_frame_interval: Some(Duration::from_micros(33_333)), - is_resizable: true, - is_minimizable: true, - display_id: None, - window_background: WindowBackgroundAppearance::default(), - icon: None, - app_id: None, - window_min_size: None, - window_decorations: None, - tabbing_identifier: None, - } - } -} - -/// The options that can be configured for a window's titlebar -#[derive(Debug, Default)] -pub struct TitlebarOptions { - /// The initial title of the window - pub title: Option, - - /// Should the default system titlebar be hidden to allow for a custom-drawn titlebar? (macOS and Windows only) - /// Refer to [`WindowOptions::window_decorations`] on Linux - pub appears_transparent: bool, - - /// The position of the macOS traffic light buttons - pub traffic_light_position: Option>, -} - -/// The kind of window to create -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum WindowKind { - /// A normal application window - Normal, - - /// A window that appears above all other windows, usually used for alerts or popups - /// use sparingly! - PopUp, - - /// A parent-anchored, platform-native popup window for menus, comboboxes, context menus and - /// tooltips. Unlike [`WindowKind::PopUp`], it is positioned relative to a parent window. - /// - /// The popup's size comes from [`WindowOptions::window_bounds`], whose origin is ignored. - /// See [`popup::PopupOptions`] for the placement options. Platforms without a native - /// implementation reject it with [`popup::PopupNotSupportedError`]. - AnchoredPopup(popup::PopupOptions), - - /// A floating window that appears on top of its parent window - Floating, - - /// A Wayland LayerShell window, used to draw overlays or backgrounds for applications such as - /// docks, notifications or wallpapers. - #[cfg(all(target_os = "linux", feature = "wayland"))] - LayerShell(layer_shell::LayerShellOptions), - - /// A window that appears on top of its parent window and blocks interaction with it - /// until the modal window is closed - Dialog, -} - -/// The appearance of the window, as defined by the operating system. -/// -/// On macOS, this corresponds to named [`NSAppearance`](https://developer.apple.com/documentation/appkit/nsappearance) -/// values. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub enum WindowAppearance { - /// A light appearance. - /// - /// On macOS, this corresponds to the `aqua` appearance. - #[default] - Light, - - /// A light appearance with vibrant colors. - /// - /// On macOS, this corresponds to the `NSAppearanceNameVibrantLight` appearance. - VibrantLight, - - /// A dark appearance. - /// - /// On macOS, this corresponds to the `darkAqua` appearance. - Dark, - - /// A dark appearance with vibrant colors. - /// - /// On macOS, this corresponds to the `NSAppearanceNameVibrantDark` appearance. - VibrantDark, -} - -/// The appearance of the background of the window itself, when there is -/// no content or the content is transparent. -#[derive(Copy, Clone, Debug, Default, PartialEq)] -pub enum WindowBackgroundAppearance { - /// Opaque. - /// - /// This lets the window manager know that content behind this - /// window does not need to be drawn. - /// - /// Actual color depends on the system and themes should define a fully - /// opaque background color instead. - #[default] - Opaque, - /// Plain alpha transparency. - Transparent, - /// Transparency, but the contents behind the window are blurred. - /// - /// Not always supported. - Blurred, - /// The Mica backdrop material, supported on Windows 11. - MicaBackdrop, - /// The Mica Alt backdrop material, supported on Windows 11. - MicaAltBackdrop, -} - -/// The text rendering mode to use for drawing glyphs. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub enum TextRenderingMode { - /// Use the platform's default text rendering mode. - #[default] - PlatformDefault, - /// Use subpixel (ClearType-style) text rendering. - Subpixel, - /// Use grayscale text rendering. - Grayscale, -} - -/// The options that can be configured for a file dialog prompt -#[derive(Clone, Debug)] -pub struct PathPromptOptions { - /// Should the prompt allow files to be selected? - pub files: bool, - /// Should the prompt allow directories to be selected? - pub directories: bool, - /// Should the prompt allow multiple files to be selected? - pub multiple: bool, - /// The prompt to show to a user when selecting a path - pub prompt: Option, -} - -/// What kind of prompt styling to show -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum PromptLevel { - /// A prompt that is shown when the user should be notified of something - Info, - - /// A prompt that is shown when the user needs to be warned of a potential problem - Warning, - - /// A prompt that is shown when a critical problem has occurred - Critical, -} - -/// Prompt Button -#[derive(Clone, Debug, PartialEq)] -pub enum PromptButton { - /// Ok button - Ok(SharedString), - /// Cancel button - Cancel(SharedString), - /// Other button - Other(SharedString), -} - -impl PromptButton { - /// Create a button with label - pub fn new(label: impl Into) -> Self { - PromptButton::Other(label.into()) - } - - /// Create an Ok button - pub fn ok(label: impl Into) -> Self { - PromptButton::Ok(label.into()) - } - - /// Create a Cancel button - pub fn cancel(label: impl Into) -> Self { - PromptButton::Cancel(label.into()) - } - - /// Returns true if this button is a cancel button. - #[allow(dead_code)] - pub fn is_cancel(&self) -> bool { - matches!(self, PromptButton::Cancel(_)) - } - - /// Returns the label of the button - pub fn label(&self) -> &SharedString { - match self { - PromptButton::Ok(label) => label, - PromptButton::Cancel(label) => label, - PromptButton::Other(label) => label, - } - } -} - -impl From<&str> for PromptButton { - fn from(value: &str) -> Self { - match value.to_lowercase().as_str() { - "ok" => PromptButton::Ok("OK".into()), - "cancel" => PromptButton::Cancel("Cancel".into()), - _ => PromptButton::Other(SharedString::from(value.to_owned())), - } - } -} - -/// The style of the cursor (pointer) -#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] -pub enum CursorStyle { - /// The default cursor - #[default] - Arrow, - - /// A text input cursor - /// corresponds to the CSS cursor value `text` - IBeam, - - /// A crosshair cursor - /// corresponds to the CSS cursor value `crosshair` - Crosshair, - - /// A closed hand cursor - /// corresponds to the CSS cursor value `grabbing` - ClosedHand, - - /// An open hand cursor - /// corresponds to the CSS cursor value `grab` - OpenHand, - - /// A pointing hand cursor - /// corresponds to the CSS cursor value `pointer` - PointingHand, - - /// A resize left cursor - /// corresponds to the CSS cursor value `w-resize` - ResizeLeft, - - /// A resize right cursor - /// corresponds to the CSS cursor value `e-resize` - ResizeRight, - - /// A resize cursor to the left and right - /// corresponds to the CSS cursor value `ew-resize` - ResizeLeftRight, - - /// A resize up cursor - /// corresponds to the CSS cursor value `n-resize` - ResizeUp, - - /// A resize down cursor - /// corresponds to the CSS cursor value `s-resize` - ResizeDown, - - /// A resize cursor directing up and down - /// corresponds to the CSS cursor value `ns-resize` - ResizeUpDown, - - /// A resize cursor directing up-left and down-right - /// corresponds to the CSS cursor value `nesw-resize` - ResizeUpLeftDownRight, - - /// A resize cursor directing up-right and down-left - /// corresponds to the CSS cursor value `nwse-resize` - ResizeUpRightDownLeft, - - /// A cursor indicating that the item/column can be resized horizontally. - /// corresponds to the CSS cursor value `col-resize` - ResizeColumn, - - /// A cursor indicating that the item/row can be resized vertically. - /// corresponds to the CSS cursor value `row-resize` - ResizeRow, - - /// A text input cursor for vertical layout - /// corresponds to the CSS cursor value `vertical-text` - IBeamCursorForVerticalLayout, - - /// A cursor indicating that the operation is not allowed - /// corresponds to the CSS cursor value `not-allowed` - OperationNotAllowed, - - /// A cursor indicating that the operation will result in a link - /// corresponds to the CSS cursor value `alias` - DragLink, - - /// A cursor indicating that the operation will result in a copy - /// corresponds to the CSS cursor value `copy` - DragCopy, - - /// A cursor indicating that the operation will result in a context menu - /// corresponds to the CSS cursor value `context-menu` - ContextualMenu, -} - -/// A clipboard item that should be copied to the clipboard -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ClipboardItem { - /// The entries in this clipboard item. - pub entries: Vec, -} - -/// An error produced by [`Platform::read_from_clipboard_async`]. -/// -/// Callers surface these failures to users, so the variants distinguish -/// conditions that call for different user-facing guidance. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum ClipboardReadError { - /// The platform clipboard is not available in this context, e.g. the - /// browser does not expose the async clipboard API or the page is not a - /// secure context. - Unavailable, - /// The platform refused access, e.g. the user declined the browser's - /// clipboard permission prompt or paste confirmation. - Denied(String), - /// The clipboard contents could not be converted into a - /// [`ClipboardItem`]. - UnsupportedContent, -} - -impl std::fmt::Display for ClipboardReadError { - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Unavailable => formatter.write_str("the clipboard is unavailable"), - Self::Denied(message) => { - write!(formatter, "clipboard access was denied: {message}") - } - Self::UnsupportedContent => { - formatter.write_str("the clipboard contents are unsupported") - } - } - } -} - -impl std::error::Error for ClipboardReadError {} - -/// Either a ClipboardString or a ClipboardImage -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClipboardEntry { - /// A string entry - String(ClipboardString), - /// An image entry - Image(Image), - /// A file entry - ExternalPaths(crate::ExternalPaths), -} - -impl ClipboardItem { - /// Create a new ClipboardItem::String with no associated metadata - pub fn new_string(text: String) -> Self { - Self { - entries: vec![ClipboardEntry::String(ClipboardString::new(text))], - } - } - - /// Create a new ClipboardItem::String with the given text and associated metadata - pub fn new_string_with_metadata(text: String, metadata: String) -> Self { - Self { - entries: vec![ClipboardEntry::String(ClipboardString { - text, - metadata: Some(metadata), - })], - } - } - - /// Create a new ClipboardItem::String with the given text and associated metadata - pub fn new_string_with_json_metadata(text: String, metadata: T) -> Self { - Self { - entries: vec![ClipboardEntry::String( - ClipboardString::new(text).with_json_metadata(metadata), - )], - } - } - - /// Create a new ClipboardItem::Image with the given image with no associated metadata - pub fn new_image(image: &Image) -> Self { - Self { - entries: vec![ClipboardEntry::Image(image.clone())], - } - } - - /// Concatenates together all the ClipboardString entries in the item. - /// Returns None if there were no ClipboardString entries. - pub fn text(&self) -> Option { - let mut answer = String::new(); - - for entry in self.entries.iter() { - if let ClipboardEntry::String(ClipboardString { text, metadata: _ }) = entry { - answer.push_str(text); - } - } - - if answer.is_empty() { - for entry in self.entries.iter() { - if let ClipboardEntry::ExternalPaths(paths) = entry { - for path in &paths.0 { - use std::fmt::Write as _; - _ = write!(answer, "{}", path.display()); - } - } - } - } - - if !answer.is_empty() { - Some(answer) - } else { - None - } - } - - /// If this item is one ClipboardEntry::String, returns its metadata. - #[cfg_attr(not(target_os = "windows"), allow(dead_code))] - pub fn metadata(&self) -> Option<&String> { - match self.entries().first() { - Some(ClipboardEntry::String(clipboard_string)) if self.entries.len() == 1 => { - clipboard_string.metadata.as_ref() - } - _ => None, - } - } - - /// Get the item's entries - pub fn entries(&self) -> &[ClipboardEntry] { - &self.entries - } - - /// Get owned versions of the item's entries - pub fn into_entries(self) -> impl Iterator { - self.entries.into_iter() - } -} - -impl From for ClipboardEntry { - fn from(value: ClipboardString) -> Self { - Self::String(value) - } -} - -impl From for ClipboardEntry { - fn from(value: String) -> Self { - Self::from(ClipboardString::from(value)) - } -} - -impl From for ClipboardEntry { - fn from(value: Image) -> Self { - Self::Image(value) - } -} - -impl From for ClipboardItem { - fn from(value: ClipboardEntry) -> Self { - Self { - entries: vec![value], - } - } -} - -impl From for ClipboardItem { - fn from(value: String) -> Self { - Self::from(ClipboardEntry::from(value)) - } -} - -impl From for ClipboardItem { - fn from(value: Image) -> Self { - Self::from(ClipboardEntry::from(value)) - } -} - -/// One of the editor's supported image formats (e.g. PNG, JPEG) - used when dealing with images in the clipboard -#[derive(Clone, Copy, Debug, Eq, PartialEq, EnumIter, Hash)] -pub enum ImageFormat { - // Sorted from most to least likely to be pasted into an editor, - // which matters when we iterate through them trying to see if - // clipboard content matches them. - /// .png - Png, - /// .jpeg or .jpg - Jpeg, - /// .webp - Webp, - /// .gif - Gif, - /// .svg - Svg, - /// .bmp - Bmp, - /// .tif or .tiff - Tiff, - /// .ico - Ico, - /// Netpbm image formats (.pbm, .ppm, .pgm). - Pnm, -} - -impl ImageFormat { - /// Returns the mime type for the ImageFormat - pub const fn mime_type(self) -> &'static str { - match self { - ImageFormat::Png => "image/png", - ImageFormat::Jpeg => "image/jpeg", - ImageFormat::Webp => "image/webp", - ImageFormat::Gif => "image/gif", - ImageFormat::Svg => "image/svg+xml", - ImageFormat::Bmp => "image/bmp", - ImageFormat::Tiff => "image/tiff", - ImageFormat::Ico => "image/ico", - ImageFormat::Pnm => "image/x-portable-anymap", - } - } - - /// Returns the file extension for this image format (without leading dot). - pub const fn extension(self) -> &'static str { - match self { - ImageFormat::Png => "png", - ImageFormat::Jpeg => "jpg", - ImageFormat::Webp => "webp", - ImageFormat::Gif => "gif", - ImageFormat::Svg => "svg", - ImageFormat::Bmp => "bmp", - ImageFormat::Tiff => "tiff", - ImageFormat::Ico => "ico", - ImageFormat::Pnm => "pnm", - } - } - - /// Returns the ImageFormat for the given mime type, including known aliases. - pub fn from_mime_type(mime_type: &str) -> Option { - use strum::IntoEnumIterator; - Self::iter() - .find(|format| format.mime_type() == mime_type) - .or_else(|| Self::from_mime_type_alias(mime_type)) - } - - /// Non-canonical mime types that some producers use in the wild. - /// Unlike `mime_type()` which returns the single canonical form, - /// these are legacy or shortened variants we still need to recognize. - fn from_mime_type_alias(mime_type: &str) -> Option { - match mime_type { - "image/jpg" => Some(Self::Jpeg), - "image/tif" => Some(Self::Tiff), - _ => None, - } - } -} - -/// An image, with a format and certain bytes -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Image { - /// The image format the bytes represent (e.g. PNG) - pub format: ImageFormat, - /// The raw image bytes - pub bytes: Vec, - /// The unique ID for the image - pub id: u64, -} - -pub(crate) fn decode_static_image( - bytes: &[u8], - format: image::ImageFormat, -) -> Result> { - let decoder = image::ImageReader::with_format(Cursor::new(bytes), format) - .into_decoder() - .context("creating image decoder")?; - decode_static_image_from_decoder(decoder) -} - -pub(crate) fn decode_static_image_from_decoder( - mut decoder: impl image::ImageDecoder, -) -> Result> { - let orientation = decoder - .orientation() - .context("reading decoder's orientation")?; - let mut image = DynamicImage::from_decoder(decoder).context("decoding image")?; - image.apply_orientation(orientation); - - let mut data = image.into_rgba8(); - for pixel in data.chunks_exact_mut(4) { - pixel.swap(0, 2); - } - - Ok(SmallVec::from_elem(Frame::new(data), 1)) -} - -impl Hash for Image { - fn hash(&self, state: &mut H) { - state.write_u64(self.id); - } -} - -impl Image { - /// An empty image containing no data - pub fn empty() -> Self { - Self::from_bytes(ImageFormat::Png, Vec::new()) - } - - /// Create an image from a format and bytes - pub fn from_bytes(format: ImageFormat, bytes: Vec) -> Self { - Self { - id: hash(&bytes), - format, - bytes, - } - } - - /// Get this image's ID - pub fn id(&self) -> u64 { - self.id - } - - /// Use the GPUI `use_asset` API to make this image renderable - pub fn use_render_image( - self: Arc, - window: &mut Window, - cx: &mut App, - ) -> Option> { - ImageSource::Image(self) - .use_data(None, window, cx) - .and_then(|result| result.ok()) - } - - /// Use the GPUI `get_asset` API to make this image renderable - pub fn get_render_image( - self: Arc, - window: &mut Window, - cx: &mut App, - ) -> Option> { - ImageSource::Image(self) - .get_data(None, window, cx) - .and_then(|result| result.ok()) - } - - /// Use the GPUI `remove_asset` API to drop this image, if possible. - pub fn remove_asset(self: Arc, cx: &mut App) { - ImageSource::Image(self).remove_asset(cx); - } - - /// Check whether this image is present in GPUI's asset cache (loading or - /// loaded), without fetching it. - #[cfg(any(test, feature = "test-support"))] - pub fn is_asset_cached(self: &Arc, cx: &App) -> bool { - ImageSource::Image(self.clone()).is_asset_cached(cx) - } - - /// Convert the clipboard image to an `ImageData` object. - pub fn to_image_data(&self, svg_renderer: SvgRenderer) -> Result> { - let frames = match self.format { - ImageFormat::Gif => { - let decoder = GifDecoder::new(Cursor::new(&self.bytes))?; - let mut frames = SmallVec::new(); - - for frame in decoder.into_frames() { - match frame { - Ok(mut frame) => { - // Convert from RGBA to BGRA. - for pixel in frame.buffer_mut().chunks_exact_mut(4) { - pixel.swap(0, 2); - } - frames.push(frame); - } - Err(err) => { - log::debug!("Skipping GIF frame due to decode error: {err}"); - } - } - } - - if frames.is_empty() { - anyhow::bail!("GIF could not be decoded: all frames failed"); - } - - frames - } - ImageFormat::Png => decode_static_image(&self.bytes, image::ImageFormat::Png)?, - ImageFormat::Jpeg => decode_static_image(&self.bytes, image::ImageFormat::Jpeg)?, - ImageFormat::Webp => decode_static_image(&self.bytes, image::ImageFormat::WebP)?, - ImageFormat::Bmp => decode_static_image(&self.bytes, image::ImageFormat::Bmp)?, - ImageFormat::Tiff => decode_static_image(&self.bytes, image::ImageFormat::Tiff)?, - ImageFormat::Ico => decode_static_image(&self.bytes, image::ImageFormat::Ico)?, - ImageFormat::Svg => { - return svg_renderer - .render_single_frame(&self.bytes, 1.0) - .map_err(Into::into); - } - ImageFormat::Pnm => decode_static_image(&self.bytes, image::ImageFormat::Pnm)?, - }; - - Ok(Arc::new(RenderImage::new(frames))) - } - - /// Get the format of the clipboard image - pub fn format(&self) -> ImageFormat { - self.format - } - - /// Get the raw bytes of the clipboard image - pub fn bytes(&self) -> &[u8] { - self.bytes.as_slice() - } -} - -/// A clipboard item that should be copied to the clipboard -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ClipboardString { - /// The text content. - pub text: String, - /// Optional metadata associated with this clipboard string. - pub metadata: Option, -} - -impl ClipboardString { - /// Create a new clipboard string with the given text - pub fn new(text: String) -> Self { - Self { - text, - metadata: None, - } - } - - /// Return a new clipboard item with the metadata replaced by the given metadata, - /// after serializing it as JSON. - pub fn with_json_metadata(mut self, metadata: T) -> Self { - self.metadata = Some(serde_json::to_string(&metadata).unwrap()); - self - } - - /// Get the text of the clipboard string - pub fn text(&self) -> &String { - &self.text - } - - /// Get the owned text of the clipboard string - pub fn into_text(self) -> String { - self.text - } - - /// Get the metadata of the clipboard string, formatted as JSON - pub fn metadata_json(&self) -> Option - where - T: for<'a> Deserialize<'a>, - { - self.metadata - .as_ref() - .and_then(|m| serde_json::from_str(m).ok()) - } - - #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))] - /// Compute a hash of the given text for clipboard change detection. - pub fn text_hash(text: &str) -> u64 { - let mut hasher = SeaHasher::new(); - text.hash(&mut hasher); - hasher.finish() - } -} - -impl From for ClipboardString { - fn from(value: String) -> Self { - Self { - text: value, - metadata: None, - } - } -} - -#[cfg(test)] -mod image_tests { - use super::*; - use std::sync::Arc; - - #[test] - fn test_image_to_image_data_applies_exif_orientation() { - let image = Image::from_bytes( - ImageFormat::Jpeg, - include_bytes!("../examples/image/exif-orientation-rotate-180.jpg").to_vec(), - ); - - let render_image = image.to_image_data(SvgRenderer::new(Arc::new(()))).unwrap(); - - assert_eq!(render_image.size(0), size(16.into(), 32.into())); - - let bytes = render_image.as_bytes(0).unwrap(); - assert_eq!(&bytes[..4], &[255, 255, 255, 255]); - assert_eq!(&bytes[(16 * 32 - 1) * 4..], &[0, 0, 0, 255]); - } - - #[test] - fn test_svg_image_to_image_data_converts_to_bgra() { - let image = Image::from_bytes( - ImageFormat::Svg, - br##" - -"## - .to_vec(), - ); - - let render_image = image.to_image_data(SvgRenderer::new(Arc::new(()))).unwrap(); - let bytes = render_image.as_bytes(0).unwrap(); - - for pixel in bytes.chunks_exact(4) { - assert_eq!(pixel, &[0xF8, 0xBD, 0x38, 0xFF]); - } - } -} - -#[cfg(all(test, any(target_os = "linux", target_os = "freebsd")))] -mod tests { - use super::*; - use std::collections::HashSet; - - #[test] - fn test_window_button_layout_parse_standard() { - let layout = WindowButtonLayout::parse("close,minimize:maximize").unwrap(); - assert_eq!( - layout.left, - [ - Some(WindowButton::Close), - Some(WindowButton::Minimize), - None - ] - ); - assert_eq!(layout.right, [Some(WindowButton::Maximize), None, None]); - } - - #[test] - fn test_window_button_layout_parse_right_only() { - let layout = WindowButtonLayout::parse("minimize,maximize,close").unwrap(); - assert_eq!(layout.left, [None, None, None]); - assert_eq!( - layout.right, - [ - Some(WindowButton::Minimize), - Some(WindowButton::Maximize), - Some(WindowButton::Close) - ] - ); - } - - #[test] - fn test_window_button_layout_parse_left_only() { - let layout = WindowButtonLayout::parse("close,minimize,maximize:").unwrap(); - assert_eq!( - layout.left, - [ - Some(WindowButton::Close), - Some(WindowButton::Minimize), - Some(WindowButton::Maximize) - ] - ); - assert_eq!(layout.right, [None, None, None]); - } - - #[test] - fn test_window_button_layout_parse_with_whitespace() { - let layout = WindowButtonLayout::parse(" close , minimize : maximize ").unwrap(); - assert_eq!( - layout.left, - [ - Some(WindowButton::Close), - Some(WindowButton::Minimize), - None - ] - ); - assert_eq!(layout.right, [Some(WindowButton::Maximize), None, None]); - } - - #[test] - fn test_window_button_layout_parse_empty() { - let layout = WindowButtonLayout::parse("").unwrap(); - assert_eq!(layout.left, [None, None, None]); - assert_eq!(layout.right, [None, None, None]); - } - - #[test] - fn test_window_button_layout_parse_intentionally_empty() { - let layout = WindowButtonLayout::parse(":").unwrap(); - assert_eq!(layout.left, [None, None, None]); - assert_eq!(layout.right, [None, None, None]); - } - - #[test] - fn test_window_button_layout_parse_invalid_buttons() { - let layout = WindowButtonLayout::parse("close,invalid,minimize:maximize,foo").unwrap(); - assert_eq!( - layout.left, - [ - Some(WindowButton::Close), - Some(WindowButton::Minimize), - None - ] - ); - assert_eq!(layout.right, [Some(WindowButton::Maximize), None, None]); - } - - #[test] - fn test_window_button_layout_parse_deduplicates_same_side_buttons() { - let layout = WindowButtonLayout::parse("close,close,minimize").unwrap(); - assert_eq!( - layout.right, - [ - Some(WindowButton::Close), - Some(WindowButton::Minimize), - None - ] - ); - assert_eq!(layout.format(), ":close,minimize"); - } - - #[test] - fn test_window_button_layout_parse_deduplicates_buttons_across_sides() { - let layout = WindowButtonLayout::parse("close:maximize,close,minimize").unwrap(); - assert_eq!(layout.left, [Some(WindowButton::Close), None, None]); - assert_eq!( - layout.right, - [ - Some(WindowButton::Maximize), - Some(WindowButton::Minimize), - None - ] - ); - - let button_ids: Vec<_> = layout - .left - .iter() - .chain(layout.right.iter()) - .flatten() - .map(WindowButton::id) - .collect(); - let unique_button_ids = button_ids.iter().copied().collect::>(); - assert_eq!(unique_button_ids.len(), button_ids.len()); - assert_eq!(layout.format(), "close:maximize,minimize"); - } - - #[test] - fn test_window_button_layout_parse_gnome_style() { - let layout = WindowButtonLayout::parse("close").unwrap(); - assert_eq!(layout.left, [None, None, None]); - assert_eq!(layout.right, [Some(WindowButton::Close), None, None]); - } - - #[test] - fn test_window_button_layout_parse_elementary_style() { - let layout = WindowButtonLayout::parse("close:maximize").unwrap(); - assert_eq!(layout.left, [Some(WindowButton::Close), None, None]); - assert_eq!(layout.right, [Some(WindowButton::Maximize), None, None]); - } - - #[test] - fn test_window_button_layout_round_trip() { - let cases = [ - "close:minimize,maximize", - "minimize,maximize,close:", - ":close", - "close:", - "close:maximize", - ":", - ]; - - for case in cases { - let layout = WindowButtonLayout::parse(case).unwrap(); - assert_eq!(layout.format(), case, "Round-trip failed for: {}", case); - } - } - - #[test] - fn test_window_button_layout_linux_default() { - let layout = WindowButtonLayout::linux_default(); - assert_eq!(layout.left, [None, None, None]); - assert_eq!( - layout.right, - [ - Some(WindowButton::Minimize), - Some(WindowButton::Maximize), - Some(WindowButton::Close) - ] - ); - - let round_tripped = WindowButtonLayout::parse(&layout.format()).unwrap(); - assert_eq!(round_tripped, layout); - } - - #[test] - fn test_window_button_layout_parse_all_invalid() { - assert!(WindowButtonLayout::parse("asdfghjkl").is_err()); - } -} diff --git a/crates/gpui_pre/src/platform/app_menu.rs b/crates/gpui_pre/src/platform/app_menu.rs deleted file mode 100644 index 27c20c00..00000000 --- a/crates/gpui_pre/src/platform/app_menu.rs +++ /dev/null @@ -1,426 +0,0 @@ -use crate::{Action, App, Platform, SharedString}; - -/// A menu of the application, either a main menu or a submenu -pub struct Menu { - /// The name of the menu - pub name: SharedString, - - /// The items in the menu - pub items: Vec, - - /// Whether this menu is disabled - pub disabled: bool, -} - -impl Menu { - /// Create a new Menu with the given name - pub fn new(name: impl Into) -> Self { - Self { - name: name.into(), - items: vec![], - disabled: false, - } - } - - /// Set items to be in this menu - pub fn items(mut self, items: impl IntoIterator) -> Self { - self.items = items.into_iter().collect(); - self - } - - /// Set whether this menu is disabled - pub fn disabled(mut self, disabled: bool) -> Self { - self.disabled = disabled; - self - } - - /// Create an OwnedMenu from this Menu - pub fn owned(self) -> OwnedMenu { - OwnedMenu { - name: self.name.to_string().into(), - items: self.items.into_iter().map(|item| item.owned()).collect(), - disabled: self.disabled, - } - } -} - -/// OS menus are menus that are recognized by the operating system -/// This allows the operating system to provide specialized items for -/// these menus -pub struct OsMenu { - /// The name of the menu - pub name: SharedString, - - /// The type of menu - pub menu_type: SystemMenuType, -} - -impl OsMenu { - /// Create an OwnedOsMenu from this OsMenu - pub fn owned(self) -> OwnedOsMenu { - OwnedOsMenu { - name: self.name.to_string().into(), - menu_type: self.menu_type, - } - } -} - -/// The type of system menu -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum SystemMenuType { - /// The 'Services' menu in the Application menu on macOS - Services, -} - -/// The different kinds of items that can be in a menu -pub enum MenuItem { - /// A separator between items - Separator, - - /// A submenu - Submenu(Menu), - - /// A menu, managed by the system (for example, the Services menu on macOS) - SystemMenu(OsMenu), - - /// An action that can be performed - Action { - /// The name of this menu item - name: SharedString, - - /// The action to perform when this menu item is selected - action: Box, - - /// The OS Action that corresponds to this action, if any - /// See [`OsAction`] for more information - os_action: Option, - - /// Whether this action is checked - checked: bool, - - /// Whether this action is disabled - disabled: bool, - }, -} - -impl MenuItem { - /// Creates a new menu item that is a separator - pub fn separator() -> Self { - Self::Separator - } - - /// Creates a new menu item that is a submenu - pub fn submenu(menu: Menu) -> Self { - Self::Submenu(menu) - } - - /// Creates a new submenu that is populated by the OS - pub fn os_submenu(name: impl Into, menu_type: SystemMenuType) -> Self { - Self::SystemMenu(OsMenu { - name: name.into(), - menu_type, - }) - } - - /// Creates a new menu item that invokes an action - pub fn action(name: impl Into, action: impl Action) -> Self { - Self::Action { - name: name.into(), - action: Box::new(action), - os_action: None, - checked: false, - disabled: false, - } - } - - /// Creates a new menu item that invokes an action and has an OS action - pub fn os_action( - name: impl Into, - action: impl Action, - os_action: OsAction, - ) -> Self { - Self::Action { - name: name.into(), - action: Box::new(action), - os_action: Some(os_action), - checked: false, - disabled: false, - } - } - - /// Create an OwnedMenuItem from this MenuItem - pub fn owned(self) -> OwnedMenuItem { - match self { - MenuItem::Separator => OwnedMenuItem::Separator, - MenuItem::Submenu(submenu) => OwnedMenuItem::Submenu(submenu.owned()), - MenuItem::Action { - name, - action, - os_action, - checked, - disabled, - } => OwnedMenuItem::Action { - name: name.into(), - action, - os_action, - checked, - disabled, - }, - MenuItem::SystemMenu(os_menu) => OwnedMenuItem::SystemMenu(os_menu.owned()), - } - } - - /// Set whether this menu item is checked - /// - /// Only for [`MenuItem::Action`], otherwise, will be ignored - pub fn checked(mut self, checked: bool) -> Self { - match &mut self { - MenuItem::Action { checked: old, .. } => { - *old = checked; - } - _ => {} - } - self - } - - /// Returns whether this menu item is checked - /// - /// Only for [`MenuItem::Action`], otherwise, returns false - #[inline] - pub fn is_checked(&self) -> bool { - match self { - MenuItem::Action { checked, .. } => *checked, - _ => false, - } - } - - /// Set whether this menu item is disabled - pub fn disabled(mut self, disabled: bool) -> Self { - match &mut self { - MenuItem::Action { disabled: old, .. } => { - *old = disabled; - } - MenuItem::Submenu(submenu) => { - submenu.disabled = disabled; - } - _ => {} - } - self - } - - /// Returns whether this menu item is disabled - /// - /// Only for [`MenuItem::Action`] and [`MenuItem::Submenu`], otherwise, returns false - #[inline] - pub fn is_disabled(&self) -> bool { - match self { - MenuItem::Action { disabled, .. } => *disabled, - MenuItem::Submenu(submenu) => submenu.disabled, - _ => false, - } - } -} - -/// OS menus are menus that are recognized by the operating system -/// This allows the operating system to provide specialized items for -/// these menus -#[derive(Clone)] -pub struct OwnedOsMenu { - /// The name of the menu - pub name: SharedString, - - /// The type of menu - pub menu_type: SystemMenuType, -} - -/// A menu of the application, either a main menu or a submenu -#[derive(Clone)] -pub struct OwnedMenu { - /// The name of the menu - pub name: SharedString, - - /// The items in the menu - pub items: Vec, - - /// Whether this menu is disabled - pub disabled: bool, -} - -/// The different kinds of items that can be in a menu -pub enum OwnedMenuItem { - /// A separator between items - Separator, - - /// A submenu - Submenu(OwnedMenu), - - /// A menu, managed by the system (for example, the Services menu on macOS) - SystemMenu(OwnedOsMenu), - - /// An action that can be performed - Action { - /// The name of this menu item - name: String, - - /// The action to perform when this menu item is selected - action: Box, - - /// The OS Action that corresponds to this action, if any - /// See [`OsAction`] for more information - os_action: Option, - - /// Whether this action is checked - checked: bool, - - /// Whether this action is disabled - disabled: bool, - }, -} - -impl Clone for OwnedMenuItem { - fn clone(&self) -> Self { - match self { - OwnedMenuItem::Separator => OwnedMenuItem::Separator, - OwnedMenuItem::Submenu(submenu) => OwnedMenuItem::Submenu(submenu.clone()), - OwnedMenuItem::Action { - name, - action, - os_action, - checked, - disabled, - } => OwnedMenuItem::Action { - name: name.clone(), - action: action.boxed_clone(), - os_action: *os_action, - checked: *checked, - disabled: *disabled, - }, - OwnedMenuItem::SystemMenu(os_menu) => OwnedMenuItem::SystemMenu(os_menu.clone()), - } - } -} - -// TODO: As part of the global selections refactor, these should -// be moved to GPUI-provided actions that make this association -// without leaking the platform details to GPUI users - -/// OS actions are actions that are recognized by the operating system -/// This allows the operating system to provide specialized behavior for -/// these actions -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum OsAction { - /// The 'cut' action - Cut, - - /// The 'copy' action - Copy, - - /// The 'paste' action - Paste, - - /// The 'select all' action - SelectAll, - - /// The 'undo' action - Undo, - - /// The 'redo' action - Redo, -} - -pub(crate) fn init_app_menus(platform: &dyn Platform, cx: &App) { - platform.on_will_open_app_menu(Box::new({ - let cx = cx.to_async(); - move || { - if let Some(app) = cx.app.upgrade() { - app.borrow_mut().update(|cx| cx.clear_pending_keystrokes()); - } - } - })); - - platform.on_validate_app_menu_command(Box::new({ - let cx = cx.to_async(); - move |action| { - cx.app - .upgrade() - .map(|app| app.borrow_mut().update(|cx| cx.is_action_available(action))) - .unwrap_or(false) - } - })); - - platform.on_app_menu_action(Box::new({ - let cx = cx.to_async(); - move |action| { - if let Some(app) = cx.app.upgrade() { - app.borrow_mut().update(|cx| cx.dispatch_action(action)); - } - } - })); -} - -#[cfg(test)] -mod tests { - use crate::Menu; - - #[test] - fn test_menu() { - let menu = Menu::new("App") - .items(vec![ - crate::MenuItem::action("Action 1", gpui::NoAction), - crate::MenuItem::separator(), - ]) - .disabled(true); - - assert_eq!(menu.name.as_ref(), "App"); - assert_eq!(menu.items.len(), 2); - assert!(menu.disabled); - } - - #[test] - fn test_menu_item_builder() { - use super::MenuItem; - - let item = MenuItem::action("Test Action", gpui::NoAction); - assert_eq!( - match &item { - MenuItem::Action { name, .. } => name.as_ref(), - _ => unreachable!(), - }, - "Test Action" - ); - assert!(matches!( - item, - MenuItem::Action { - checked: false, - disabled: false, - .. - } - )); - - assert!( - MenuItem::action("Test Action", gpui::NoAction) - .checked(true) - .is_checked() - ); - assert!( - MenuItem::action("Test Action", gpui::NoAction) - .disabled(true) - .is_disabled() - ); - - let submenu = MenuItem::submenu(super::Menu { - name: "Submenu".into(), - items: vec![], - disabled: true, - }); - assert_eq!( - match &submenu { - MenuItem::Submenu(menu) => menu.name.as_ref(), - _ => unreachable!(), - }, - "Submenu" - ); - assert!(!submenu.is_checked()); - assert!(submenu.is_disabled()); - } -} diff --git a/crates/gpui_pre/src/platform/keyboard.rs b/crates/gpui_pre/src/platform/keyboard.rs deleted file mode 100644 index 10b86202..00000000 --- a/crates/gpui_pre/src/platform/keyboard.rs +++ /dev/null @@ -1,41 +0,0 @@ -use collections::HashMap; - -use crate::{KeybindingKeystroke, Keystroke}; - -/// A trait for platform-specific keyboard layouts -pub trait PlatformKeyboardLayout { - /// Get the keyboard layout ID, which should be unique to the layout - fn id(&self) -> &str; - /// Get the keyboard layout display name - fn name(&self) -> &str; -} - -/// A trait for platform-specific keyboard mappings -pub trait PlatformKeyboardMapper { - /// Map a key equivalent to its platform-specific representation - fn map_key_equivalent( - &self, - keystroke: Keystroke, - use_key_equivalents: bool, - ) -> KeybindingKeystroke; - /// Get the key equivalents for the current keyboard layout, - /// only used on macOS - fn get_key_equivalents(&self) -> Option<&HashMap>; -} - -/// A dummy implementation of the platform keyboard mapper -pub struct DummyKeyboardMapper; - -impl PlatformKeyboardMapper for DummyKeyboardMapper { - fn map_key_equivalent( - &self, - keystroke: Keystroke, - _use_key_equivalents: bool, - ) -> KeybindingKeystroke { - KeybindingKeystroke::from_keystroke(keystroke) - } - - fn get_key_equivalents(&self) -> Option<&HashMap> { - None - } -} diff --git a/crates/gpui_pre/src/platform/keystroke.rs b/crates/gpui_pre/src/platform/keystroke.rs deleted file mode 100644 index c45c7c1b..00000000 --- a/crates/gpui_pre/src/platform/keystroke.rs +++ /dev/null @@ -1,776 +0,0 @@ -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use std::{ - error::Error, - fmt::{Display, Write}, -}; - -use crate::PlatformKeyboardMapper; - -/// This is a helper trait so that we can simplify the implementation of some functions -pub trait AsKeystroke { - /// Returns the GPUI representation of the keystroke. - fn as_keystroke(&self) -> &Keystroke; -} - -/// A keystroke and associated metadata generated by the platform -#[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)] -pub struct Keystroke { - /// the state of the modifier keys at the time the keystroke was generated - pub modifiers: Modifiers, - - /// key is the character printed on the key that was pressed - /// e.g. for option-s, key is "s" - /// On layouts that do not have ascii keys (e.g. Thai) - /// this will be the ASCII-equivalent character (q instead of ๆ), - /// and the typed character will be present in key_char. - pub key: String, - - /// key_char is the character that could have been typed when - /// this binding was pressed. - /// e.g. for s this is "s", for option-s "ß", and cmd-s None - pub key_char: Option, -} - -/// Represents a keystroke that can be used in keybindings and displayed to the user. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct KeybindingKeystroke { - /// The GPUI representation of the keystroke. - inner: Keystroke, - /// The modifiers to display. - #[cfg(target_os = "windows")] - display_modifiers: Modifiers, - /// The key to display. - #[cfg(target_os = "windows")] - display_key: String, -} - -/// Error type for `Keystroke::parse`. This is used instead of `anyhow::Error` so that Zed can use -/// markdown to display it. -#[derive(Debug)] -pub struct InvalidKeystrokeError { - /// The invalid keystroke. - pub keystroke: String, -} - -impl Error for InvalidKeystrokeError {} - -impl Display for InvalidKeystrokeError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Invalid keystroke \"{}\". {}", - self.keystroke, KEYSTROKE_PARSE_EXPECTED_MESSAGE - ) - } -} - -/// Sentence explaining what keystroke parser expects, starting with "Expected ..." -pub const KEYSTROKE_PARSE_EXPECTED_MESSAGE: &str = "Expected a sequence of modifiers \ - (`ctrl`, `alt`, `shift`, `fn`, `cmd`, `super`, or `win`) \ - followed by a key, separated by `-`."; - -impl Keystroke { - /// When matching a key we cannot know whether the user intended to type - /// the key_char or the key itself. 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 brazilian keyboard). - /// - /// This method assumes that `self` was typed and `target' is in the keymap, and checks - /// both possibilities for self against the target. - pub fn should_match(&self, target: &KeybindingKeystroke) -> bool { - #[cfg(not(target_os = "windows"))] - if let Some(key_char) = self - .key_char - .as_ref() - .filter(|key_char| key_char != &&self.key) - { - let ime_modifiers = Modifiers { - control: self.modifiers.control, - platform: self.modifiers.platform, - ..Default::default() - }; - - if &target.inner.key == key_char && target.inner.modifiers == ime_modifiers { - return true; - } - } - - #[cfg(target_os = "windows")] - if let Some(key_char) = self - .key_char - .as_ref() - .filter(|key_char| key_char != &&self.key) - { - // On Windows, if key_char is set, then the typed keystroke produced the key_char - if &target.inner.key == key_char && target.inner.modifiers == Modifiers::none() { - return true; - } - } - - target.inner.modifiers == self.modifiers && target.inner.key == self.key - } - - /// key syntax is: - /// [secondary-][ctrl-][alt-][shift-][cmd-][fn-]key[->key_char] - /// key_char syntax is only used for generating test events, - /// secondary means "cmd" on macOS and "ctrl" on other platforms - /// when matching a key with an key_char set will be matched without it. - pub fn parse(source: &str) -> std::result::Result { - let mut modifiers = Modifiers::none(); - let mut key = None; - let mut key_char = None; - - let mut components = source.split('-').peekable(); - while let Some(component) = components.next() { - if component.eq_ignore_ascii_case("ctrl") { - modifiers.control = true; - continue; - } - if component.eq_ignore_ascii_case("alt") { - modifiers.alt = true; - continue; - } - if component.eq_ignore_ascii_case("shift") { - modifiers.shift = true; - continue; - } - if component.eq_ignore_ascii_case("fn") { - modifiers.function = true; - continue; - } - if component.eq_ignore_ascii_case("secondary") { - if cfg!(target_os = "macos") { - modifiers.platform = true; - } else { - modifiers.control = true; - }; - continue; - } - - let is_platform = component.eq_ignore_ascii_case("cmd") - || component.eq_ignore_ascii_case("super") - || component.eq_ignore_ascii_case("win"); - - if is_platform { - modifiers.platform = true; - continue; - } - - let mut key_str = component.to_string(); - - 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(key_str); - key_char = Some(String::from(&next[1..])); - components.next(); - } else { - return Err(InvalidKeystrokeError { - keystroke: source.to_owned(), - }); - } - continue; - } - - if component.len() == 1 && component.as_bytes()[0].is_ascii_uppercase() { - // Convert to shift + lowercase char - modifiers.shift = true; - key_str.make_ascii_lowercase(); - } else { - // convert ascii chars to lowercase so that named keys like "tab" and "enter" - // are accepted case insensitively and stored how we expect so they are matched properly - key_str.make_ascii_lowercase() - } - key = Some(key_str); - } - - // Allow for the user to specify a keystroke modifier as the key itself - // This sets the `key` to the modifier, and disables the modifier - key = key.or_else(|| { - use std::mem; - // std::mem::take clears bool incase its true - if mem::take(&mut modifiers.shift) { - Some("shift".to_string()) - } else if mem::take(&mut modifiers.control) { - Some("control".to_string()) - } else if mem::take(&mut modifiers.alt) { - Some("alt".to_string()) - } else if mem::take(&mut modifiers.platform) { - Some("platform".to_string()) - } else if mem::take(&mut modifiers.function) { - Some("function".to_string()) - } else { - None - } - }); - - let key = key.ok_or_else(|| InvalidKeystrokeError { - keystroke: source.to_owned(), - })?; - - Ok(Keystroke { - modifiers, - key, - key_char, - }) - } - - /// Produces a representation of this key that Parse can understand. - pub fn unparse(&self) -> String { - unparse(&self.modifiers, &self.key) - } - - /// Returns true if this keystroke left - /// the ime system in an incomplete state. - pub fn is_ime_in_progress(&self) -> bool { - self.key_char.is_none() - && (is_printable_key(&self.key) || self.key.is_empty()) - && !(self.modifiers.platform - || self.modifiers.control - || self.modifiers.function - || self.modifiers.alt) - } - - /// Returns a new keystroke with the key_char filled. - /// This is used for dispatch_keystroke where we want users to - /// be able to simulate typing "space", etc. - pub fn with_simulated_ime(mut self) -> Self { - if self.key_char.is_none() - && !self.modifiers.platform - && !self.modifiers.control - && !self.modifiers.function - && !self.modifiers.alt - { - self.key_char = match self.key.as_str() { - "space" => Some(" ".into()), - "tab" => Some("\t".into()), - "enter" => Some("\n".into()), - key if !is_printable_key(key) || key.is_empty() => None, - key => { - if self.modifiers.shift { - Some(key.to_uppercase()) - } else { - Some(key.into()) - } - } - } - } - self - } -} - -impl KeybindingKeystroke { - #[cfg(target_os = "windows")] - #[expect(missing_docs)] - pub fn new(inner: Keystroke, display_modifiers: Modifiers, display_key: String) -> Self { - KeybindingKeystroke { - inner, - display_modifiers, - display_key, - } - } - - /// Create a new keybinding keystroke from the given keystroke using the given keyboard mapper. - pub fn new_with_mapper( - inner: Keystroke, - use_key_equivalents: bool, - keyboard_mapper: &dyn PlatformKeyboardMapper, - ) -> Self { - keyboard_mapper.map_key_equivalent(inner, use_key_equivalents) - } - - /// Create a new keybinding keystroke from the given keystroke, without any platform-specific mapping. - pub fn from_keystroke(keystroke: Keystroke) -> Self { - #[cfg(target_os = "windows")] - { - let key = keystroke.key.clone(); - let modifiers = keystroke.modifiers; - KeybindingKeystroke { - inner: keystroke, - display_modifiers: modifiers, - display_key: key, - } - } - #[cfg(not(target_os = "windows"))] - { - KeybindingKeystroke { inner: keystroke } - } - } - - /// Returns the GPUI representation of the keystroke. - pub fn inner(&self) -> &Keystroke { - &self.inner - } - - /// Returns the modifiers. - /// - /// Platform-specific behavior: - /// - On macOS and Linux, this modifiers is the same as `inner.modifiers`, which is the GPUI representation of the keystroke. - /// - On Windows, this modifiers is the display modifiers, for example, a `ctrl-@` keystroke will have `inner.modifiers` as - /// `Modifiers::control()` and `display_modifiers` as `Modifiers::control_shift()`. - pub fn modifiers(&self) -> &Modifiers { - #[cfg(target_os = "windows")] - { - &self.display_modifiers - } - #[cfg(not(target_os = "windows"))] - { - &self.inner.modifiers - } - } - - /// Returns the key. - /// - /// Platform-specific behavior: - /// - On macOS and Linux, this key is the same as `inner.key`, which is the GPUI representation of the keystroke. - /// - On Windows, this key is the display key, for example, a `ctrl-@` keystroke will have `inner.key` as `@` and `display_key` as `2`. - pub fn key(&self) -> &str { - #[cfg(target_os = "windows")] - { - &self.display_key - } - #[cfg(not(target_os = "windows"))] - { - &self.inner.key - } - } - - /// Sets the modifiers. On Windows this modifies both `inner.modifiers` and `display_modifiers`. - pub fn set_modifiers(&mut self, modifiers: Modifiers) { - self.inner.modifiers = modifiers; - #[cfg(target_os = "windows")] - { - self.display_modifiers = modifiers; - } - } - - /// Sets the key. On Windows this modifies both `inner.key` and `display_key`. - pub fn set_key(&mut self, key: String) { - #[cfg(target_os = "windows")] - { - self.display_key = key.clone(); - } - self.inner.key = key; - } - - /// Produces a representation of this key that Parse can understand. - pub fn unparse(&self) -> String { - #[cfg(target_os = "windows")] - { - unparse(&self.display_modifiers, &self.display_key) - } - #[cfg(not(target_os = "windows"))] - { - unparse(&self.inner.modifiers, &self.inner.key) - } - } - - /// Removes the key_char - pub fn remove_key_char(&mut self) { - self.inner.key_char = None; - } -} - -fn is_printable_key(key: &str) -> bool { - !matches!( - key, - "f1" | "f2" - | "f3" - | "f4" - | "f5" - | "f6" - | "f7" - | "f8" - | "f9" - | "f10" - | "f11" - | "f12" - | "f13" - | "f14" - | "f15" - | "f16" - | "f17" - | "f18" - | "f19" - | "f20" - | "f21" - | "f22" - | "f23" - | "f24" - | "f25" - | "f26" - | "f27" - | "f28" - | "f29" - | "f30" - | "f31" - | "f32" - | "f33" - | "f34" - | "f35" - | "backspace" - | "delete" - | "left" - | "right" - | "up" - | "down" - | "pageup" - | "pagedown" - | "insert" - | "home" - | "end" - | "back" - | "forward" - | "escape" - ) -} - -impl std::fmt::Display for Keystroke { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - display_modifiers(&self.modifiers, f)?; - display_key(&self.key, f) - } -} - -impl std::fmt::Display for KeybindingKeystroke { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - display_modifiers(self.modifiers(), f)?; - display_key(self.key(), f) - } -} - -/// The state of the modifier keys at some point in time -#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize, Hash, JsonSchema)] -pub struct Modifiers { - /// The control key - #[serde(default)] - pub control: bool, - - /// The alt key - /// Sometimes also known as the 'meta' key - #[serde(default)] - pub alt: bool, - - /// The shift key - #[serde(default)] - pub shift: bool, - - /// The command key, on macos - /// the windows key, on windows - /// the super key, on linux - #[serde(default)] - pub platform: bool, - - /// The function key - #[serde(default)] - pub function: bool, -} - -impl Modifiers { - /// Returns whether any modifier key is pressed. - pub fn modified(&self) -> bool { - self.control || self.alt || self.shift || self.platform || self.function - } - - /// Whether the semantically 'secondary' modifier key is pressed. - /// - /// On macOS, this is the command key. - /// On Linux and Windows, this is the control key. - pub fn secondary(&self) -> bool { - #[cfg(target_os = "macos")] - { - self.platform - } - - #[cfg(not(target_os = "macos"))] - { - self.control - } - } - - /// Returns how many modifier keys are pressed. - pub fn number_of_modifiers(&self) -> u8 { - self.control as u8 - + self.alt as u8 - + self.shift as u8 - + self.platform as u8 - + self.function as u8 - } - - /// Returns [`Modifiers`] with no modifiers. - pub fn none() -> Modifiers { - Default::default() - } - - /// Returns [`Modifiers`] with just the command key. - pub fn command() -> Modifiers { - Modifiers { - platform: true, - ..Default::default() - } - } - - /// A Returns [`Modifiers`] with just the secondary key pressed. - pub fn secondary_key() -> Modifiers { - #[cfg(target_os = "macos")] - { - Modifiers { - platform: true, - ..Default::default() - } - } - - #[cfg(not(target_os = "macos"))] - { - Modifiers { - control: true, - ..Default::default() - } - } - } - - /// Returns [`Modifiers`] with just the windows key. - pub fn windows() -> Modifiers { - Modifiers { - platform: true, - ..Default::default() - } - } - - /// Returns [`Modifiers`] with just the super key. - pub fn super_key() -> Modifiers { - Modifiers { - platform: true, - ..Default::default() - } - } - - /// Returns [`Modifiers`] with just control. - pub fn control() -> Modifiers { - Modifiers { - control: true, - ..Default::default() - } - } - - /// Returns [`Modifiers`] with just alt. - pub fn alt() -> Modifiers { - Modifiers { - alt: true, - ..Default::default() - } - } - - /// Returns [`Modifiers`] with just shift. - pub fn shift() -> Modifiers { - Modifiers { - shift: true, - ..Default::default() - } - } - - /// Returns [`Modifiers`] with just function. - pub fn function() -> Modifiers { - Modifiers { - function: true, - ..Default::default() - } - } - - /// Returns [`Modifiers`] with command + shift. - pub fn command_shift() -> Modifiers { - Modifiers { - shift: true, - platform: true, - ..Default::default() - } - } - - /// Returns [`Modifiers`] with command + shift. - pub fn control_shift() -> Modifiers { - Modifiers { - shift: true, - control: true, - ..Default::default() - } - } - - /// Checks if this [`Modifiers`] is a subset of another [`Modifiers`]. - pub fn is_subset_of(&self, other: &Modifiers) -> bool { - (*other & *self) == *self - } -} - -impl std::ops::BitOr for Modifiers { - type Output = Self; - - fn bitor(mut self, other: Self) -> Self::Output { - self |= other; - self - } -} - -impl std::ops::BitOrAssign for Modifiers { - fn bitor_assign(&mut self, other: Self) { - self.control |= other.control; - self.alt |= other.alt; - self.shift |= other.shift; - self.platform |= other.platform; - self.function |= other.function; - } -} - -impl std::ops::BitXor for Modifiers { - type Output = Self; - fn bitxor(mut self, rhs: Self) -> Self::Output { - self ^= rhs; - self - } -} - -impl std::ops::BitXorAssign for Modifiers { - fn bitxor_assign(&mut self, other: Self) { - self.control ^= other.control; - self.alt ^= other.alt; - self.shift ^= other.shift; - self.platform ^= other.platform; - self.function ^= other.function; - } -} - -impl std::ops::BitAnd for Modifiers { - type Output = Self; - fn bitand(mut self, rhs: Self) -> Self::Output { - self &= rhs; - self - } -} - -impl std::ops::BitAndAssign for Modifiers { - fn bitand_assign(&mut self, other: Self) { - self.control &= other.control; - self.alt &= other.alt; - self.shift &= other.shift; - self.platform &= other.platform; - self.function &= other.function; - } -} - -/// The state of the capslock key at some point in time -#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize, Hash, JsonSchema)] -pub struct Capslock { - /// The capslock key is on - #[serde(default)] - pub on: bool, -} - -impl AsKeystroke for Keystroke { - fn as_keystroke(&self) -> &Keystroke { - self - } -} - -impl AsKeystroke for KeybindingKeystroke { - fn as_keystroke(&self) -> &Keystroke { - &self.inner - } -} - -fn display_modifiers(modifiers: &Modifiers, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if modifiers.control { - #[cfg(target_os = "macos")] - f.write_char('^')?; - - #[cfg(not(target_os = "macos"))] - write!(f, "ctrl-")?; - } - if modifiers.alt { - #[cfg(target_os = "macos")] - f.write_char('⌥')?; - - #[cfg(not(target_os = "macos"))] - write!(f, "alt-")?; - } - if modifiers.platform { - #[cfg(target_os = "macos")] - f.write_char('⌘')?; - - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - f.write_char('❖')?; - - #[cfg(target_os = "windows")] - f.write_char('⊞')?; - } - if modifiers.shift { - #[cfg(target_os = "macos")] - f.write_char('⇧')?; - - #[cfg(not(target_os = "macos"))] - write!(f, "shift-")?; - } - Ok(()) -} - -fn display_key(key: &str, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let key = match key { - #[cfg(target_os = "macos")] - "backspace" => '⌫', - #[cfg(target_os = "macos")] - "up" => '↑', - #[cfg(target_os = "macos")] - "down" => '↓', - #[cfg(target_os = "macos")] - "left" => '←', - #[cfg(target_os = "macos")] - "right" => '→', - #[cfg(target_os = "macos")] - "tab" => '⇥', - #[cfg(target_os = "macos")] - "escape" => '⎋', - #[cfg(target_os = "macos")] - "shift" => '⇧', - #[cfg(target_os = "macos")] - "control" => '⌃', - #[cfg(target_os = "macos")] - "alt" => '⌥', - #[cfg(target_os = "macos")] - "platform" => '⌘', - - key if key.len() == 1 => key.chars().next().unwrap().to_ascii_uppercase(), - key => return f.write_str(key), - }; - f.write_char(key) -} - -#[inline] -fn unparse(modifiers: &Modifiers, key: &str) -> String { - let mut result = String::new(); - if modifiers.function { - result.push_str("fn-"); - } - if modifiers.control { - result.push_str("ctrl-"); - } - if modifiers.alt { - result.push_str("alt-"); - } - if modifiers.platform { - #[cfg(target_os = "macos")] - result.push_str("cmd-"); - - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - result.push_str("super-"); - - #[cfg(target_os = "windows")] - result.push_str("win-"); - } - if modifiers.shift { - result.push_str("shift-"); - } - result.push_str(&key); - result -} diff --git a/crates/gpui_pre/src/platform/layer_shell.rs b/crates/gpui_pre/src/platform/layer_shell.rs deleted file mode 100644 index 8be1b5fc..00000000 --- a/crates/gpui_pre/src/platform/layer_shell.rs +++ /dev/null @@ -1,83 +0,0 @@ -use bitflags::bitflags; -use thiserror::Error; - -use crate::Pixels; - -/// The layer the surface is rendered on. Multiple surfaces can share a layer, and ordering within -/// a single layer is undefined. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub enum Layer { - /// The background layer, typically used for wallpapers. - Background, - - /// The bottom layer. - Bottom, - - /// The top layer, typically used for fullscreen windows. - Top, - - /// The overlay layer, used for surfaces that should always be on top. - #[default] - Overlay, -} - -bitflags! { - /// Screen anchor point for layer_shell surfaces. These can be used in any combination, e.g. - /// specifying `Anchor::LEFT | Anchor::RIGHT` will stretch the surface across the width of the - /// screen. - #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] - pub struct Anchor: u32 { - /// Anchor to the top edge of the screen. - const TOP = 1; - /// Anchor to the bottom edge of the screen. - const BOTTOM = 2; - /// Anchor to the left edge of the screen. - const LEFT = 4; - /// Anchor to the right edge of the screen. - const RIGHT = 8; - } -} - -/// Keyboard interactivity mode for the layer_shell surfaces. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub enum KeyboardInteractivity { - /// No keyboard inputs will be delivered to the surface and it won't be able to receive - /// keyboard focus. - None, - - /// The surface will receive exclusive keyboard focus as long as it is above the shell surface - /// layer, and no other layer_shell surfaces are above it. - Exclusive, - - /// The surface can be focused similarly to a normal window. - #[default] - OnDemand, -} - -/// Options for creating a layer_shell window. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct LayerShellOptions { - /// The namespace for the surface, mostly used by compositors to apply rules, can not be - /// changed after the surface is created. - pub namespace: String, - /// The layer the surface is rendered on. - pub layer: Layer, - /// The anchor point of the surface. - pub anchor: Anchor, - /// Requests that the compositor avoids occluding an area with other surfaces. - pub exclusive_zone: Option, - /// The anchor point of the exclusive zone, will be determined using the anchor if left - /// unspecified. - pub exclusive_edge: Option, - /// Margins between the surface and its anchor point(s). - /// Specified in CSS order: top, right, bottom, left. - pub margin: Option<(Pixels, Pixels, Pixels, Pixels)>, - /// How keyboard events should be delivered to the surface. - pub keyboard_interactivity: KeyboardInteractivity, -} - -/// An error indicating that an action failed because the compositor doesn't support the required -/// layer_shell protocol. -#[derive(Debug, Error)] -#[error("Compositor doesn't support zwlr_layer_shell_v1")] -pub struct LayerShellNotSupportedError; diff --git a/crates/gpui_pre/src/platform/popup.rs b/crates/gpui_pre/src/platform/popup.rs deleted file mode 100644 index a1f8d7ea..00000000 --- a/crates/gpui_pre/src/platform/popup.rs +++ /dev/null @@ -1,134 +0,0 @@ -use bitflags::bitflags; -use thiserror::Error; - -use crate::{AnyWindowHandle, Bounds, Pixels, Point}; - -/// Options for a parent-anchored popup window such as a menu, dropdown, context menu or tooltip. -/// -/// A popup is placed relative to an anchor rectangle on its parent window rather than at an -/// absolute screen position. The platform resolves the final position, so this works both on -/// systems where the compositor owns window placement (Wayland) and on platforms with absolute -/// coordinates. -/// -/// The popup's size comes from [`WindowOptions::window_bounds`](crate::WindowOptions), whose -/// origin is ignored. All coordinates are in logical pixels. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PopupOptions { - /// The window the popup is anchored to. - pub parent: AnyWindowHandle, - - /// The rectangle the popup is positioned relative to, in the parent window's coordinate - /// space (the same space element bounds are in). For example, a dropdown menu uses the - /// bounds of the button that opened it. - pub anchor_rect: Bounds, - - /// Which point of [`Self::anchor_rect`] the popup is anchored to. - pub anchor: PopupAnchor, - - /// The direction in which the popup extends away from the anchor point. A dropdown that - /// drops below its button anchors to [`PopupAnchor::BottomLeft`] with a gravity of - /// [`PopupGravity::BottomRight`] so it grows down and to the right. - pub gravity: PopupGravity, - - /// How the platform may adjust the popup if the requested placement would put it off-screen. - pub constraint_adjustment: PopupConstraintAdjustment, - - /// An additional offset applied to the popup after anchoring. - pub offset: Point, - - /// Whether the popup should take an explicit input grab. - /// - /// Grabbing popups behave like menus: they take keyboard focus and are dismissed when the - /// user clicks outside of them or presses a dismissing key. Use it for menus and comboboxes, - /// not for tooltips or other passive popups. - /// - /// A grab must be requested while the triggering input is still active, in practice the - /// press of the mouse button that opens the popup. Open grabbing popups from a mouse-down - /// handler rather than a click handler, otherwise the grab is refused. - /// - /// Automatic dismissal only covers input aimed at other applications. A click elsewhere in - /// your own application still reaches it as usual, so closing the popup in that case is up - /// to you. Nested grabbing popups must be closed in the reverse order they were opened. - pub grab: bool, -} - -/// The point of the anchor rectangle that a popup is anchored to. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub enum PopupAnchor { - /// Anchor to the center of the anchor rectangle. - #[default] - Center, - /// Anchor to the center of the top edge. - Top, - /// Anchor to the center of the bottom edge. - Bottom, - /// Anchor to the center of the left edge. - Left, - /// Anchor to the center of the right edge. - Right, - /// Anchor to the top-left corner. - TopLeft, - /// Anchor to the bottom-left corner. - BottomLeft, - /// Anchor to the top-right corner. - TopRight, - /// Anchor to the bottom-right corner. - BottomRight, -} - -/// The direction in which a popup extends away from its anchor point. -/// -/// For instance, a gravity of [`PopupGravity::BottomRight`] places the popup below and to the -/// right of the anchor point. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub enum PopupGravity { - /// The popup is centered over the anchor point. - #[default] - Center, - /// The popup extends upwards from the anchor point. - Top, - /// The popup extends downwards from the anchor point. - Bottom, - /// The popup extends to the left of the anchor point. - Left, - /// The popup extends to the right of the anchor point. - Right, - /// The popup extends up and to the left of the anchor point. - TopLeft, - /// The popup extends down and to the left of the anchor point. - BottomLeft, - /// The popup extends up and to the right of the anchor point. - TopRight, - /// The popup extends down and to the right of the anchor point. - BottomRight, -} - -bitflags! { - /// How a popup may be adjusted by the platform if the requested placement would put it - /// off-screen. If no flags are set, the popup is placed exactly as requested and may be - /// clipped. - #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] - pub struct PopupConstraintAdjustment: u32 { - /// The popup may be slid horizontally to stay on-screen. - const SLIDE_X = 1; - /// The popup may be slid vertically to stay on-screen. - const SLIDE_Y = 2; - /// The popup's anchor and gravity may be flipped horizontally to stay on-screen. - const FLIP_X = 4; - /// The popup's anchor and gravity may be flipped vertically to stay on-screen. - const FLIP_Y = 8; - /// The popup may be shrunk horizontally to stay on-screen. - const RESIZE_X = 16; - /// The popup may be shrunk vertically to stay on-screen. - const RESIZE_Y = 32; - } -} - -/// Returned when the current platform has no native popup implementation yet. -/// -/// Native popups are separate from gpui's in-window popovers, which are drawn as elements inside -/// an existing window. A caller that wants a popup on every platform should treat this error as -/// a cue to fall back to that in-window rendering. -#[derive(Debug, Error)] -#[error("popups are not supported on this platform")] -pub struct PopupNotSupportedError; diff --git a/crates/gpui_pre/src/platform/scap_screen_capture.rs b/crates/gpui_pre/src/platform/scap_screen_capture.rs deleted file mode 100644 index 797e19ba..00000000 --- a/crates/gpui_pre/src/platform/scap_screen_capture.rs +++ /dev/null @@ -1,325 +0,0 @@ -//! Screen capture for Linux and Windows -use crate::{ - DevicePixels, ForegroundExecutor, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream, - Size, SourceMetadata, size, -}; -use anyhow::{Context as _, Result, anyhow}; -use futures::channel::oneshot; -use scap::Target; -use std::rc::Rc; -use std::sync::Arc; -use std::sync::atomic::{self, AtomicBool}; - -/// Populates the receiver with the screens that can be captured. -/// -/// `scap_default_target_source` should be used instead on Wayland, since `scap_screen_sources` -/// won't return any results. -#[allow(dead_code)] -pub fn scap_screen_sources( - foreground_executor: &ForegroundExecutor, -) -> oneshot::Receiver>>> { - let (sources_tx, sources_rx) = oneshot::channel(); - get_screen_targets(sources_tx); - to_dyn_screen_capture_sources(sources_rx, foreground_executor) -} - -/// Starts screen capture for the default target, and populates the receiver with a single source -/// for it. The first frame of the screen capture is used to determine the size of the stream. -/// -/// On Wayland (Linux), prompts the user to select a target, and populates the receiver with a -/// single screen capture source for their selection. -#[allow(dead_code)] -pub(crate) fn start_scap_default_target_source( - foreground_executor: &ForegroundExecutor, -) -> oneshot::Receiver>>> { - let (sources_tx, sources_rx) = oneshot::channel(); - start_default_target_screen_capture(sources_tx); - to_dyn_screen_capture_sources(sources_rx, foreground_executor) -} - -struct ScapCaptureSource { - target: scap::Display, - size: Size, -} - -/// Populates the sender with the screens available for capture. -fn get_screen_targets(sources_tx: oneshot::Sender>>) { - // Due to use of blocking APIs, a new thread is used. - std::thread::spawn(|| { - let targets = match scap::get_all_targets() { - Ok(targets) => targets, - Err(err) => { - sources_tx.send(Err(err)).ok(); - return; - } - }; - let sources = targets - .into_iter() - .filter_map(|target| match target { - scap::Target::Display(display) => { - let size = Size { - width: DevicePixels(display.width as i32), - height: DevicePixels(display.height as i32), - }; - Some(ScapCaptureSource { - target: display, - size, - }) - } - scap::Target::Window(_) => None, - }) - .collect::>(); - sources_tx.send(Ok(sources)).ok(); - }); -} - -impl ScreenCaptureSource for ScapCaptureSource { - fn metadata(&self) -> Result { - Ok(SourceMetadata { - resolution: self.size, - label: Some(self.target.title.clone().into()), - is_main: None, - id: self.target.id as u64, - }) - } - - fn stream( - &self, - foreground_executor: &ForegroundExecutor, - frame_callback: Box, - ) -> oneshot::Receiver>> { - let (stream_tx, stream_rx) = oneshot::channel(); - let target = self.target.clone(); - - // Due to use of blocking APIs, a dedicated thread is used. - std::thread::spawn(move || { - match new_scap_capturer(Some(scap::Target::Display(target.clone()))) { - Ok(mut capturer) => { - capturer.start_capture(); - run_capture(capturer, target.clone(), frame_callback, stream_tx); - } - Err(e) => { - stream_tx.send(Err(e)).ok(); - } - } - }); - - to_dyn_screen_capture_stream(stream_rx, foreground_executor) - } -} - -struct ScapDefaultTargetCaptureSource { - // Sender populated by single call to `ScreenCaptureSource::stream`. - stream_call_tx: std::sync::mpsc::SyncSender<( - // Provides the result of `ScreenCaptureSource::stream`. - oneshot::Sender>, - // Callback for frames. - Box, - )>, - target: scap::Display, - size: Size, -} - -/// Starts screen capture on the default capture target, and populates the sender with the source. -fn start_default_target_screen_capture( - sources_tx: oneshot::Sender>>, -) { - // Due to use of blocking APIs, a dedicated thread is used. - std::thread::spawn(|| { - let start_result = gpui_util::maybe!({ - let mut capturer = new_scap_capturer(None)?; - capturer.start_capture(); - let first_frame = capturer - .get_next_frame() - .context("Failed to get first frame of screenshare to get the size.")?; - let size = frame_size(&first_frame); - let target = capturer - .target() - .context("Unable to determine the target display.")?; - let target = target.clone(); - Ok((capturer, size, target)) - }); - - match start_result { - Ok((capturer, size, Target::Display(display))) => { - let (stream_call_tx, stream_rx) = std::sync::mpsc::sync_channel(1); - sources_tx - .send(Ok(vec![ScapDefaultTargetCaptureSource { - stream_call_tx, - size, - target: display.clone(), - }])) - .ok(); - let Ok((stream_tx, frame_callback)) = stream_rx.recv() else { - return; - }; - run_capture(capturer, display, frame_callback, stream_tx); - } - Err(e) => { - sources_tx.send(Err(e)).ok(); - } - _ => { - sources_tx - .send(Err(anyhow!("The screen capture source is not a display"))) - .ok(); - } - } - }); -} - -impl ScreenCaptureSource for ScapDefaultTargetCaptureSource { - fn metadata(&self) -> Result { - Ok(SourceMetadata { - resolution: self.size, - label: None, - is_main: None, - id: self.target.id as u64, - }) - } - - fn stream( - &self, - foreground_executor: &ForegroundExecutor, - frame_callback: Box, - ) -> oneshot::Receiver>> { - let (tx, rx) = oneshot::channel(); - match self.stream_call_tx.try_send((tx, frame_callback)) { - Ok(()) => {} - Err(std::sync::mpsc::TrySendError::Full((tx, _))) - | Err(std::sync::mpsc::TrySendError::Disconnected((tx, _))) => { - // Note: support could be added for being called again after end of prior stream. - tx.send(Err(anyhow!( - "Can't call ScapDefaultTargetCaptureSource::stream multiple times." - ))) - .ok(); - } - } - to_dyn_screen_capture_stream(rx, foreground_executor) - } -} - -fn new_scap_capturer(target: Option) -> Result { - scap::capturer::Capturer::build(scap::capturer::Options { - fps: 60, - show_cursor: true, - show_highlight: true, - // Note that the actual frame output type may differ. - output_type: scap::frame::FrameType::YUVFrame, - output_resolution: scap::capturer::Resolution::Captured, - crop_area: None, - target, - excluded_targets: None, - }) -} - -fn run_capture( - mut capturer: scap::capturer::Capturer, - display: scap::Display, - frame_callback: Box, - stream_tx: oneshot::Sender>, -) { - let cancel_stream = Arc::new(AtomicBool::new(false)); - let size = Size { - width: DevicePixels(display.width as i32), - height: DevicePixels(display.height as i32), - }; - let stream_send_result = stream_tx.send(Ok(ScapStream { - cancel_stream: cancel_stream.clone(), - display, - size, - })); - if stream_send_result.is_err() { - return; - } - while !cancel_stream.load(std::sync::atomic::Ordering::SeqCst) { - match capturer.get_next_frame() { - Ok(frame) => frame_callback(ScreenCaptureFrame(frame)), - Err(err) => { - log::error!("Halting screen capture due to error: {err}"); - break; - } - } - } - capturer.stop_capture(); -} - -struct ScapStream { - cancel_stream: Arc, - display: scap::Display, - size: Size, -} - -impl ScreenCaptureStream for ScapStream { - fn metadata(&self) -> Result { - Ok(SourceMetadata { - resolution: self.size, - label: Some(self.display.title.clone().into()), - is_main: None, - id: self.display.id as u64, - }) - } -} - -impl Drop for ScapStream { - fn drop(&mut self) { - self.cancel_stream.store(true, atomic::Ordering::SeqCst); - } -} - -fn frame_size(frame: &scap::frame::Frame) -> Size { - let (width, height) = match frame { - scap::frame::Frame::YUVFrame(frame) => (frame.width, frame.height), - scap::frame::Frame::RGB(frame) => (frame.width, frame.height), - scap::frame::Frame::RGBx(frame) => (frame.width, frame.height), - scap::frame::Frame::XBGR(frame) => (frame.width, frame.height), - scap::frame::Frame::BGRx(frame) => (frame.width, frame.height), - scap::frame::Frame::BGR0(frame) => (frame.width, frame.height), - scap::frame::Frame::BGRA(frame) => (frame.width, frame.height), - }; - size(DevicePixels(width), DevicePixels(height)) -} - -/// This is used by `get_screen_targets` and `start_default_target_screen_capture` to turn their -/// results into `Rc`. They need to `Send` their capture source, and so -/// the capture source structs are used as `Rc` is not `Send`. -fn to_dyn_screen_capture_sources( - sources_rx: oneshot::Receiver>>, - foreground_executor: &ForegroundExecutor, -) -> oneshot::Receiver>>> { - let (dyn_sources_tx, dyn_sources_rx) = oneshot::channel(); - foreground_executor - .spawn(async move { - match sources_rx.await { - Ok(Ok(results)) => dyn_sources_tx - .send(Ok(results - .into_iter() - .map(|source| Rc::new(source) as Rc) - .collect::>())) - .ok(), - Ok(Err(err)) => dyn_sources_tx.send(Err(err)).ok(), - Err(oneshot::Canceled) => None, - } - }) - .detach(); - dyn_sources_rx -} - -/// Same motivation as `to_dyn_screen_capture_sources` above. -fn to_dyn_screen_capture_stream( - sources_rx: oneshot::Receiver>, - foreground_executor: &ForegroundExecutor, -) -> oneshot::Receiver>> { - let (dyn_sources_tx, dyn_sources_rx) = oneshot::channel(); - foreground_executor - .spawn(async move { - match sources_rx.await { - Ok(Ok(stream)) => dyn_sources_tx - .send(Ok(Box::new(stream) as Box)) - .ok(), - Ok(Err(err)) => dyn_sources_tx.send(Err(err)).ok(), - Err(oneshot::Canceled) => None, - } - }) - .detach(); - dyn_sources_rx -} diff --git a/crates/gpui_pre/src/platform/test.rs b/crates/gpui_pre/src/platform/test.rs deleted file mode 100644 index a327d8fe..00000000 --- a/crates/gpui_pre/src/platform/test.rs +++ /dev/null @@ -1,12 +0,0 @@ -mod dispatcher; -mod display; -mod platform; -mod window; - -pub use dispatcher::*; -pub(crate) use display::*; -pub(crate) use platform::*; -pub(crate) use window::*; - -#[cfg(any(test, feature = "test-support"))] -pub use platform::{TestScreenCaptureSource, TestScreenCaptureStream}; diff --git a/crates/gpui_pre/src/platform/test/dispatcher.rs b/crates/gpui_pre/src/platform/test/dispatcher.rs deleted file mode 100644 index c48e4913..00000000 --- a/crates/gpui_pre/src/platform/test/dispatcher.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::{PlatformDispatcher, Priority, RunnableVariant}; -use scheduler::Instant; -use scheduler::{Clock, Scheduler, SessionId, TestScheduler, TestSchedulerConfig, Yield}; -use std::{ - sync::{ - Arc, - atomic::{AtomicUsize, Ordering}, - }, - time::Duration, -}; - -/// TestDispatcher provides deterministic async execution for tests. -/// -/// This implementation delegates task scheduling to the scheduler crate's `TestScheduler`. -/// Access the scheduler directly via `scheduler()` for clock, rng, and parking control. -#[doc(hidden)] -pub struct TestDispatcher { - session_id: SessionId, - scheduler: Arc, - num_cpus_override: Arc, -} - -impl TestDispatcher { - pub fn new(seed: u64) -> Self { - let scheduler = Arc::new(TestScheduler::new(TestSchedulerConfig { - seed, - randomize_order: true, - allow_parking: false, - capture_pending_traces: std::env::var("PENDING_TRACES") - .map_or(false, |var| var == "1" || var == "true"), - timeout_ticks: 0..=1000, - })); - Self::from_scheduler(scheduler) - } - - pub fn from_scheduler(scheduler: Arc) -> Self { - TestDispatcher { - session_id: scheduler.allocate_session_id(), - scheduler, - num_cpus_override: Arc::new(AtomicUsize::new(0)), - } - } - - pub fn scheduler(&self) -> &Arc { - &self.scheduler - } - - pub fn session_id(&self) -> SessionId { - self.session_id - } - - pub fn drain_tasks(&self) { - self.scheduler.drain_tasks(); - } - - pub fn advance_clock(&self, by: Duration) { - self.scheduler.advance_clock(by); - } - - pub fn advance_clock_to_next_timer(&self) -> bool { - self.scheduler.advance_clock_to_next_timer() - } - - pub fn simulate_random_delay(&self) -> Yield { - self.scheduler.yield_random() - } - - pub fn tick(&self, background_only: bool) -> bool { - if background_only { - self.scheduler.tick_background_only() - } else { - self.scheduler.tick() - } - } - - pub fn run_until_parked(&self) { - while self.tick(false) {} - } - - pub fn allow_parking(&self) { - self.scheduler.allow_parking(); - } - - pub fn forbid_parking(&self) { - self.scheduler.forbid_parking(); - } - - /// Override the value returned by `BackgroundExecutor::num_cpus()` in tests. - /// A value of 0 means no override (the default of 4 is used). - pub fn set_num_cpus(&self, count: usize) { - self.num_cpus_override.store(count, Ordering::SeqCst); - } - - /// Returns the overridden CPU count, or `None` if no override is set. - pub fn num_cpus_override(&self) -> Option { - match self.num_cpus_override.load(Ordering::SeqCst) { - 0 => None, - n => Some(n), - } - } -} - -impl Clone for TestDispatcher { - fn clone(&self) -> Self { - let session_id = self.scheduler.allocate_session_id(); - Self { - session_id, - scheduler: self.scheduler.clone(), - num_cpus_override: self.num_cpus_override.clone(), - } - } -} - -impl PlatformDispatcher for TestDispatcher { - fn is_main_thread(&self) -> bool { - self.scheduler.is_main_thread() - } - - fn now(&self) -> Instant { - self.scheduler.clock().now() - } - - fn dispatch(&self, runnable: RunnableVariant, priority: Priority) { - self.scheduler - .schedule_background_with_priority(runnable, priority); - } - - fn dispatch_on_main_thread(&self, runnable: RunnableVariant, _priority: Priority) { - self.scheduler.schedule_local(self.session_id, runnable); - } - - fn dispatch_after(&self, _duration: Duration, _runnable: RunnableVariant) { - panic!( - "dispatch_after should not be called in tests. \ - Use BackgroundExecutor::timer() which uses the scheduler's native timer." - ); - } - - fn as_test(&self) -> Option<&TestDispatcher> { - Some(self) - } - - fn spawn_realtime(&self, f: Box) { - std::thread::spawn(move || { - f(); - }); - } -} diff --git a/crates/gpui_pre/src/platform/test/display.rs b/crates/gpui_pre/src/platform/test/display.rs deleted file mode 100644 index c4adb014..00000000 --- a/crates/gpui_pre/src/platform/test/display.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::{Bounds, DisplayId, Pixels, PlatformDisplay, Point, px}; -use anyhow::{Ok, Result}; - -#[derive(Debug)] -pub(crate) struct TestDisplay { - id: DisplayId, - uuid: uuid::Uuid, - bounds: Bounds, -} - -impl TestDisplay { - pub fn new() -> Self { - TestDisplay { - id: DisplayId(1), - uuid: uuid::Uuid::new_v4(), - bounds: Bounds::from_corners(Point::default(), Point::new(px(1920.), px(1080.))), - } - } -} - -impl PlatformDisplay for TestDisplay { - fn id(&self) -> crate::DisplayId { - self.id - } - - fn uuid(&self) -> Result { - Ok(self.uuid) - } - - fn bounds(&self) -> crate::Bounds { - self.bounds - } -} diff --git a/crates/gpui_pre/src/platform/test/platform.rs b/crates/gpui_pre/src/platform/test/platform.rs deleted file mode 100644 index 5e82fbe3..00000000 --- a/crates/gpui_pre/src/platform/test/platform.rs +++ /dev/null @@ -1,676 +0,0 @@ -#[cfg(any(test, feature = "test-support"))] -use crate::NoopTextSystem; -#[cfg(any(test, feature = "test-support"))] -use crate::PathPromptOptions; -use crate::{ - AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DevicePixels, - DummyKeyboardMapper, ForegroundExecutor, Keymap, Platform, PlatformDisplay, - PlatformHeadlessRenderer, PlatformKeyboardLayout, PlatformKeyboardMapper, PlatformTextSystem, - PromptButton, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream, SharedString, - SourceMetadata, SystemNotification, SystemNotificationResponse, Task, TestDisplay, TestWindow, - ThermalState, WindowAppearance, WindowParams, size, -}; -use anyhow::Result; -#[cfg(any(test, feature = "test-support"))] -use collections::VecDeque; -use futures::channel::oneshot; -use parking_lot::Mutex; -use std::{ - cell::RefCell, - path::{Path, PathBuf}, - rc::{Rc, Weak}, - sync::Arc, -}; - -/// TestPlatform implements the Platform trait for use in tests. -pub(crate) struct TestPlatform { - background_executor: BackgroundExecutor, - foreground_executor: ForegroundExecutor, - - pub(crate) active_window: RefCell>, - active_display: Rc, - active_cursor: Mutex, - current_clipboard_item: Mutex>, - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - current_primary_item: Mutex>, - #[cfg(target_os = "macos")] - current_find_pasteboard_item: Mutex>, - #[cfg(any(test, feature = "test-support"))] - pub(crate) prompts: RefCell, - screen_capture_sources: RefCell>, - pub opened_url: RefCell>, - pub(crate) system_notifications: RefCell, - pub text_system: Arc, - pub expect_restart: - RefCell, Vec)>>>, - headless_renderer_factory: Option Option>>>, - weak: Weak, -} - -#[derive(Clone)] -/// A fake screen capture source, used for testing. -pub struct TestScreenCaptureSource {} - -/// A fake screen capture stream, used for testing. -pub struct TestScreenCaptureStream {} - -impl ScreenCaptureSource for TestScreenCaptureSource { - fn metadata(&self) -> Result { - Ok(SourceMetadata { - id: 0, - is_main: None, - label: None, - resolution: size(DevicePixels(1), DevicePixels(1)), - }) - } - - fn stream( - &self, - _foreground_executor: &ForegroundExecutor, - _frame_callback: Box, - ) -> oneshot::Receiver>> { - let (mut tx, rx) = oneshot::channel(); - let stream = TestScreenCaptureStream {}; - tx.send(Ok(Box::new(stream) as Box)) - .ok(); - rx - } -} - -impl ScreenCaptureStream for TestScreenCaptureStream { - fn metadata(&self) -> Result { - TestScreenCaptureSource {}.metadata() - } -} - -#[cfg(any(test, feature = "test-support"))] -struct TestPrompt { - msg: String, - detail: Option, - answers: Vec, - tx: oneshot::Sender, -} - -#[derive(Default)] -pub(crate) struct TestSystemNotifications { - pub(crate) app_identity: Option<(SharedString, SharedString)>, - pub(crate) shown: Vec, - pub(crate) delivered: Vec, - pub(crate) dismissed: Vec, - response_callback: Option>, -} - -#[cfg(any(test, feature = "test-support"))] -#[derive(Default)] -pub(crate) struct TestPrompts { - multiple_choice: VecDeque, - new_path: VecDeque<(PathBuf, oneshot::Sender>>)>, - paths: VecDeque<( - PathPromptOptions, - oneshot::Sender>>>, - )>, -} - -impl TestPlatform { - #[cfg(any(test, feature = "test-support"))] - pub fn new(executor: BackgroundExecutor, foreground_executor: ForegroundExecutor) -> Rc { - Self::with_platform( - executor, - foreground_executor, - Arc::new(NoopTextSystem), - None, - ) - } - - #[cfg(any(test, feature = "test-support"))] - pub fn with_text_system( - executor: BackgroundExecutor, - foreground_executor: ForegroundExecutor, - text_system: Arc, - ) -> Rc { - Self::with_platform(executor, foreground_executor, text_system, None) - } - - pub fn with_platform( - executor: BackgroundExecutor, - foreground_executor: ForegroundExecutor, - text_system: Arc, - headless_renderer_factory: Option< - Box Option>>, - >, - ) -> Rc { - Rc::new_cyclic(|weak| TestPlatform { - background_executor: executor, - foreground_executor, - #[cfg(any(test, feature = "test-support"))] - prompts: Default::default(), - screen_capture_sources: Default::default(), - active_cursor: Default::default(), - active_display: Rc::new(TestDisplay::new()), - active_window: Default::default(), - expect_restart: Default::default(), - current_clipboard_item: Mutex::new(None), - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - current_primary_item: Mutex::new(None), - #[cfg(target_os = "macos")] - current_find_pasteboard_item: Mutex::new(None), - weak: weak.clone(), - opened_url: Default::default(), - system_notifications: Default::default(), - text_system, - headless_renderer_factory, - }) - } - - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn simulate_new_path_selection( - &self, - select_path: impl FnOnce(&std::path::Path) -> Option, - ) { - let (path, tx) = self - .prompts - .borrow_mut() - .new_path - .pop_front() - .expect("no pending new path prompt"); - tx.send(Ok(select_path(&path))).ok(); - } - - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn simulate_path_prompt_response( - &self, - select_paths: impl FnOnce(&PathPromptOptions) -> Option>, - ) { - let (options, tx) = self - .prompts - .borrow_mut() - .paths - .pop_front() - .expect("no pending paths prompt"); - let selection = select_paths(&options); - if let Some(paths) = &selection - && !options.multiple - && paths.len() > 1 - { - panic!( - "selected {} paths for a prompt that does not allow multiple selection", - paths.len() - ); - } - tx.send(Ok(selection)).ok(); - } - - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn did_prompt_for_paths(&self) -> bool { - !self.prompts.borrow().paths.is_empty() - } - - #[cfg(any(test, feature = "test-support"))] - #[track_caller] - pub(crate) fn simulate_prompt_answer(&self, response: &str) { - let prompt = self - .prompts - .borrow_mut() - .multiple_choice - .pop_front() - .expect("no pending multiple choice prompt"); - let Some(ix) = prompt.answers.iter().position(|a| a == response) else { - panic!( - "PROMPT: {}\n{:?}\n{:?}\nCannot respond with {}", - prompt.msg, prompt.detail, prompt.answers, response - ) - }; - prompt.tx.send(ix).ok(); - } - - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn has_pending_prompt(&self) -> bool { - !self.prompts.borrow().multiple_choice.is_empty() - } - - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn pending_prompt(&self) -> Option<(String, String)> { - let prompts = self.prompts.borrow(); - let prompt = prompts.multiple_choice.front()?; - Some(( - prompt.msg.clone(), - prompt.detail.clone().unwrap_or_default(), - )) - } - - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn set_screen_capture_sources(&self, sources: Vec) { - *self.screen_capture_sources.borrow_mut() = sources; - } - - /// Queues the prompt so a test can later inspect or answer it through - /// [`Self::pending_prompt`] and [`Self::simulate_prompt_answer`]. - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn prompt( - &self, - msg: &str, - detail: Option<&str>, - answers: &[PromptButton], - ) -> oneshot::Receiver { - let (tx, rx) = oneshot::channel(); - let answers: Vec = answers.iter().map(|s| s.label().to_string()).collect(); - self.prompts - .borrow_mut() - .multiple_choice - .push_back(TestPrompt { - msg: msg.to_string(), - detail: detail.map(|s| s.to_string()), - answers, - tx, - }); - rx - } - - /// Benchmarks have no API to answer a prompt, so this doesn't retain it - /// for later inspection; dropping the sender immediately cancels the - /// returned receiver instead of leaving it pending indefinitely. - #[cfg(not(any(test, feature = "test-support")))] - pub(crate) fn prompt( - &self, - _msg: &str, - _detail: Option<&str>, - _answers: &[PromptButton], - ) -> oneshot::Receiver { - oneshot::channel().1 - } - - pub(crate) fn set_active_window(&self, window: Option) { - let executor = self.foreground_executor(); - let previous_window = self.active_window.borrow_mut().take(); - self.active_window.borrow_mut().clone_from(&window); - - executor - .spawn(async move { - if let Some(previous_window) = previous_window { - if let Some(window) = window.as_ref() - && Rc::ptr_eq(&previous_window.0, &window.0) - { - return; - } - previous_window.simulate_active_status_change(false); - } - if let Some(window) = window { - window.simulate_active_status_change(true); - } - }) - .detach(); - } - - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn did_prompt_for_new_path(&self) -> bool { - !self.prompts.borrow().new_path.is_empty() - } - - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn app_identity(&self) -> Option<(SharedString, SharedString)> { - self.system_notifications.borrow().app_identity.clone() - } - - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn shown_system_notifications(&self) -> Vec { - self.system_notifications.borrow().shown.clone() - } - - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn delivered_system_notifications(&self) -> Vec { - self.system_notifications.borrow().delivered.clone() - } - - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn dismissed_system_notifications(&self) -> Vec { - self.system_notifications.borrow().dismissed.clone() - } - - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn simulate_system_notification_response( - &self, - response: SystemNotificationResponse, - ) { - let callback = self - .system_notifications - .borrow_mut() - .response_callback - .take(); - if let Some(mut callback) = callback { - callback(response); - self.system_notifications - .borrow_mut() - .response_callback - .get_or_insert(callback); - } - } -} - -impl Platform for TestPlatform { - fn background_executor(&self) -> BackgroundExecutor { - self.background_executor.clone() - } - - fn foreground_executor(&self) -> ForegroundExecutor { - self.foreground_executor.clone() - } - - fn text_system(&self) -> Arc { - self.text_system.clone() - } - - fn keyboard_layout(&self) -> Box { - Box::new(TestKeyboardLayout) - } - - fn keyboard_mapper(&self) -> Rc { - Rc::new(DummyKeyboardMapper) - } - - fn on_keyboard_layout_change(&self, _: Box) {} - - fn on_thermal_state_change(&self, _: Box) {} - - fn thermal_state(&self) -> ThermalState { - ThermalState::Nominal - } - - fn run(&self, _on_finish_launching: Box) { - unimplemented!() - } - - fn quit(&self) {} - - fn restart(&self, path: Option, arguments: Vec) { - if let Some(tx) = self.expect_restart.take() { - tx.send((path, arguments)).unwrap(); - } - } - - fn activate(&self, _ignoring_other_apps: bool) { - // - } - - fn hide(&self) { - unimplemented!() - } - - fn hide_other_apps(&self) { - unimplemented!() - } - - fn unhide_other_apps(&self) { - unimplemented!() - } - - fn displays(&self) -> Vec> { - vec![self.active_display.clone()] - } - - fn primary_display(&self) -> Option> { - Some(self.active_display.clone()) - } - - fn is_screen_capture_supported(&self) -> bool { - true - } - - fn screen_capture_sources( - &self, - ) -> oneshot::Receiver>>> { - let (mut tx, rx) = oneshot::channel(); - tx.send(Ok(self - .screen_capture_sources - .borrow() - .iter() - .map(|source| Rc::new(source.clone()) as Rc) - .collect())) - .ok(); - rx - } - - fn active_window(&self) -> Option { - self.active_window - .borrow() - .as_ref() - .map(|window| window.0.lock().handle) - } - - fn open_window( - &self, - handle: AnyWindowHandle, - params: WindowParams, - ) -> anyhow::Result> { - let renderer = self.headless_renderer_factory.as_ref().and_then(|f| f()); - let window = TestWindow::new( - handle, - params, - self.weak.clone(), - self.active_display.clone(), - renderer, - ); - Ok(Box::new(window)) - } - - fn window_appearance(&self) -> WindowAppearance { - WindowAppearance::Light - } - - fn open_url(&self, url: &str) { - *self.opened_url.borrow_mut() = Some(url.to_string()) - } - - fn on_open_urls(&self, _callback: Box)>) { - unimplemented!() - } - - /// Queues the prompt so a test can later answer it through - /// [`Self::simulate_path_prompt_response`]. - #[cfg(any(test, feature = "test-support"))] - fn prompt_for_paths( - &self, - options: crate::PathPromptOptions, - ) -> oneshot::Receiver>>> { - let (tx, rx) = oneshot::channel(); - self.prompts.borrow_mut().paths.push_back((options, tx)); - rx - } - - /// Benchmarks have no API to answer a path prompt, so this doesn't - /// retain it for later inspection; dropping the sender immediately - /// cancels the returned receiver instead of leaving it pending - /// indefinitely. - #[cfg(not(any(test, feature = "test-support")))] - fn prompt_for_paths( - &self, - _options: crate::PathPromptOptions, - ) -> oneshot::Receiver>>> { - oneshot::channel().1 - } - - /// Queues the prompt so a test can later answer it through - /// [`Self::simulate_new_path_selection`]. - #[cfg(any(test, feature = "test-support"))] - fn prompt_for_new_path( - &self, - directory: &std::path::Path, - _suggested_name: Option<&str>, - ) -> oneshot::Receiver>> { - let (tx, rx) = oneshot::channel(); - self.prompts - .borrow_mut() - .new_path - .push_back((directory.to_path_buf(), tx)); - rx - } - - /// Benchmarks have no API to answer a new-path prompt, so this doesn't - /// retain it for later inspection; dropping the sender immediately - /// cancels the returned receiver instead of leaving it pending - /// indefinitely. - #[cfg(not(any(test, feature = "test-support")))] - fn prompt_for_new_path( - &self, - _directory: &std::path::Path, - _suggested_name: Option<&str>, - ) -> oneshot::Receiver>> { - oneshot::channel().1 - } - - fn can_select_mixed_files_and_dirs(&self) -> bool { - true - } - - fn reveal_path(&self, _path: &std::path::Path) { - unimplemented!() - } - - fn on_quit(&self, _callback: Box bool>) {} - - fn on_reopen(&self, _callback: Box) { - unimplemented!() - } - - fn on_system_wake(&self, _callback: Box) {} - - fn set_app_identity(&self, identifier: &str, name: &str) { - self.system_notifications.borrow_mut().app_identity = - Some((identifier.to_string().into(), name.to_string().into())); - } - - fn show_system_notification(&self, notification: SystemNotification) { - let mut system_notifications = self.system_notifications.borrow_mut(); - if system_notifications.app_identity.is_none() { - return; - } - - let delivered = system_notifications - .delivered - .iter_mut() - .find(|delivered| delivered.tag == notification.tag); - if let Some(delivered) = delivered { - *delivered = notification.clone(); - } else { - system_notifications.delivered.push(notification.clone()); - } - system_notifications.shown.push(notification); - } - - fn dismiss_system_notification(&self, tag: &str) { - let mut system_notifications = self.system_notifications.borrow_mut(); - system_notifications - .delivered - .retain(|notification| notification.tag != tag); - system_notifications - .dismissed - .push(SharedString::from(tag.to_string())); - } - - fn on_system_notification_response( - &self, - callback: Box, - ) { - self.system_notifications.borrow_mut().response_callback = Some(callback); - } - - fn set_menus(&self, _menus: Vec, _keymap: &Keymap) {} - fn set_dock_menu(&self, _menu: Vec, _keymap: &Keymap) {} - - fn add_recent_document(&self, _paths: &Path) {} - - fn on_app_menu_action(&self, _callback: Box) {} - - fn on_will_open_app_menu(&self, _callback: Box) {} - - fn on_validate_app_menu_command(&self, _callback: Box bool>) {} - - fn app_path(&self) -> Result { - unimplemented!() - } - - fn path_for_auxiliary_executable(&self, _name: &str) -> Result { - unimplemented!() - } - - fn set_cursor_style(&self, style: crate::CursorStyle) { - *self.active_cursor.lock() = style; - } - - fn hide_cursor_until_mouse_moves(&self) {} - - fn is_cursor_visible(&self) -> bool { - true - } - - fn should_auto_hide_scrollbars(&self) -> bool { - false - } - - fn read_from_clipboard(&self) -> Option { - self.current_clipboard_item.lock().clone() - } - - fn write_to_clipboard(&self, item: ClipboardItem) { - *self.current_clipboard_item.lock() = Some(item); - } - - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - fn read_from_primary(&self) -> Option { - self.current_primary_item.lock().clone() - } - - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - fn write_to_primary(&self, item: ClipboardItem) { - *self.current_primary_item.lock() = Some(item); - } - - #[cfg(target_os = "macos")] - fn read_from_find_pasteboard(&self) -> Option { - self.current_find_pasteboard_item.lock().clone() - } - - #[cfg(target_os = "macos")] - fn write_to_find_pasteboard(&self, item: ClipboardItem) { - *self.current_find_pasteboard_item.lock() = Some(item); - } - - fn write_credentials(&self, _url: &str, _username: &str, _password: &[u8]) -> Task> { - Task::ready(Ok(())) - } - - fn read_credentials(&self, _url: &str) -> Task)>>> { - Task::ready(Ok(None)) - } - - fn delete_credentials(&self, _url: &str) -> Task> { - Task::ready(Ok(())) - } - - fn register_url_scheme(&self, _: &str) -> Task> { - unimplemented!() - } - - fn open_with_system(&self, _path: &Path) { - unimplemented!() - } -} - -impl TestScreenCaptureSource { - /// Create a fake screen capture source, for testing. - #[cfg(any(test, feature = "test-support"))] - pub fn new() -> Self { - Self {} - } -} - -struct TestKeyboardLayout; - -impl PlatformKeyboardLayout for TestKeyboardLayout { - fn id(&self) -> &str { - "zed.keyboard.example" - } - - fn name(&self) -> &str { - "zed.keyboard.example" - } -} diff --git a/crates/gpui_pre/src/platform/test/window.rs b/crates/gpui_pre/src/platform/test/window.rs deleted file mode 100644 index 38926b64..00000000 --- a/crates/gpui_pre/src/platform/test/window.rs +++ /dev/null @@ -1,550 +0,0 @@ -use crate::{ - AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels, - DispatchEventResult, GpuSpecs, Pixels, PlatformAtlas, PlatformDisplay, - PlatformHeadlessRenderer, PlatformInput, PlatformInputHandler, PlatformWindow, Point, - PromptButton, RequestFrameOptions, Scene, Size, TestPlatform, TextInputConfiguration, - TextInputStateChange, TileId, WindowAppearance, WindowBackgroundAppearance, WindowBounds, - WindowControlArea, WindowParams, -}; -use collections::HashMap; -use gpui_util::ResultExt as _; -#[cfg(any(test, feature = "test-support"))] -use image::RgbaImage; -use parking_lot::Mutex; -use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; -use std::{ - cell::Cell, - path::PathBuf, - rc::{Rc, Weak}, - sync::{self, Arc}, -}; - -pub(crate) struct TestWindowState { - pub(crate) bounds: Bounds, - pub(crate) handle: AnyWindowHandle, - display: Rc, - pub(crate) title: Option, - pub(crate) edited: bool, - pub(crate) document_path: Option, - platform: Weak, - // TODO: Replace with `Rc` - sprite_atlas: Arc, - renderer: Option>, - pub(crate) should_close_handler: Option bool>>, - hit_test_window_control_callback: Option Option>>, - input_callback: Option DispatchEventResult>>, - active_status_change_callback: Option>, - hover_status_change_callback: Option>, - resize_callback: Option, f32)>>, - moved_callback: Option>, - appearance_change_callback: Option>, - request_frame_callback: Option>, - frame_wake_count: Rc>, - frame_scheduled: bool, - frame_callback_pending: bool, - input_handler: Option, - text_input_configurations: Vec, - text_input_state_changes: Vec, - is_fullscreen: bool, - appearance: WindowAppearance, - external_drag_files: Vec<(PathBuf, bool)>, - start_external_drag_result: bool, -} - -#[derive(Clone)] -pub struct TestWindow(pub(crate) Rc>); - -// Test windows are not backed by a real platform window, so there is no raw -// handle to report; `NotSupported` is `raw_window_handle`'s variant for exactly this. -impl HasWindowHandle for TestWindow { - fn window_handle( - &self, - ) -> Result, raw_window_handle::HandleError> { - Err(raw_window_handle::HandleError::NotSupported) - } -} - -impl HasDisplayHandle for TestWindow { - fn display_handle( - &self, - ) -> Result, raw_window_handle::HandleError> { - Err(raw_window_handle::HandleError::NotSupported) - } -} - -impl TestWindow { - pub(crate) fn new( - handle: AnyWindowHandle, - params: WindowParams, - platform: Weak, - display: Rc, - renderer: Option>, - ) -> Self { - let sprite_atlas: Arc = match &renderer { - Some(r) => r.sprite_atlas(), - None => Arc::new(TestAtlas::new()), - }; - Self(Rc::new(Mutex::new(TestWindowState { - bounds: params.bounds, - display, - platform, - handle, - sprite_atlas, - renderer, - title: Default::default(), - edited: false, - document_path: None, - should_close_handler: None, - hit_test_window_control_callback: None, - input_callback: None, - active_status_change_callback: None, - hover_status_change_callback: None, - resize_callback: None, - moved_callback: None, - appearance_change_callback: None, - request_frame_callback: None, - frame_wake_count: Rc::new(Cell::new(0)), - frame_scheduled: false, - frame_callback_pending: false, - input_handler: None, - text_input_configurations: Vec::new(), - text_input_state_changes: Vec::new(), - is_fullscreen: false, - appearance: WindowAppearance::Light, - external_drag_files: Vec::new(), - start_external_drag_result: false, - }))) - } - pub fn simulate_scheduled_frame(&self) -> bool { - let callback = { - let mut state = self.0.lock(); - if !std::mem::take(&mut state.frame_scheduled) { - return false; - } - state.frame_callback_pending = false; - state.request_frame_callback.take() - }; - let Some(mut callback) = callback else { - self.0.lock().frame_scheduled = true; - return false; - }; - - callback(RequestFrameOptions::default()); - self.0.lock().request_frame_callback = Some(callback); - true - } - - pub fn frame_scheduled(&self) -> bool { - self.0.lock().frame_scheduled - } - - /// Every [`TextInputConfiguration`] forwarded to this window, in order. - pub fn text_input_configurations(&self) -> Vec { - self.0.lock().text_input_configurations.clone() - } - - pub fn text_input_state_changes(&self) -> Vec { - self.0.lock().text_input_state_changes.clone() - } - - pub fn simulate_resize(&mut self, size: Size) { - let scale_factor = self.scale_factor(); - let mut lock = self.0.lock(); - // Always update bounds, even if no callback is registered - lock.bounds.size = size; - let Some(mut callback) = lock.resize_callback.take() else { - return; - }; - drop(lock); - callback(size, scale_factor); - self.0.lock().resize_callback = Some(callback); - } - - pub(crate) fn simulate_active_status_change(&self, active: bool) { - let mut lock = self.0.lock(); - let Some(mut callback) = lock.active_status_change_callback.take() else { - return; - }; - drop(lock); - callback(active); - self.0.lock().active_status_change_callback = Some(callback); - } - - pub fn simulate_appearance_change(&self, appearance: WindowAppearance) { - let mut lock = self.0.lock(); - lock.appearance = appearance; - let Some(mut callback) = lock.appearance_change_callback.take() else { - return; - }; - drop(lock); - callback(); - self.0.lock().appearance_change_callback = Some(callback); - } - - /// Returns how many times this window's frame waker has been invoked. - pub fn frame_wake_count(&self) -> usize { - self.0.lock().frame_wake_count.get() - } - - /// Delivers a frame request to the window, as the platform's frame source - /// would. - pub fn simulate_frame_request(&self, options: RequestFrameOptions) { - let mut lock = self.0.lock(); - let Some(mut callback) = lock.request_frame_callback.take() else { - return; - }; - drop(lock); - callback(options); - self.0.lock().request_frame_callback = Some(callback); - } - - pub fn simulate_input(&mut self, event: PlatformInput) -> bool { - let mut lock = self.0.lock(); - let Some(mut callback) = lock.input_callback.take() else { - return false; - }; - drop(lock); - let result = callback(event); - self.0.lock().input_callback = Some(callback); - !result.propagate - } - - pub fn external_drag_files(&self) -> Vec<(PathBuf, bool)> { - self.0.lock().external_drag_files.clone() - } - - pub fn set_start_external_drag_result(&self, result: bool) { - self.0.lock().start_external_drag_result = result; - } -} - -impl PlatformWindow for TestWindow { - fn bounds(&self) -> Bounds { - self.0.lock().bounds - } - - fn window_bounds(&self) -> WindowBounds { - WindowBounds::Windowed(self.bounds()) - } - - fn is_maximized(&self) -> bool { - false - } - - fn content_size(&self) -> Size { - self.bounds().size - } - - fn resize(&mut self, size: Size) { - let mut lock = self.0.lock(); - lock.bounds.size = size; - } - - fn scale_factor(&self) -> f32 { - 2.0 - } - - fn appearance(&self) -> WindowAppearance { - self.0.lock().appearance - } - - fn display(&self) -> Option> { - Some(self.0.lock().display.clone()) - } - - fn mouse_position(&self) -> Point { - Point::default() - } - - fn modifiers(&self) -> crate::Modifiers { - crate::Modifiers::default() - } - - fn capslock(&self) -> crate::Capslock { - crate::Capslock::default() - } - - fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { - self.0.lock().input_handler = Some(input_handler); - } - - fn take_input_handler(&mut self) -> Option { - self.0.lock().input_handler.take() - } - - fn set_text_input_configuration(&mut self, configuration: TextInputConfiguration) { - self.0.lock().text_input_configurations.push(configuration); - } - - fn text_input_state_changed(&self, change: TextInputStateChange) { - self.0.lock().text_input_state_changes.push(change); - } - - fn prompt( - &self, - _level: crate::PromptLevel, - msg: &str, - detail: Option<&str>, - answers: &[PromptButton], - ) -> Option> { - Some( - self.0 - .lock() - .platform - .upgrade() - .expect("platform dropped") - .prompt(msg, detail, answers), - ) - } - - fn activate(&self) { - self.0 - .lock() - .platform - .upgrade() - .unwrap() - .set_active_window(Some(self.clone())) - } - - fn is_active(&self) -> bool { - false - } - - fn is_hovered(&self) -> bool { - false - } - - fn background_appearance(&self) -> WindowBackgroundAppearance { - WindowBackgroundAppearance::Opaque - } - - fn is_subpixel_rendering_supported(&self) -> bool { - false - } - - fn set_title(&mut self, title: &str) { - self.0.lock().title = Some(title.to_owned()); - } - - fn set_app_id(&mut self, _app_id: &str) {} - - fn set_background_appearance(&self, _background: WindowBackgroundAppearance) {} - - fn set_edited(&mut self, edited: bool) { - self.0.lock().edited = edited; - } - - fn set_document_path(&self, path: Option<&std::path::Path>) { - self.0.lock().document_path = path.map(|p| p.to_path_buf()); - } - - fn show_character_palette(&self) { - unimplemented!() - } - - fn minimize(&self) { - unimplemented!() - } - - fn zoom(&self) { - unimplemented!() - } - - fn toggle_fullscreen(&self) { - let mut lock = self.0.lock(); - lock.is_fullscreen = !lock.is_fullscreen; - } - - fn is_fullscreen(&self) -> bool { - self.0.lock().is_fullscreen - } - - fn frame_waker(&self) -> Option> { - // Recording invocations (rather than delivering a frame) lets tests - // assert the wake protocol without coupling to frame timing; tests - // deliver frames explicitly via `simulate_frame_request`. - let frame_wake_count = self.0.lock().frame_wake_count.clone(); - Some(Rc::new(move || { - frame_wake_count.set(frame_wake_count.get() + 1); - })) - } - - fn on_request_frame(&self, callback: Box) { - self.0.lock().request_frame_callback = Some(callback); - } - - fn schedule_frame(&self) { - let mut state = self.0.lock(); - if !state.frame_callback_pending { - state.frame_scheduled = true; - } - } - - fn on_input(&self, callback: Box DispatchEventResult>) { - self.0.lock().input_callback = Some(callback) - } - - fn on_active_status_change(&self, callback: Box) { - self.0.lock().active_status_change_callback = Some(callback) - } - - fn on_hover_status_change(&self, callback: Box) { - self.0.lock().hover_status_change_callback = Some(callback) - } - - fn on_resize(&self, callback: Box, f32)>) { - self.0.lock().resize_callback = Some(callback) - } - - fn on_moved(&self, callback: Box) { - self.0.lock().moved_callback = Some(callback) - } - - fn on_should_close(&self, callback: Box bool>) { - self.0.lock().should_close_handler = Some(callback); - } - - fn on_close(&self, _callback: Box) {} - - fn on_hit_test_window_control(&self, callback: Box Option>) { - self.0.lock().hit_test_window_control_callback = Some(callback); - } - - fn on_appearance_changed(&self, callback: Box) { - self.0.lock().appearance_change_callback = Some(callback); - } - - fn draw(&self, scene: &Scene) { - let scale_factor = self.scale_factor(); - let mut state = self.0.lock(); - state.frame_callback_pending = true; - state.frame_scheduled = true; - let device_size: Size = state.bounds.size.to_device_pixels(scale_factor); - if let Some(renderer) = &mut state.renderer { - renderer.render_scene(scene, device_size).warn_on_err(); - } - } - - fn sprite_atlas(&self) -> sync::Arc { - self.0.lock().sprite_atlas.clone() - } - - #[cfg(any(test, feature = "test-support"))] - fn render_to_image(&self, scene: &Scene) -> anyhow::Result { - let scale_factor = self.scale_factor(); - let mut state = self.0.lock(); - let size = state.bounds.size; - if let Some(renderer) = &mut state.renderer { - let device_size: Size = size.to_device_pixels(scale_factor); - renderer.render_scene_to_image(scene, device_size) - } else { - anyhow::bail!("render_to_image not available: no HeadlessRenderer configured") - } - } - - fn as_test(&mut self) -> Option<&mut TestWindow> { - Some(self) - } - - #[cfg(target_os = "windows")] - fn get_raw_handle(&self) -> windows::Win32::Foundation::HWND { - unimplemented!() - } - - fn show_window_menu(&self, _position: Point) { - unimplemented!() - } - - fn start_window_move(&self) { - unimplemented!() - } - - fn can_start_external_drag(&self) -> bool { - true - } - - fn start_external_drag(&self, payload: &crate::ExternalDragPayload) -> bool { - let mut state = self.0.lock(); - match payload { - crate::ExternalDragPayload::Files(paths) => { - state.external_drag_files.extend_from_slice(paths.entries()); - } - } - state.start_external_drag_result - } - - fn update_ime_position(&self, _bounds: Bounds) {} - - fn gpu_specs(&self) -> Option { - None - } -} - -pub(crate) struct TestAtlasState { - next_id: u32, - tiles: HashMap, -} - -pub(crate) struct TestAtlas(Mutex); - -impl TestAtlas { - pub fn new() -> Self { - TestAtlas(Mutex::new(TestAtlasState { - next_id: 0, - tiles: HashMap::default(), - })) - } -} - -impl PlatformAtlas for TestAtlas { - fn get_or_insert_with<'a>( - &self, - key: &crate::AtlasKey, - build: &mut dyn FnMut() -> anyhow::Result< - Option<(Size, std::borrow::Cow<'a, [u8]>)>, - >, - ) -> anyhow::Result> { - let mut state = self.0.lock(); - if let Some(&tile) = state.tiles.get(key) { - return Ok(Some(tile)); - } - drop(state); - - let Some((size, _)) = build()? else { - return Ok(None); - }; - - let mut state = self.0.lock(); - state.next_id += 1; - let texture_id = state.next_id; - state.next_id += 1; - let tile_id = state.next_id; - - state.tiles.insert( - key.clone(), - crate::AtlasTile { - texture_id: AtlasTextureId { - index: texture_id, - kind: crate::AtlasTextureKind::Monochrome, - }, - tile_id: TileId(tile_id), - padding: 0, - bounds: crate::Bounds { - origin: Point::default(), - size, - }, - }, - ); - - Ok(Some(state.tiles[key])) - } - - fn remove(&self, key: &AtlasKey) { - let mut state = self.0.lock(); - state.tiles.remove(key); - } - - fn contains(&self, key: &AtlasKey) -> bool { - self.0.lock().tiles.contains_key(key) - } -} diff --git a/crates/gpui_pre/src/platform/threaded_dispatcher.rs b/crates/gpui_pre/src/platform/threaded_dispatcher.rs deleted file mode 100644 index 762464a1..00000000 --- a/crates/gpui_pre/src/platform/threaded_dispatcher.rs +++ /dev/null @@ -1,720 +0,0 @@ -use std::{ - collections::BinaryHeap, - sync::Arc, - thread, - time::{Duration, Instant}, -}; - -use parking_lot::{Condvar, Mutex}; - -use crate::{ - PlatformDispatcher, Priority, RunnableVariant, profiler, - queue::{PriorityQueueReceiver, PriorityQueueSender}, -}; - -const MIN_THREADS: usize = 2; - -/// A multithreaded [`PlatformDispatcher`] for tests and benchmarks. -/// -/// Background tasks run in parallel on a pool of worker threads and timers fire -/// in real time on a dedicated timer thread, mirroring the production -/// dispatchers (see `LinuxDispatcher`). Main-thread tasks are queued until the -/// creating thread drains them via [`Self::run_until_idle`], since there is no -/// platform run loop pumping them. -/// -/// Unlike [`TestDispatcher`](crate::TestDispatcher), which runs everything on a -/// single thread with a virtual clock, work dispatched through this dispatcher -/// executes with production concurrency. -pub struct ThreadedDispatcher { - background_sender: PriorityQueueSender, - main_sender: PriorityQueueSender, - main_receiver: Mutex>, - timers: Arc, - idle: Arc, - main_thread_id: thread::ThreadId, -} - -/// Tracks how many background and timer runnables are queued or running so -/// [`ThreadedDispatcher::run_until_idle`] knows when to stop waiting. -#[derive(Default)] -struct IdleTracker { - inflight: Mutex, - condvar: Condvar, -} - -impl IdleTracker { - fn increment(&self) { - *self.inflight.lock() += 1; - } - - fn decrement(&self) { - let mut inflight = self.inflight.lock(); - *inflight -= 1; - if *inflight == 0 { - self.condvar.notify_all(); - } - } - - /// Returns a guard that decrements the in-flight count when dropped, so - /// the count stays correct even if the runnable being executed panics. - fn decrement_on_drop(&self) -> impl Drop + '_ { - gpui_util::defer(|| self.decrement()) - } - - /// Notifies waiters while holding the in-flight lock. `run_until_idle` - /// re-checks its wake conditions under this lock before waiting, so the - /// notification can't slip between its check and its wait and be lost. - fn notify_under_lock(&self) { - let _inflight = self.inflight.lock(); - self.condvar.notify_all(); - } -} - -struct TimerQueue { - state: Mutex, - condvar: Condvar, -} - -struct TimerQueueState { - heap: BinaryHeap, - next_seq: u64, -} - -struct TimerEntry { - due: Instant, - seq: u64, - runnable: RunnableVariant, -} - -impl PartialEq for TimerEntry { - fn eq(&self, other: &Self) -> bool { - self.due == other.due && self.seq == other.seq - } -} - -impl Eq for TimerEntry {} - -impl PartialOrd for TimerEntry { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for TimerEntry { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - // Reversed so that the entry with the earliest due time (breaking ties - // by insertion order) is at the top of the max-heap. - other - .due - .cmp(&self.due) - .then_with(|| other.seq.cmp(&self.seq)) - } -} - -impl Default for ThreadedDispatcher { - fn default() -> Self { - Self::new() - } -} - -impl ThreadedDispatcher { - /// Creates a dispatcher whose main thread is the calling thread. - /// - /// Worker and timer threads live for the lifetime of the process; the - /// dispatcher is expected to be created once and reused. - pub fn new() -> Self { - let (background_sender, background_receiver) = PriorityQueueReceiver::new(); - let (main_sender, main_receiver) = PriorityQueueReceiver::new(); - let idle = Arc::new(IdleTracker::default()); - - let thread_count = - thread::available_parallelism().map_or(MIN_THREADS, |i| i.get().max(MIN_THREADS)); - for i in 0..thread_count { - let mut receiver: PriorityQueueReceiver = background_receiver.clone(); - let idle = idle.clone(); - thread::Builder::new() - .name(format!("ThreadedDispatcherWorker-{i}")) - .spawn(move || { - while let Ok(runnable) = receiver.pop() { - let _decrement = idle.decrement_on_drop(); - let location = runnable.metadata().location; - let spawned = runnable.metadata().spawned; - profiler::update_running_task(spawned, location); - runnable.run(); - profiler::save_task_timing(); - } - }) - .expect("failed to spawn threaded dispatcher worker"); - } - drop(background_receiver); - - let timers = Arc::new(TimerQueue { - state: Mutex::new(TimerQueueState { - heap: BinaryHeap::new(), - next_seq: 0, - }), - condvar: Condvar::new(), - }); - { - let timers = timers.clone(); - let idle = idle.clone(); - thread::Builder::new() - .name("ThreadedDispatcherTimer".to_owned()) - .spawn(move || { - let mut state = timers.state.lock(); - loop { - let Some(entry) = state.heap.peek() else { - timers.condvar.wait(&mut state); - continue; - }; - let due = entry.due; - if due > Instant::now() { - timers.condvar.wait_until(&mut state, due); - continue; - } - let Some(entry) = state.heap.pop() else { - continue; - }; - // Count the firing timer as in-flight before releasing - // the lock so it can spawn follow-up work that - // `run_until_idle` will wait for. Lock order is always - // timer state, then in-flight count; `run_until_idle` - // never takes them in the opposite order. - idle.increment(); - drop(state); - - { - let _decrement = idle.decrement_on_drop(); - let location = entry.runnable.metadata().location; - let spawned = entry.runnable.metadata().spawned; - profiler::update_running_task(spawned, location); - entry.runnable.run(); - profiler::save_task_timing(); - } - - state = timers.state.lock(); - } - }) - .expect("failed to spawn threaded dispatcher timer"); - } - - Self { - background_sender, - main_sender, - main_receiver: Mutex::new(main_receiver), - timers, - idle, - main_thread_id: thread::current().id(), - } - } - - /// Runs queued main thread tasks and waits until no background or timer - /// work is queued, running, or already due. - /// - /// Timers that haven't reached their due time yet are *not* waited for: - /// the dispatcher runs in real time and cannot skip ahead like the - /// `TestDispatcher`'s virtual clock, so waiting on a future timer would - /// block for its full real duration. Tasks sleeping on such timers are - /// considered idle. Must be called on the thread that created this - /// dispatcher. - pub fn run_until_idle(&self) { - assert!( - self.is_main_thread(), - "run_until_idle must be called on the threaded dispatcher's main thread" - ); - loop { - if self.drain_main_queue() { - continue; - } - - // Checked before taking the in-flight lock; the timer thread - // locks them in the opposite order, so nesting would deadlock. - if self.has_due_timer() { - // Poll briefly: a firing timer leaves the heap just before it - // registers as in-flight. - let mut inflight = self.idle.inflight.lock(); - self.idle - .condvar - .wait_for(&mut inflight, Duration::from_millis(1)); - continue; - } - - let mut inflight = self.idle.inflight.lock(); - // Re-checked under the lock that `dispatch_on_main_thread` - // notifies under, so the notification can't be lost. - if self.main_queue_has_work() { - continue; - } - if *inflight == 0 { - // Main-thread sends happen before in-flight decrements, and - // decrements happen under this lock, so the check above - // observed all completed work. - return; - } - // Woken when main-thread work arrives or the in-flight count - // reaches zero; both notify under this lock. - self.idle.condvar.wait(&mut inflight); - } - } - - /// Drives main-thread work until `ready` returns a value. - /// - /// Unlike [`Self::run_until_idle`], this waits across temporary quiescence. - /// This is required when completion can arrive from an external worker that - /// is not represented in the dispatcher's in-flight count. - /// - /// Readiness is checked before every main-thread runnable, so this returns - /// as soon as `ready` observes completion rather than after the queue - /// drains — deferred work that re-queues itself (idle sweeps, pollers) - /// must not extend a benchmark's measured interval past the completion it - /// awaits. - #[cfg(any(test, feature = "bench-support"))] - pub(crate) fn run_until(&self, mut ready: impl FnMut() -> Option) -> R { - assert!( - self.is_main_thread(), - "run_until must be called on the threaded dispatcher's main thread" - ); - loop { - if let Some(result) = ready() { - return result; - } - if self.run_one_main_task() { - continue; - } - - let mut inflight = self.idle.inflight.lock(); - if self.main_queue_has_work() { - continue; - } - self.idle.condvar.wait(&mut inflight); - } - } - - /// Runs at most one queued main-thread task, returning whether one ran. - /// - /// [`Self::run_until`] steps tasks one at a time so it can observe - /// readiness between them: a task that perpetually re-queues itself (like - /// an idle-time sweep) would otherwise keep [`Self::drain_main_queue`] - /// looping past the completion the caller is waiting for. - #[cfg(any(test, feature = "bench-support"))] - fn run_one_main_task(&self) -> bool { - let runnable = self.main_receiver.lock().try_pop(); - match runnable { - Ok(Some(runnable)) => { - let location = runnable.metadata().location; - let spawned = runnable.metadata().spawned; - profiler::update_running_task(spawned, location); - runnable.run(); - profiler::save_task_timing(); - true - } - Ok(None) | Err(_) => false, - } - } - - /// Runs the main-thread tasks that were queued when the call began, - /// returning whether any ran. Tasks dispatched while running (e.g. a task - /// re-queuing itself after yielding) are left for the next call, as on - /// the platform run loops. - pub fn run_ready_main_tasks(&self) -> bool { - assert!( - self.is_main_thread(), - "run_ready_main_tasks must be called on the threaded dispatcher's main thread" - ); - let pending = self.main_receiver.lock().len(); - let mut ran_any = false; - for _ in 0..pending { - let runnable = self.main_receiver.lock().try_pop(); - match runnable { - Ok(Some(runnable)) => { - let location = runnable.metadata().location; - let spawned = runnable.metadata().spawned; - profiler::update_running_task(spawned, location); - runnable.run(); - profiler::save_task_timing(); - ran_any = true; - } - Ok(None) | Err(_) => break, - } - } - ran_any - } - - /// Cancels all pending timers so timers armed by one workload can't fire - /// during a later workload sharing this process-lifetime dispatcher. - /// - /// Dropping a timer runnable drops its completion sender, waking the task - /// awaiting the timer. Call [`Self::run_until_idle`] after this method to - /// drain any work that cancellation unblocks. - pub fn cancel_pending_timers(&self) -> usize { - let timers = { - let mut state = self.timers.state.lock(); - let timers: Vec<_> = state.heap.drain().collect(); - self.timers.condvar.notify_all(); - timers - }; - let canceled = timers.len(); - drop(timers); - canceled - } - - /// Describes the dispatcher's idle-tracking state, for diagnosing - /// workloads that fail to reach quiescence. - pub fn debug_state(&self) -> String { - let inflight = *self.idle.inflight.lock(); - let timers = self.timers.state.lock().heap.len(); - let main_queue_has_work = self.main_queue_has_work(); - format!( - "ThreadedDispatcher {{ inflight: {inflight}, pending_timers: {timers}, \ - main_queue_has_work: {main_queue_has_work} }}" - ) - } - - /// Whether no main-thread work is queued, no background or timer - /// runnables are queued or running, and no armed timer is due. Timers - /// that aren't due yet are ignored, as in [`Self::run_until_idle`]. - #[cfg(any(test, feature = "bench-support"))] - pub(crate) fn is_idle(&self) -> bool { - !self.main_queue_has_work() && !self.has_due_timer() && *self.idle.inflight.lock() == 0 - } - - fn has_due_timer(&self) -> bool { - let state = self.timers.state.lock(); - state - .heap - .peek() - .is_some_and(|entry| entry.due <= Instant::now()) - } - - fn main_queue_has_work(&self) -> bool { - !self.main_receiver.lock().is_empty() - } - - fn drain_main_queue(&self) -> bool { - let mut ran_any = false; - loop { - // Lock only around the pop so runnables can re-entrantly dispatch - // more main-thread work through the sender while they run. - let runnable = self.main_receiver.lock().try_pop(); - match runnable { - Ok(Some(runnable)) => { - let location = runnable.metadata().location; - let spawned = runnable.metadata().spawned; - profiler::update_running_task(spawned, location); - runnable.run(); - profiler::save_task_timing(); - ran_any = true; - } - Ok(None) | Err(_) => return ran_any, - } - } - } -} - -impl PlatformDispatcher for ThreadedDispatcher { - fn is_main_thread(&self) -> bool { - thread::current().id() == self.main_thread_id - } - - fn dispatch(&self, runnable: RunnableVariant, priority: Priority) { - self.idle.increment(); - self.background_sender - .send(priority, runnable) - .unwrap_or_else(|_| panic!("threaded dispatcher workers are no longer running")); - } - - fn dispatch_on_main_thread(&self, runnable: RunnableVariant, priority: Priority) { - if let Err(error) = self.main_sender.send(priority, runnable) { - // The main receiver lives as long as this dispatcher, so a failed - // send means we're mid-teardown. The runnable may wrap a !Send - // future, so forget it rather than dropping it on this thread - // (mirrors LinuxDispatcher). - std::mem::forget(error); - return; - } - // Wake `run_until_idle` if it's waiting for main-thread work. - self.idle.notify_under_lock(); - } - - fn dispatch_after(&self, duration: Duration, runnable: RunnableVariant) { - let mut state = self.timers.state.lock(); - let seq = state.next_seq; - state.next_seq += 1; - state.heap.push(TimerEntry { - due: Instant::now() + duration, - seq, - runnable, - }); - self.timers.condvar.notify_one(); - } - - fn spawn_realtime(&self, f: Box) { - // This dispatcher does not need realtime scheduling priority; a plain - // thread keeps it portable. - thread::Builder::new() - .name("ThreadedDispatcherRealtime".to_owned()) - .spawn(f) - .expect("failed to spawn threaded dispatcher realtime thread"); - } - - fn as_threaded(&self) -> Option<&ThreadedDispatcher> { - Some(self) - } -} - -#[cfg(test)] -mod tests { - use std::future::Future; - use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; - - use super::*; - use crate::{BackgroundExecutor, ForegroundExecutor}; - - #[test] - fn is_idle_tracks_queued_work_but_ignores_undue_timers() { - let dispatcher = Arc::new(ThreadedDispatcher::new()); - let foreground = ForegroundExecutor::new(dispatcher.clone()); - assert!(dispatcher.is_idle()); - - foreground.spawn(async {}).detach(); - assert!(!dispatcher.is_idle()); - dispatcher.run_until_idle(); - assert!(dispatcher.is_idle()); - - let background = BackgroundExecutor::new(dispatcher.clone()); - let timer = background.timer(Duration::from_secs(60)); - // The timer future's initial poll runs on a worker thread; wait for - // it so only the armed, not-yet-due timer remains. - dispatcher.run_until_idle(); - assert!( - dispatcher.is_idle(), - "a timer that is not due yet should not count as pending work" - ); - drop(timer); - dispatcher.cancel_pending_timers(); - dispatcher.run_until_idle(); - assert!(dispatcher.is_idle()); - } - - #[test] - fn run_ready_main_tasks_does_not_wait_for_background_handoffs() { - let dispatcher = Arc::new(ThreadedDispatcher::new()); - let background = BackgroundExecutor::new(dispatcher.clone()); - let foreground = ForegroundExecutor::new(dispatcher.clone()); - - let (sender, receiver) = futures::channel::oneshot::channel(); - background - .spawn(async move { - thread::sleep(Duration::from_millis(10)); - sender.send(()).ok(); - }) - .detach(); - - let completed = Arc::new(AtomicBool::new(false)); - foreground - .spawn({ - let completed = completed.clone(); - async move { - receiver.await.ok(); - completed.store(true, Ordering::SeqCst); - } - }) - .detach(); - - assert!(dispatcher.run_ready_main_tasks()); - assert!(!completed.load(Ordering::SeqCst)); - - dispatcher.run_until_idle(); - assert!(completed.load(Ordering::SeqCst)); - } - - #[test] - fn run_until_idle_completes_background_to_main_handoffs() { - let dispatcher = Arc::new(ThreadedDispatcher::new()); - let background = BackgroundExecutor::new(dispatcher.clone()); - let foreground = ForegroundExecutor::new(dispatcher.clone()); - - let (sender, receiver) = futures::channel::oneshot::channel(); - background - .spawn(async move { - thread::sleep(Duration::from_millis(10)); - sender.send(()).ok(); - }) - .detach(); - - let completed = Arc::new(AtomicBool::new(false)); - foreground - .spawn({ - let completed = completed.clone(); - async move { - receiver.await.ok(); - completed.store(true, Ordering::SeqCst); - } - }) - .detach(); - - dispatcher.run_until_idle(); - assert!(completed.load(Ordering::SeqCst)); - } - - #[test] - fn run_until_waits_for_untracked_external_wakes() { - let dispatcher = Arc::new(ThreadedDispatcher::new()); - let foreground = ForegroundExecutor::new(dispatcher.clone()); - let (sender, receiver) = futures::channel::oneshot::channel(); - let sender_thread = thread::spawn(move || { - thread::sleep(Duration::from_millis(10)); - sender - .send(()) - .expect("foreground receiver should remain alive"); - }); - - let completed = Arc::new(AtomicBool::new(false)); - foreground - .spawn({ - let completed = completed.clone(); - async move { - receiver - .await - .expect("external sender should deliver its wake"); - completed.store(true, Ordering::SeqCst); - } - }) - .detach(); - - dispatcher.run_until(|| completed.load(Ordering::SeqCst).then_some(())); - sender_thread.join().expect("sender thread should finish"); - assert!(completed.load(Ordering::SeqCst)); - } - - #[test] - fn run_until_returns_at_readiness_despite_requeuing_main_work() { - const REQUEUE_LIMIT: usize = 10_000; - - let dispatcher = Arc::new(ThreadedDispatcher::new()); - let foreground = ForegroundExecutor::new(dispatcher.clone()); - - // Mirrors main-thread work that yields and immediately re-queues - // itself (e.g. an idle-time sweep): the main queue never drains until - // such work finishes every iteration, so readiness must be observed - // between runnables rather than only at quiescence. - let iterations = Arc::new(AtomicUsize::new(0)); - foreground - .spawn({ - let iterations = iterations.clone(); - async move { - for _ in 0..REQUEUE_LIMIT { - iterations.fetch_add(1, Ordering::SeqCst); - yield_once().await; - } - } - }) - .detach(); - - let completed = Arc::new(AtomicBool::new(false)); - foreground - .spawn({ - let completed = completed.clone(); - async move { - completed.store(true, Ordering::SeqCst); - } - }) - .detach(); - - dispatcher.run_until(|| completed.load(Ordering::SeqCst).then_some(())); - assert!( - iterations.load(Ordering::SeqCst) < REQUEUE_LIMIT, - "run_until should return at readiness instead of draining re-queued main work" - ); - } - - /// Completes after one re-schedule: the poll returns `Pending` and wakes - /// immediately, so the runnable re-enters the main queue. - fn yield_once() -> impl Future { - let mut yielded = false; - std::future::poll_fn(move |poll_context| { - if yielded { - std::task::Poll::Ready(()) - } else { - yielded = true; - poll_context.waker().wake_by_ref(); - std::task::Poll::Pending - } - }) - } - - #[test] - fn run_ready_main_tasks_advances_requeuing_work_one_batch_per_call() { - const REQUEUE_LIMIT: usize = 10_000; - - let dispatcher = Arc::new(ThreadedDispatcher::new()); - let foreground = ForegroundExecutor::new(dispatcher.clone()); - - let iterations = Arc::new(AtomicUsize::new(0)); - foreground - .spawn({ - let iterations = iterations.clone(); - async move { - for _ in 0..REQUEUE_LIMIT { - iterations.fetch_add(1, Ordering::SeqCst); - yield_once().await; - } - } - }) - .detach(); - - assert!(dispatcher.run_ready_main_tasks()); - assert_eq!(iterations.load(Ordering::SeqCst), 1); - assert!(dispatcher.run_ready_main_tasks()); - assert_eq!(iterations.load(Ordering::SeqCst), 2); - } - - #[test] - fn timers_fire_in_real_time() { - let dispatcher = Arc::new(ThreadedDispatcher::new()); - let background = BackgroundExecutor::new(dispatcher); - - let fired = Arc::new(AtomicBool::new(false)); - let timer = background.timer(Duration::from_millis(10)); - background - .spawn({ - let fired = fired.clone(); - async move { - timer.await; - fired.store(true, Ordering::SeqCst); - } - }) - .detach(); - - let deadline = Instant::now() + Duration::from_secs(10); - while !fired.load(Ordering::SeqCst) && Instant::now() < deadline { - thread::sleep(Duration::from_millis(1)); - } - assert!(fired.load(Ordering::SeqCst)); - } - - #[test] - fn cancel_pending_timers_wakes_waiters_without_waiting_for_deadline() { - let dispatcher = Arc::new(ThreadedDispatcher::new()); - let background = BackgroundExecutor::new(dispatcher.clone()); - - let fired = Arc::new(AtomicBool::new(false)); - let timer = background.timer(Duration::from_secs(10)); - background - .spawn({ - let fired = fired.clone(); - async move { - timer.await; - fired.store(true, Ordering::SeqCst); - } - }) - .detach(); - - dispatcher.run_until_idle(); - assert_eq!(dispatcher.cancel_pending_timers(), 1); - dispatcher.run_until_idle(); - - assert!(fired.load(Ordering::SeqCst)); - assert_eq!(dispatcher.cancel_pending_timers(), 0); - } -} diff --git a/crates/gpui_pre/src/platform/visual_test.rs b/crates/gpui_pre/src/platform/visual_test.rs deleted file mode 100644 index 042a757f..00000000 --- a/crates/gpui_pre/src/platform/visual_test.rs +++ /dev/null @@ -1,264 +0,0 @@ -//! Visual test platform that combines real rendering (macOs-only for now) with controllable TestDispatcher. -//! -//! This platform is used for visual tests that need: -//! - Real rendering (e.g. Metal/compositor) for accurate screenshots -//! - Deterministic task scheduling via TestDispatcher -//! - Controllable time via `advance_clock` - -use crate::ScreenCaptureSource; -use crate::{ - AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor, Keymap, - Menu, MenuItem, OwnedMenu, PathPromptOptions, Platform, PlatformDisplay, - PlatformKeyboardLayout, PlatformKeyboardMapper, PlatformTextSystem, PlatformWindow, Task, - TestDispatcher, WindowAppearance, WindowParams, -}; -use anyhow::Result; -use futures::channel::oneshot; -use parking_lot::Mutex; - -use std::{ - path::{Path, PathBuf}, - rc::Rc, - sync::Arc, -}; - -/// A platform that combines real Mac rendering with controllable TestDispatcher. -/// -/// This allows visual tests to: -/// - Render real UI via Metal for accurate screenshots -/// - Control task scheduling deterministically via TestDispatcher -/// - Advance simulated time for testing time-based behaviors (tooltips, animations, etc.) -pub struct VisualTestPlatform { - dispatcher: TestDispatcher, - background_executor: BackgroundExecutor, - foreground_executor: ForegroundExecutor, - platform: Rc, - clipboard: Mutex>, - find_pasteboard: Mutex>, -} - -impl VisualTestPlatform { - /// Creates a new VisualTestPlatform with the given random seed. - /// - /// The seed is used for deterministic random number generation in the TestDispatcher. - pub fn new(platform: Rc, seed: u64) -> Self { - let dispatcher = TestDispatcher::new(seed); - let arc_dispatcher = Arc::new(dispatcher.clone()); - - let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(arc_dispatcher); - - Self { - dispatcher, - background_executor, - foreground_executor, - platform, - clipboard: Mutex::new(None), - find_pasteboard: Mutex::new(None), - } - } - - /// Returns a reference to the TestDispatcher for controlling task scheduling and time. - pub fn dispatcher(&self) -> &TestDispatcher { - &self.dispatcher - } -} - -impl Platform for VisualTestPlatform { - fn background_executor(&self) -> BackgroundExecutor { - self.background_executor.clone() - } - - fn foreground_executor(&self) -> ForegroundExecutor { - self.foreground_executor.clone() - } - - fn text_system(&self) -> Arc { - self.platform.text_system() - } - - fn run(&self, _on_finish_launching: Box) { - panic!("VisualTestPlatform::run should not be called in tests") - } - - fn quit(&self) {} - - fn restart(&self, _binary_path: Option, _arguments: Vec) {} - - fn activate(&self, _ignoring_other_apps: bool) {} - - fn hide(&self) {} - - fn hide_other_apps(&self) {} - - fn unhide_other_apps(&self) {} - - fn displays(&self) -> Vec> { - self.platform.displays() - } - - fn primary_display(&self) -> Option> { - self.platform.primary_display() - } - - fn active_window(&self) -> Option { - self.platform.active_window() - } - - fn window_stack(&self) -> Option> { - self.platform.window_stack() - } - - fn is_screen_capture_supported(&self) -> bool { - self.platform.is_screen_capture_supported() - } - - fn screen_capture_sources( - &self, - ) -> oneshot::Receiver>>> { - self.platform.screen_capture_sources() - } - - fn open_window( - &self, - handle: AnyWindowHandle, - options: WindowParams, - ) -> Result> { - self.platform.open_window(handle, options) - } - - fn window_appearance(&self) -> WindowAppearance { - self.platform.window_appearance() - } - - fn open_url(&self, url: &str) { - self.platform.open_url(url) - } - - fn on_open_urls(&self, _callback: Box)>) {} - - fn register_url_scheme(&self, _url: &str) -> Task> { - Task::ready(Ok(())) - } - - fn prompt_for_paths( - &self, - _options: PathPromptOptions, - ) -> oneshot::Receiver>>> { - let (tx, rx) = oneshot::channel(); - tx.send(Ok(None)).ok(); - rx - } - - fn prompt_for_new_path( - &self, - _directory: &Path, - _suggested_name: Option<&str>, - ) -> oneshot::Receiver>> { - let (tx, rx) = oneshot::channel(); - tx.send(Ok(None)).ok(); - rx - } - - fn can_select_mixed_files_and_dirs(&self) -> bool { - true - } - - fn reveal_path(&self, path: &Path) { - self.platform.reveal_path(path) - } - - fn open_with_system(&self, path: &Path) { - self.platform.open_with_system(path) - } - - fn on_quit(&self, _callback: Box bool>) {} - - fn on_reopen(&self, _callback: Box) {} - - fn on_system_wake(&self, _callback: Box) {} - - fn set_menus(&self, _menus: Vec, _keymap: &Keymap) {} - - fn get_menus(&self) -> Option> { - None - } - - fn set_dock_menu(&self, _menu: Vec, _keymap: &Keymap) {} - - fn on_app_menu_action(&self, _callback: Box) {} - - fn on_will_open_app_menu(&self, _callback: Box) {} - - fn on_validate_app_menu_command(&self, _callback: Box bool>) {} - - fn app_path(&self) -> Result { - self.platform.app_path() - } - - fn path_for_auxiliary_executable(&self, name: &str) -> Result { - self.platform.path_for_auxiliary_executable(name) - } - - fn set_cursor_style(&self, style: CursorStyle) { - self.platform.set_cursor_style(style) - } - - fn hide_cursor_until_mouse_moves(&self) { - self.platform.hide_cursor_until_mouse_moves(); - } - - fn is_cursor_visible(&self) -> bool { - self.platform.is_cursor_visible() - } - - fn should_auto_hide_scrollbars(&self) -> bool { - self.platform.should_auto_hide_scrollbars() - } - - fn read_from_clipboard(&self) -> Option { - self.clipboard.lock().clone() - } - - fn write_to_clipboard(&self, item: ClipboardItem) { - *self.clipboard.lock() = Some(item); - } - - #[cfg(target_os = "macos")] - fn read_from_find_pasteboard(&self) -> Option { - self.find_pasteboard.lock().clone() - } - - #[cfg(target_os = "macos")] - fn write_to_find_pasteboard(&self, item: ClipboardItem) { - *self.find_pasteboard.lock() = Some(item); - } - - fn write_credentials(&self, _url: &str, _username: &str, _password: &[u8]) -> Task> { - Task::ready(Ok(())) - } - - fn read_credentials(&self, _url: &str) -> Task)>>> { - Task::ready(Ok(None)) - } - - fn delete_credentials(&self, _url: &str) -> Task> { - Task::ready(Ok(())) - } - - fn keyboard_layout(&self) -> Box { - self.platform.keyboard_layout() - } - - fn keyboard_mapper(&self) -> Rc { - self.platform.keyboard_mapper() - } - - fn on_keyboard_layout_change(&self, _callback: Box) {} - - fn thermal_state(&self) -> super::ThermalState { - super::ThermalState::Nominal - } - - fn on_thermal_state_change(&self, _callback: Box) {} -} diff --git a/crates/gpui_pre/src/platform_scheduler.rs b/crates/gpui_pre/src/platform_scheduler.rs deleted file mode 100644 index a555a5a3..00000000 --- a/crates/gpui_pre/src/platform_scheduler.rs +++ /dev/null @@ -1,457 +0,0 @@ -use crate::{PlatformDispatcher, RunnableMeta}; -use async_task::Runnable; -use chrono::{DateTime, Utc}; -use futures::channel::oneshot; -use scheduler::Instant; -use scheduler::{ - Clock, LocalExecutor, Priority, Scheduler, SessionId, Task, TestScheduler, Timer, - spawn_dedicated_thread, -}; -#[cfg(not(target_family = "wasm"))] -use std::task::{Context, Poll}; -use std::{ - any::Any, - future::Future, - pin::Pin, - sync::{ - Arc, - atomic::{AtomicU16, Ordering}, - }, - time::Duration, -}; - -/// A production implementation of [`Scheduler`] that wraps a [`PlatformDispatcher`]. -/// -/// This allows GPUI to use the scheduler crate's executor types with the platform's -/// native dispatch mechanisms (e.g., Grand Central Dispatch on macOS). -pub struct PlatformScheduler { - dispatcher: Arc, - clock: Arc, - next_session_id: AtomicU16, - #[cfg(feature = "profiler")] - foreground_runnables: crate::profiler::journal::ForegroundRunnableCounter, -} - -impl PlatformScheduler { - pub fn new(dispatcher: Arc) -> Self { - Self { - dispatcher: dispatcher.clone(), - clock: Arc::new(PlatformClock { dispatcher }), - next_session_id: AtomicU16::new(0), - #[cfg(feature = "profiler")] - foreground_runnables: crate::profiler::journal::foreground_runnable_counter(), - } - } - - pub fn foreground_executor(self: &Arc) -> LocalExecutor { - let session_id = self.next_session_id(); - let scheduler = Arc::downgrade(self); - LocalExecutor::new(session_id, self.clone(), move |runnable| { - if let Some(scheduler) = scheduler.upgrade() { - scheduler.schedule_local(session_id, runnable); - } - }) - } - - fn next_session_id(&self) -> SessionId { - SessionId::new(self.next_session_id.fetch_add(1, Ordering::SeqCst)) - } - - #[cfg(feature = "profiler")] - pub(crate) fn foreground_runnable_counter( - &self, - ) -> crate::profiler::journal::ForegroundRunnableCounter { - self.foreground_runnables.clone() - } -} - -impl Scheduler for PlatformScheduler { - #[cfg(not(target_family = "wasm"))] - fn block( - &self, - _session_id: Option, - mut future: Pin<&mut dyn Future>, - timeout: Option, - ) -> bool { - use waker_fn::waker_fn; - let deadline = timeout.map(|t| Instant::now() + t); - let parker = parking::Parker::new(); - let unparker = parker.unparker(); - let waker = waker_fn(move || { - unparker.unpark(); - }); - let mut cx = Context::from_waker(&waker); - if let Poll::Ready(()) = future.as_mut().poll(&mut cx) { - return true; - } - - let park_deadline = |deadline: Instant| { - // Timer expirations are only delivered every ~15.6 milliseconds by default on Windows. - // We increase the resolution during this wait so that short timeouts stay reasonably short. - let _timer_guard = self.dispatcher.increase_timer_resolution(); - parker.park_deadline(deadline) - }; - - loop { - match deadline { - Some(deadline) if !park_deadline(deadline) && deadline <= Instant::now() => { - return false; - } - Some(_) => (), - None => parker.park(), - } - if let Poll::Ready(()) = future.as_mut().poll(&mut cx) { - break true; - } - } - } - - fn schedule_local(&self, _session_id: SessionId, runnable: Runnable) { - #[cfg(feature = "profiler")] - self.foreground_runnables.queued(); - self.dispatcher - .dispatch_on_main_thread(runnable, Priority::default()); - } - - fn schedule_background_with_priority( - &self, - runnable: Runnable, - priority: Priority, - ) { - self.dispatcher.dispatch(runnable, priority); - } - - fn spawn_realtime(&self, f: Box) { - self.dispatcher.spawn_realtime(f); - } - - #[track_caller] - fn timer(&self, duration: Duration) -> Timer { - let (tx, rx) = oneshot::channel(); - let dispatcher = self.dispatcher.clone(); - - // Create a runnable that will send the completion signal - let location = std::panic::Location::caller(); - let (runnable, _task) = async_task::Builder::new() - .metadata(RunnableMeta { - location, - spawned: scheduler::SpawnTime(Instant::now()), - }) - .spawn( - move |_| async move { - let _ = tx.send(()); - }, - move |runnable| { - dispatcher.dispatch_after(duration, runnable); - }, - ); - runnable.schedule(); - - Timer::new(rx) - } - - fn clock(&self) -> Arc { - self.clock.clone() - } - - fn spawn_dedicated( - self: Arc, - f: Box< - dyn FnOnce( - LocalExecutor, - ) - -> Pin> + 'static>> - + Send - + 'static, - >, - ) -> Task> { - let session_id = self.next_session_id(); - spawn_dedicated_thread(session_id, self, move |executor| f(executor)) - } - - fn as_test(&self) -> Option<&TestScheduler> { - None - } -} - -/// A production clock that uses the platform dispatcher's time. -struct PlatformClock { - dispatcher: Arc, -} - -impl Clock for PlatformClock { - fn utc_now(&self) -> DateTime { - Utc::now() - } - - fn now(&self) -> Instant { - self.dispatcher.now() - } -} - -#[cfg(all(test, not(target_family = "wasm")))] -mod tests { - use super::*; - use crate::RunnableVariant; - use scheduler::BackgroundExecutor; - use std::time::Instant as StdInstant; - - // `spawn_dedicated` shouldn't touch the platform dispatcher at all; - // panicking on every method ensures the test catches it if it does. - struct SmokeDispatcher; - - impl PlatformDispatcher for SmokeDispatcher { - fn is_main_thread(&self) -> bool { - false - } - fn dispatch(&self, _runnable: RunnableVariant, _priority: Priority) { - panic!("SmokeDispatcher should not be asked to dispatch in this test"); - } - fn dispatch_on_main_thread(&self, _runnable: RunnableVariant, _priority: Priority) { - panic!("SmokeDispatcher does not implement a main thread"); - } - fn dispatch_after(&self, _duration: Duration, _runnable: RunnableVariant) { - panic!("SmokeDispatcher does not implement timers"); - } - fn spawn_realtime(&self, _f: Box) { - panic!("SmokeDispatcher does not implement realtime"); - } - } - - #[test] - fn dedicated_executor_tasks_share_one_thread() { - let background = - BackgroundExecutor::new(Arc::new(PlatformScheduler::new(Arc::new(SmokeDispatcher)))); - let dedicated = scheduler::DedicatedExecutor::new(&background); - - let first = dedicated.spawn(async { std::thread::current().id() }); - let second = dedicated.spawn(async { std::thread::current().id() }); - - let first = futures::executor::block_on(first); - let second = futures::executor::block_on(second); - - assert_eq!(first, second, "tasks must share the dedicated thread"); - assert_ne!( - first, - std::thread::current().id(), - "dedicated tasks must not run on the spawning thread" - ); - } - - #[test] - fn spawn_dedicated_runs_on_a_real_separate_thread() { - let background = - BackgroundExecutor::new(Arc::new(PlatformScheduler::new(Arc::new(SmokeDispatcher)))); - let started = StdInstant::now(); - let task = background.spawn_dedicated(|_executor| async move { - // A genuine blocking syscall on the dedicated thread. If - // `spawn_dedicated` were running the future on any shared - // executor, this would stall that executor. - let thread_id_before = std::thread::current().id(); - std::thread::sleep(Duration::from_millis(50)); - let thread_id_after = std::thread::current().id(); - assert_eq!(thread_id_before, thread_id_after); - (thread_id_before, "slept") - }); - let (dedicated_thread_id, message) = futures::executor::block_on(task); - let elapsed = started.elapsed(); - assert_eq!(message, "slept"); - assert_ne!( - dedicated_thread_id, - std::thread::current().id(), - "dedicated future ran on the test thread" - ); - assert!( - elapsed >= Duration::from_millis(40), - "expected the dedicated thread to genuinely sleep, elapsed = {:?}", - elapsed - ); - } - - #[test] - fn spawn_dedicated_returns_not_send_future_output() { - // The whole point of `spawn_dedicated` is that the future can be - // `!Send`. Constructing one with `Rc>` ensures the - // signature actually permits it. - use std::cell::RefCell; - use std::rc::Rc; - - let background = - BackgroundExecutor::new(Arc::new(PlatformScheduler::new(Arc::new(SmokeDispatcher)))); - let task = background.spawn_dedicated(|_executor| async move { - let state = Rc::new(RefCell::new(0_i32)); - for _ in 0..3 { - *state.borrow_mut() += 1; - } - *state.borrow() - }); - let output = futures::executor::block_on(task); - assert_eq!(output, 3); - } - - #[test] - fn spawn_dedicated_dropping_task_cancels_future() { - use parking_lot::Mutex; - use std::sync::mpsc; - - let background = - BackgroundExecutor::new(Arc::new(PlatformScheduler::new(Arc::new(SmokeDispatcher)))); - - let (started_tx, started_rx) = mpsc::channel::<()>(); - let (after_park_tx, after_park_rx) = mpsc::channel::<()>(); - let observed_post_await_write = Arc::new(Mutex::new(false)); - - let task = { - let observed_post_await_write = observed_post_await_write.clone(); - background.spawn_dedicated(move |_executor| async move { - // Announce that the future is live on the dedicated thread. - started_tx - .send(()) - .expect("started signal must be received"); - // Park forever. Dropping the `Task` must cancel us here so - // the code below this `await` never runs. - futures::future::pending::<()>().await; - *observed_post_await_write.lock() = true; - after_park_tx - .send(()) - .expect("after-park signal must be received"); - }) - }; - - // Wait until the dedicated future is actually parked at the await. - started_rx - .recv_timeout(Duration::from_secs(2)) - .expect("dedicated future failed to start"); - - // Drop the root Task: this must cancel the future. - drop(task); - - // If cancellation works, the future never advances past `pending`, - // so this recv must time out. - assert!( - after_park_rx - .recv_timeout(Duration::from_millis(100)) - .is_err(), - "dedicated future advanced past the await after its Task was dropped" - ); - assert!( - !*observed_post_await_write.lock(), - "dedicated future ran code past the cancellation point" - ); - } - - #[test] - fn spawn_dedicated_thread_tears_down_after_work_completes() { - use std::sync::mpsc; - - // Fires from `Drop` so we observe teardown of the dedicated future's - // captured state on whichever thread runs its destructor. - struct DropSignal { - tx: Option>, - } - impl Drop for DropSignal { - fn drop(&mut self) { - if let Some(tx) = self.tx.take() { - let _ = tx.send(std::thread::current().id()); - } - } - } - - let background = - BackgroundExecutor::new(Arc::new(PlatformScheduler::new(Arc::new(SmokeDispatcher)))); - let (started_tx, started_rx) = mpsc::channel::(); - let (drop_tx, drop_rx) = mpsc::channel::(); - - let task = background.spawn_dedicated(move |_executor| async move { - // Captured by the future's state. When the future completes and - // its state is dropped on the dedicated thread, this guard's - // `Drop` fires and reports the thread id it ran on. - let _guard = DropSignal { tx: Some(drop_tx) }; - started_tx - .send(std::thread::current().id()) - .expect("started signal must be received"); - // Future returns immediately. The dedicated thread should then - // drop the future (firing _guard), exit the recv loop, and exit. - }); - - let dedicated_thread_id = started_rx - .recv_timeout(Duration::from_secs(2)) - .expect("dedicated future failed to start"); - assert_ne!( - dedicated_thread_id, - std::thread::current().id(), - "dedicated future ran on the test thread" - ); - - // Drive the root task to completion so its body finishes. - futures::executor::block_on(task); - - // The guard's drop runs from the dedicated thread as it tears down - // the future's captured state. If the executor/recv-loop were - // keeping the future alive past task completion, this would hang. - let drop_thread_id = drop_rx - .recv_timeout(Duration::from_secs(2)) - .expect("dedicated future's captured state was not dropped after task completion"); - assert_eq!( - drop_thread_id, dedicated_thread_id, - "dedicated future's captured state must be dropped on the dedicated thread, not elsewhere" - ); - } - - #[test] - fn spawn_dedicated_detached_child_outlives_root() { - use std::sync::mpsc; - - let background = - BackgroundExecutor::new(Arc::new(PlatformScheduler::new(Arc::new(SmokeDispatcher)))); - - // `gate_rx` lets the detached child park until the test explicitly - // releases it — after we've already observed the root completing. - let (gate_tx, gate_rx) = mpsc::channel::<()>(); - let (child_done_tx, child_done_rx) = mpsc::channel::(); - - let task = background.spawn_dedicated(move |executor| async move { - executor - .spawn(async move { - // Blocking on `recv` is normally wrong inside an - // executor, but the dedicated thread is exclusive to - // this session, so blocking the only future on it is - // fine — this is the property `spawn_dedicated` is - // designed to provide. - gate_rx - .recv() - .expect("gate sender dropped before child resumed"); - child_done_tx - .send(std::thread::current().id()) - .expect("child_done receiver dropped"); - }) - .detach(); - // Root finishes here. The detached child must keep the - // dedicated thread alive until it completes. - }); - - futures::executor::block_on(task); - - // Negative assertion: the child has not finished, because the gate - // hasn't been released yet. - assert!( - child_done_rx - .recv_timeout(Duration::from_millis(50)) - .is_err(), - "detached child finished before being released" - ); - - // Release the gate. The detached child should now complete on the - // dedicated thread. - gate_tx.send(()).expect("gate receiver dropped"); - - let child_thread_id = child_done_rx - .recv_timeout(Duration::from_secs(2)) - .expect("detached child failed to complete after gate was released"); - assert_ne!( - child_thread_id, - std::thread::current().id(), - "detached child ran on the test thread instead of the dedicated thread" - ); - } -} diff --git a/crates/gpui_pre/src/prelude.rs b/crates/gpui_pre/src/prelude.rs deleted file mode 100644 index b5185a25..00000000 --- a/crates/gpui_pre/src/prelude.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! The GPUI prelude is a collection of traits and types that are widely used -//! throughout the library. It is recommended to import this prelude into your -//! application to avoid having to import each trait individually. - -pub use crate::{ - AppContext as _, BorrowAppContext, Context, Element, InteractiveElement, IntoElement, - ParentElement, Refineable, Render, RenderOnce, StatefulInteractiveElement, Styled, StyledImage, - TaskExt as _, VisualContext, util::FluentBuilder, -}; diff --git a/crates/gpui_pre/src/profiler.rs b/crates/gpui_pre/src/profiler.rs deleted file mode 100644 index 340f054b..00000000 --- a/crates/gpui_pre/src/profiler.rs +++ /dev/null @@ -1,1582 +0,0 @@ -#[cfg(feature = "profiler")] -use hdrhistogram::Histogram; -use itertools::Itertools; -use scheduler::{Instant, SpawnTime}; -#[cfg(feature = "profiler")] -use smallvec::SmallVec; -use std::{ - cell::LazyCell, - collections::{HashMap, VecDeque}, - hash::{DefaultHasher, Hash, Hasher}, - sync::{ - Arc, - atomic::{AtomicU64, Ordering}, - }, - thread::ThreadId, - time::Duration, -}; - -mod actions; -#[cfg(feature = "profiler")] -pub mod hang; -#[cfg(feature = "profiler")] -pub mod journal; -pub use actions::{ActionStatistics, ActionTiming, take_action_stats}; - -use serde::{Deserialize, Serialize}; - -#[cfg(feature = "profiler")] -use crate::{Action, App, WindowId}; -use crate::{SharedString, TasksIncluded}; - -#[cfg(feature = "profiler")] -#[doc(hidden)] -pub fn get_all_timings(included: gpui::TasksIncluded) -> Vec { - ThreadTaskTimings::collect(upgraded_thread_timings(), included) -} - -#[cfg(feature = "profiler")] -#[doc(hidden)] -pub fn get_current_thread_timings(included: TasksIncluded) -> gpui::ThreadTaskTimings { - gpui::profiler::get_current_thread_task_timings(included) -} - -#[cfg(feature = "profiler")] -#[doc(hidden)] -pub fn take_all_stats(included: TasksIncluded) -> Vec { - ThreadTaskStatistics::collect_and_reset(upgraded_thread_timings(), included) -} - -#[cfg(not(feature = "profiler"))] -#[doc(hidden)] -pub fn get_all_timings(_included: gpui::TasksIncluded) -> Vec { - Vec::new() -} -#[cfg(not(feature = "profiler"))] -#[doc(hidden)] -pub fn get_current_thread_timings(_included: TasksIncluded) -> gpui::ThreadTaskTimings { - gpui::ThreadTaskTimings { - thread_name: None, - thread_id: std::thread::current().id(), - timings: Vec::new(), - stats: TaskStatistics::default(), - total_pushed: 0, - } -} -#[cfg(not(feature = "profiler"))] -#[doc(hidden)] -pub fn take_all_stats(_included: TasksIncluded) -> Vec { - Vec::new() -} - -#[doc(hidden)] -#[derive(Debug, Copy, Clone)] -pub struct YieldTime(pub Instant); - -#[doc(hidden)] -#[derive(Copy, Clone)] -pub struct TaskTiming { - pub location: &'static core::panic::Location<'static>, - pub spawned: SpawnTime, - pub start: Instant, - pub end: YieldTime, -} - -impl std::fmt::Debug for TaskTiming { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("TaskTiming") - .field("location", &self.location) - .field("since_spawned", &self.spawned.0.elapsed()) - .field("last_poll_duration", &self.poll_duration()) - .field("total_runtime", &self.since_spawn()) - .finish() - } -} - -#[doc(hidden)] -#[derive(Debug, Copy, Clone)] -pub struct ActiveTiming { - pub location: &'static core::panic::Location<'static>, - pub spawned: SpawnTime, - pub start: Instant, -} - -impl TaskTiming { - /// A task timing with a duration of zero. Any task will replace this in history. - pub fn placeholder() -> Self { - let now = Instant::now(); - Self { - location: std::panic::Location::caller(), - spawned: SpawnTime(now), - start: now, - end: YieldTime(now), - } - } - - #[inline(always)] - pub fn poll_duration(&self) -> Duration { - self.end.0 - self.start - } - - #[inline(always)] - fn since_spawn(&self) -> Duration { - self.end.0 - self.spawned.0 - } -} - -#[doc(hidden)] -#[derive(Debug, Clone)] -pub struct ThreadTaskTimings { - pub thread_name: Option, - pub thread_id: ThreadId, - pub timings: Vec, - pub stats: TaskStatistics, - pub total_pushed: u64, -} - -impl ThreadTaskTimings { - /// Convert upgraded per-thread timings into their structured format. - pub fn collect( - timings: Vec<(ThreadId, Arc)>, - included: TasksIncluded, - ) -> Vec { - timings - .into_iter() - .map(|(thread_id, timings)| { - let timings = timings.lock(); - let thread_name = timings.thread_name.clone(); - let total_pushed = timings.total_pushed; - let completed = &timings.timings; - - let mut vec = Vec::with_capacity(completed.len() + 1); // +1 for running task - let (s1, s2) = completed.as_slices(); - vec.extend_from_slice(s1); - vec.extend_from_slice(s2); - if let TasksIncluded::CompletedAndRunning = included - && let Some(running) = timings.running - { - vec.push(TaskTiming { - location: running.location, - spawned: running.spawned, - start: running.start, - end: YieldTime(Instant::now()), - }) - } - - ThreadTaskTimings { - thread_name, - thread_id, - timings: vec, - stats: timings.stats.clone(), - total_pushed, - } - }) - .collect() - } -} - -#[doc(hidden)] -#[derive(Debug)] -pub struct ThreadTaskStatistics { - pub thread_name: Option, - pub thread_id: ThreadId, - pub stats: TaskStatistics, -} - -impl ThreadTaskStatistics { - pub fn collect_and_reset( - timings: Vec<(ThreadId, Arc)>, - include_running: TasksIncluded, - ) -> Vec { - timings - .into_iter() - .map(|(thread_id, timings)| { - let mut timings = timings.lock(); - let thread_name = timings.thread_name.clone(); - - let mut stats = std::mem::take(&mut timings.stats); - if let TasksIncluded::CompletedAndRunning = include_running - && let Some(ActiveTiming { - location, - spawned, - start, - }) = timings.running - { - let end = YieldTime(Instant::now()); - let timing = TaskTiming { - location, - spawned, - start, - end, - }; - stats.add_runtime(timing); - stats.add_yield_timing(timing); - } - - Self { - thread_name, - thread_id, - stats, - } - }) - .collect() - } -} - -/// Serializable variant of [`core::panic::Location`] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SerializedLocation { - /// Name of the source file - pub file: SharedString, - /// Line in the source file - pub line: u32, - /// Column in the source file - pub column: u32, -} - -impl From<&core::panic::Location<'static>> for SerializedLocation { - fn from(value: &core::panic::Location<'static>) -> Self { - SerializedLocation { - file: value.file().into(), - line: value.line(), - column: value.column(), - } - } -} - -/// Serializable variant of [`TaskTiming`] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SerializedTaskTiming { - /// Location of the timing - pub location: SerializedLocation, - /// Time at which the measurement was reported in nanoseconds - pub start: u128, - /// Duration of the measurement in nanoseconds - pub duration: u128, -} - -impl SerializedTaskTiming { - /// Convert an array of [`TaskTiming`] into their serializable format - /// - /// # Params - /// - /// `anchor` - [`Instant`] that should be earlier than all timings to use as base anchor - pub fn convert(anchor: Instant, timings: &[TaskTiming]) -> Vec { - let serialized = timings - .iter() - .map(|timing| { - let start = timing.start.duration_since(anchor).as_nanos(); - let duration = timing.end.0.duration_since(timing.start).as_nanos(); - SerializedTaskTiming { - location: timing.location.into(), - start, - duration, - } - }) - .collect::>(); - - serialized - } - - /// `anchor` - [`Instant`] that should be earlier than all timings to use as base anchor - pub fn from(anchor: Instant, timing: TaskTiming) -> SerializedTaskTiming { - let start = timing.start.duration_since(anchor).as_nanos(); - let duration = timing.end.0.duration_since(timing.start).as_nanos(); - SerializedTaskTiming { - location: timing.location.into(), - start, - duration, - } - } -} - -/// Serializable variant of [`ThreadTaskTimings`] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SerializedThreadTaskTimings { - /// Thread name - pub thread_name: Option, - /// Hash of the thread id - pub thread_id: u64, - /// Timing records for this thread - pub timings: Vec, -} - -impl SerializedThreadTaskTimings { - /// Convert [`ThreadTaskTimings`] into their serializable format - /// - /// # Params - /// - /// `anchor` - [`Instant`] that should be earlier than all timings to use as base anchor - pub fn convert(anchor: Instant, timings: ThreadTaskTimings) -> SerializedThreadTaskTimings { - let serialized_timings = SerializedTaskTiming::convert(anchor, &timings.timings); - - let mut hasher = DefaultHasher::new(); - timings.thread_id.hash(&mut hasher); - let thread_id = hasher.finish(); - - SerializedThreadTaskTimings { - thread_name: timings.thread_name, - thread_id, - timings: serialized_timings, - } - } -} - -#[doc(hidden)] -#[derive(Debug, Clone)] -pub struct ThreadTimingsDelta { - /// Hashed thread id - pub thread_id: u64, - /// Thread name, if known - pub thread_name: Option, - /// New timings since the last call. If the circular buffer wrapped around - /// since the previous poll, some entries may have been lost. - pub new_timings: Vec, -} - -/// Tracks which timing events have already been seen so that callers can request only unseen events. -#[doc(hidden)] -pub struct ProfilingCollector { - startup_time: Instant, - cursors: HashMap, -} - -impl ProfilingCollector { - pub fn new(startup_time: Instant) -> Self { - Self { - startup_time, - cursors: HashMap::default(), - } - } - - pub fn startup_time(&self) -> Instant { - self.startup_time - } - - pub fn collect_unseen( - &mut self, - all_timings: Vec, - ) -> Vec { - let mut deltas = Vec::with_capacity(all_timings.len()); - - for thread in all_timings { - let mut hasher = DefaultHasher::new(); - thread.thread_id.hash(&mut hasher); - let hashed_id = hasher.finish(); - - let prev_cursor = self.cursors.get(&thread.thread_id).copied().unwrap_or(0); - let buffer_len = thread.timings.len() as u64; - let buffer_start = thread.total_pushed.saturating_sub(buffer_len); - - let mut slice = if prev_cursor < buffer_start { - // Cursor fell behind the buffer — some entries were evicted. - // Return everything still in the buffer. - thread.timings.as_slice() - } else { - let skip = (prev_cursor - buffer_start) as usize; - &thread.timings[skip.min(thread.timings.len())..] - }; - - let cursor_advance = thread.total_pushed; - self.cursors.insert(thread.thread_id, cursor_advance); - - if slice.is_empty() { - continue; - } - - let new_timings = SerializedTaskTiming::convert(self.startup_time, slice); - - deltas.push(ThreadTimingsDelta { - thread_id: hashed_id, - thread_name: thread.thread_name, - new_timings, - }); - } - - deltas - } - - pub fn reset(&mut self) { - self.cursors.clear(); - } -} - -// Allow 16MiB of task timing entries. -// VecDeque grows by doubling its capacity when full, so keep this a power of 2 to avoid wasting -// memory. -#[cfg(feature = "profiler")] -const MAX_TASK_TIMINGS: usize = (16 * 1024 * 1024) / core::mem::size_of::(); - -#[doc(hidden)] -pub(crate) type TaskTimings = VecDeque; - -#[doc(hidden)] -pub type GuardedTaskTimings = spin::Mutex; - -#[doc(hidden)] -pub struct GlobalThreadTimings { - pub thread_id: ThreadId, - pub timings: std::sync::Weak, -} - -#[doc(hidden)] -#[derive(Debug, Clone)] -pub struct TaskStatistics { - pub poll_time_to_beat: Duration, - pub runtime_to_beat: Duration, - pub longest_poll_times: [TaskTiming; 5], - pub longest_runtimes: [TaskTiming; 5], -} - -impl std::fmt::Display for TaskStatistics { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("Tasks that blocked the longest before yielding\n")?; - for timing in self.longest_poll_times { - f.write_fmt(format_args!( - "{:<20} - {}:{}\n", - format!("{:?}", timing.poll_duration()), - timing.location.file(), - timing.location.column() - ))?; - } - f.write_str("Tasks that ran the longest\n")?; - for timing in self.longest_runtimes { - f.write_fmt(format_args!( - "{:<20} - {}:{}\n", - format!("{:?}", timing.since_spawn()), - timing.location.file(), - timing.location.column() - ))?; - } - Ok(()) - } -} - -impl Default for TaskStatistics { - fn default() -> Self { - Self { - // Do not track polls that are not problematic - // this keeps more calls on the fast path - poll_time_to_beat: Duration::from_micros(100), - runtime_to_beat: Duration::from_micros(100), - longest_poll_times: [TaskTiming::placeholder(); 5], - longest_runtimes: [TaskTiming::placeholder(); 5], - } - } -} - -impl TaskStatistics { - #[inline(always)] - fn add_yield_timing(&mut self, task: TaskTiming) { - let yielded_after = task.poll_duration(); - if yielded_after >= self.poll_time_to_beat { - std::hint::cold_path(); // most tasks are not the worst, optimize for that - let to_replace = self - .longest_poll_times - .iter() - .position_min_by_key(|task| task.since_spawn()) - .expect("guarded by the comparison with nth_longest_yield_time"); - self.longest_poll_times[to_replace] = task; - - self.poll_time_to_beat = self - .longest_poll_times - .iter() - .map(|task| task.since_spawn()) - .min() - .expect("never empty"); - } - } - - #[inline(always)] - fn add_runtime(&mut self, task: TaskTiming) { - let runtime = task.since_spawn(); - if runtime >= self.runtime_to_beat { - std::hint::cold_path(); // most tasks are not the worst, optimize for that - let to_replace = self - .longest_runtimes - .iter() - .position_min_by_key(|task| task.since_spawn()) - .expect("guarded by the comparison with nth_longest_yield_time"); - self.longest_runtimes[to_replace] = task; - - self.runtime_to_beat = self - .longest_runtimes - .iter() - .map(|task| task.since_spawn()) - .min() - .expect("never empty"); - } - } -} - -#[doc(hidden)] -pub static GLOBAL_THREAD_TIMINGS: spin::Mutex> = - spin::Mutex::new(Vec::new()); - -/// Upgrades all live per-thread timing handles, holding the global registry -/// lock only for the duration of the upgrades. -/// -/// The upgraded `Arc`s must never be dropped while `GLOBAL_THREAD_TIMINGS` is -/// locked: dropping the last strong reference runs [`ThreadTimings::drop`], -/// which locks `GLOBAL_THREAD_TIMINGS` again and would deadlock the -/// non-reentrant spinlock. A thread exiting concurrently can hand off its last -/// reference to us at any time, so callers of this function process (lock, -/// read, drop) the returned handles only after the global lock is released. -fn upgraded_thread_timings() -> Vec<(ThreadId, Arc)> { - let global_thread_timings = GLOBAL_THREAD_TIMINGS.lock(); - global_thread_timings - .iter() - .filter_map(|t| Some((t.thread_id, t.timings.upgrade()?))) - .collect() -} - -thread_local! { - #[doc(hidden)] - pub static THREAD_TIMINGS: LazyCell> = LazyCell::new(|| { - let current_thread = std::thread::current(); - let thread_name = current_thread.name(); - let thread_id = current_thread.id(); - let timings = ThreadTimings::new(thread_name.map(|e| e.to_string()), thread_id); - let timings = Arc::new(spin::Mutex::new(timings)); - - { - let timings = Arc::downgrade(&timings); - let global_timings = GlobalThreadTimings { - thread_id: std::thread::current().id(), - timings, - }; - GLOBAL_THREAD_TIMINGS.lock().push(global_timings); - } - - timings - }); -} - -#[doc(hidden)] -pub struct ThreadTimings { - pub thread_name: Option, - pub thread_id: ThreadId, - pub timings: TaskTimings, - pub running: Option, - pub stats: TaskStatistics, - pub total_pushed: u64, -} - -impl ThreadTimings { - pub fn new(thread_name: Option, thread_id: ThreadId) -> Self { - ThreadTimings { - thread_name, - thread_id, - timings: TaskTimings::new(), - stats: TaskStatistics::default(), - total_pushed: 0, - running: None, - } - } - - #[cfg(feature = "profiler")] - pub fn update_running_task( - &mut self, - spawned: SpawnTime, - location: &'static std::panic::Location<'_>, - ) { - let start = Instant::now(); - self.running = Some(ActiveTiming { - spawned, - location, - start, - }); - } - #[cfg(not(feature = "profiler"))] - pub fn update_running_task(&mut self, _: SpawnTime, _: &'static std::panic::Location<'_>) {} - - #[cfg(feature = "profiler")] - pub fn save_task_timing(&mut self, ended: YieldTime) -> TaskTiming { - let ActiveTiming { - location, - start, - spawned, - } = self - .running - .take() - .expect("this function is only ever called after register_task_start"); - - let timing = TaskTiming { - location, - spawned, - start, - end: ended, - }; - self.stats.add_yield_timing(timing); - self.stats.add_runtime(timing); - - if trace_enabled() { - std::hint::cold_path(); // optimize for when the profiling is off - if self.timings.len() >= MAX_TASK_TIMINGS { - self.timings.pop_front(); - } - self.timings.push_back(timing); - self.total_pushed += 1; - } - timing - } - #[cfg(not(feature = "profiler"))] - pub fn save_task_timing(&mut self, _: YieldTime) {} - - // Running tasks are included in the reliability trace, which is written - // whenever the foreground executor makes no progress for > n seconds - pub fn get_thread_task_timings(&self, includes: TasksIncluded) -> ThreadTaskTimings { - ThreadTaskTimings { - thread_name: self.thread_name.clone(), - thread_id: self.thread_id, - timings: self - .timings - .iter() - .cloned() - .chain( - self.running - .filter(|_| matches!(includes, TasksIncluded::CompletedAndRunning)) - .map(|running| TaskTiming { - spawned: running.spawned, - location: running.location, - start: running.start, - end: YieldTime(Instant::now()), - }), - ) - .collect(), - stats: self.stats.clone(), - total_pushed: self.total_pushed, - } - } -} - -impl Drop for ThreadTimings { - fn drop(&mut self) { - let mut thread_timings = GLOBAL_THREAD_TIMINGS.lock(); - - let Some((index, _)) = thread_timings - .iter() - .enumerate() - .find(|(_, t)| t.thread_id == self.thread_id) - else { - return; - }; - thread_timings.swap_remove(index); - } -} - -#[doc(hidden)] -pub fn update_running_task(spawned: SpawnTime, location: &'static std::panic::Location<'_>) { - #[cfg(feature = "profiler")] - journal::begin_foreground_turn(); - THREAD_TIMINGS.with(|timings| { - timings.lock().update_running_task(spawned, location); - }); -} - -#[doc(hidden)] -pub fn save_task_timing() { - let yielded_at = YieldTime(Instant::now()); - #[cfg(feature = "profiler")] - { - let timing = THREAD_TIMINGS.with(|timings| timings.lock().save_task_timing(yielded_at)); - journal::record_task_poll(timing); - } - #[cfg(not(feature = "profiler"))] - THREAD_TIMINGS.with(|timings| { - timings.lock().save_task_timing(yielded_at); - }); -} - -#[doc(hidden)] -pub fn get_current_thread_task_timings(include_running: TasksIncluded) -> ThreadTaskTimings { - THREAD_TIMINGS.with(|timings| timings.lock().get_thread_task_timings(include_running)) -} - -const TRACE_SETTING_ENABLED: u64 = 1 << 63; -const TRACE_SCOPE_COUNT_MASK: u64 = TRACE_SETTING_ENABLED - 1; -static TRACE_STATE: AtomicU64 = AtomicU64::new(0); - -/// Enables or disables profiler trace collection at runtime. -/// -/// When transitioning from enabled to disabled, `add_task_timing` becomes -/// cheaper since only cheap statistics are gathered. The existing per-thread -/// task buffers and the frame-event buffer are cleared so stale data isn't -/// reported after a later re-enable. Active trace scopes keep collection enabled -/// until the last scope ends. Calls with the current setting are a no-op. -pub fn set_trace_enabled(enabled: bool) -> bool { - let mut state = TRACE_STATE.load(Ordering::Acquire); - loop { - let was_enabled = state & TRACE_SETTING_ENABLED != 0; - if was_enabled == enabled { - return false; - } - - let next_state = if enabled { - state | TRACE_SETTING_ENABLED - } else { - state & TRACE_SCOPE_COUNT_MASK - }; - match TRACE_STATE.compare_exchange_weak( - state, - next_state, - Ordering::AcqRel, - Ordering::Acquire, - ) { - Ok(_) => { - if next_state == 0 { - clear_trace_buffers(); - } - return true; - } - Err(updated_state) => state = updated_state, - } - } -} - -#[cfg(any(feature = "bench-support", all(test, feature = "profiler")))] -pub(crate) struct TraceGuard; - -#[cfg(any(feature = "bench-support", all(test, feature = "profiler")))] -pub(crate) fn trace_scope() -> TraceGuard { - let incremented = TRACE_STATE.fetch_update(Ordering::AcqRel, Ordering::Acquire, |state| { - (state & TRACE_SCOPE_COUNT_MASK < TRACE_SCOPE_COUNT_MASK).then_some(state + 1) - }); - assert!(incremented.is_ok(), "too many active profiler trace scopes"); - TraceGuard -} - -#[cfg(any(feature = "bench-support", all(test, feature = "profiler")))] -impl Drop for TraceGuard { - fn drop(&mut self) { - let previous_state = - TRACE_STATE.fetch_update(Ordering::AcqRel, Ordering::Acquire, |state| { - (state & TRACE_SCOPE_COUNT_MASK > 0).then_some(state - 1) - }); - match previous_state { - Ok(1) => clear_trace_buffers(), - Ok(_) => {} - Err(_) => debug_assert!(false, "profiler trace scope count underflowed"), - } - } -} - -/// Returns whether profiler trace collection is enabled. -pub fn trace_enabled() -> bool { - TRACE_STATE.load(Ordering::Relaxed) != 0 -} - -fn clear_trace_buffers() { - for (_, timings) in upgraded_thread_timings() { - let mut timings = timings.lock(); - timings.timings.clear(); - timings.timings.shrink_to_fit(); - timings.total_pushed = 0; - } - #[cfg(feature = "profiler")] - { - let mut frames = FRAME_TIMINGS.lock(); - frames.timings.clear(); - frames.timings.shrink_to_fit(); - frames.total_pushed = 0; - } -} - -/// Timing for a single drawn window frame. -#[cfg(feature = "profiler")] -#[derive(Debug, Copy, Clone)] -pub struct FrameTiming { - /// The window that was drawn. - pub window_id: WindowId, - /// When the frame first became dirty (its first invalidation). `None` if - /// profiler tracing was not yet enabled when the invalidation occurred. - pub dirty_at: Option, - /// Number of invalidations coalesced into this frame. - pub invalidations: u64, - /// When `Window::draw` started. - pub draw_start: Instant, - /// When `Window::draw` finished. - pub draw_end: Instant, -} - -#[cfg(feature = "profiler")] -impl FrameTiming { - /// Time spent inside `Window::draw`. - pub fn draw_duration(&self) -> Duration { - self.draw_end.duration_since(self.draw_start) - } - - /// Time from the frame's first invalidation to the end of its draw, if the - /// first invalidation was observed. - pub fn dirty_to_draw_duration(&self) -> Option { - self.dirty_at - .map(|dirty_at| self.draw_end.duration_since(dirty_at)) - } -} - -/// Work spent submitting a window frame to the platform. -#[cfg(feature = "profiler")] -#[derive(Debug, Copy, Clone)] -pub struct PresentTiming { - /// The window whose frame was submitted. - pub window_id: WindowId, - /// When the platform submission began. - pub present_start: Instant, - /// When the platform submission completed. - pub present_end: Instant, - /// The interval since the previous newly drawn frame was submitted, when - /// both frames belong to an active animation. - pub animation_interval: Option, -} - -#[cfg(feature = "profiler")] -impl PresentTiming { - /// Time spent submitting the frame to the platform. - pub fn present_duration(&self) -> Duration { - self.present_end.duration_since(self.present_start) - } -} - -/// A frame event observed by the profiler. -#[cfg(feature = "profiler")] -#[derive(Debug, Copy, Clone)] -pub enum FrameEvent { - /// A window frame was drawn. - Draw(FrameTiming), - /// A newly drawn window frame was presented. - Present(PresentTiming), -} - -/// A point-in-time snapshot of the frame-duration histograms for a window, -/// suitable for external formatting. -#[cfg(feature = "profiler")] -#[derive(Clone)] -pub struct FrameDurationSnapshot { - /// Histogram of durations from the first invalidation through presentation, in nanoseconds. - pub dirty_to_present_histogram: Histogram, - /// Histogram of `Window::draw` durations, in nanoseconds. - pub draw_duration_histogram: Histogram, - /// Histogram of intervals between consecutively presented frames while the - /// window was animating, in nanoseconds. - pub present_interval_histogram: Histogram, -} - -/// A point-in-time snapshot of the input-latency histograms for a window, -/// suitable for external formatting. -#[cfg(feature = "profiler")] -#[derive(Clone)] -pub struct InputLatencySnapshot { - /// Histogram of input-to-frame latency samples, in nanoseconds. - pub latency_histogram: Histogram, - /// Histogram of input events coalesced per rendered frame. - pub events_per_frame_histogram: Histogram, - /// Count of input events that arrived mid-draw and were excluded from - /// latency recording. - pub mid_draw_events_dropped: u64, -} - -#[cfg(feature = "profiler")] -enum WindowActivity { - Input { - started_at: Instant, - kind: &'static str, - }, - Draw { - started_at: Instant, - }, -} - -/// Collects profiling information for one window. -/// -/// Aggregate histograms are always populated when the `profiler` feature is -/// compiled in. Individual draw and present events are added to the global -/// profiler buffer only while tracing is enabled. -#[cfg(feature = "profiler")] -pub struct WindowProfiler { - window_id: WindowId, - active_activities: SmallVec<[WindowActivity; 4]>, - active_actions: SmallVec<[(&'static str, Instant); 2]>, - dirty_to_present_histogram: Histogram, - draw_duration_histogram: Histogram, - present_interval_histogram: Histogram, - first_input_at: Option, - pending_input_count: u64, - input_latency_histogram: Histogram, - events_per_frame_histogram: Histogram, - mid_draw_events_dropped: u64, - last_present_at: Option, - animating_at_last_present: bool, - pending_frame: Option, -} - -#[cfg(feature = "profiler")] -impl WindowProfiler { - /// Creates a profiler for a window. - pub fn new(window_id: WindowId) -> anyhow::Result { - let profiler = Self { - window_id, - active_activities: SmallVec::new(), - active_actions: SmallVec::new(), - dirty_to_present_histogram: Histogram::new(3).map_err(|error| { - anyhow::anyhow!("Failed to create dirty-to-present histogram: {error}") - })?, - draw_duration_histogram: Histogram::new(3).map_err(|error| { - anyhow::anyhow!("Failed to create draw duration histogram: {error}") - })?, - present_interval_histogram: Histogram::new(3).map_err(|error| { - anyhow::anyhow!("Failed to create present interval histogram: {error}") - })?, - first_input_at: None, - pending_input_count: 0, - input_latency_histogram: Histogram::new(3).map_err(|error| { - anyhow::anyhow!("Failed to create input latency histogram: {error}") - })?, - events_per_frame_histogram: Histogram::new(3).map_err(|error| { - anyhow::anyhow!("Failed to create events per frame histogram: {error}") - })?, - mid_draw_events_dropped: 0, - last_present_at: None, - animating_at_last_present: false, - pending_frame: None, - }; - journal::record_frame_pending(window_id, Instant::now()); - Ok(profiler) - } - - /// Records the beginning of an input dispatch. `kind` names the platform - /// input variant being dispatched (see [`crate::PlatformInput::kind_name`]). - pub fn begin_input(&mut self, kind: &'static str) { - journal::begin_foreground_turn(); - self.active_activities.push(WindowActivity::Input { - started_at: Instant::now(), - kind, - }); - } - - /// Records the end of an input dispatch. - pub fn end_input(&mut self, caused_invalidation: bool) { - let Some(WindowActivity::Input { started_at, kind }) = self.active_activities.pop() else { - debug_assert!(false, "input activity must be the current window activity"); - journal::end_foreground_turn(); - return; - }; - - journal::record_input(journal::InputTiming { - kind, - start: started_at, - end: Instant::now(), - caused_invalidation, - }); - journal::end_foreground_turn(); - - if !caused_invalidation { - return; - } - - let arrived_during_draw = self - .active_activities - .iter() - .any(|activity| matches!(activity, WindowActivity::Draw { .. })); - if arrived_during_draw { - self.mid_draw_events_dropped += 1; - } else { - self.first_input_at.get_or_insert(started_at); - self.pending_input_count += 1; - } - } - - /// Records the beginning of an action handler. - pub fn begin_action_handler(&mut self, action: &(dyn Action + 'static), cx: &mut App) { - journal::begin_foreground_turn(); - let name = actions::update_running_action(action, cx); - self.active_actions.push((name, Instant::now())); - } - - /// Records the end of the current action handler. - pub fn end_action_handler(&mut self) { - // Dual-write to the legacy aggregate store; its single global running - // slot misbehaves when tests run actions concurrently, which is why - // the journal entry is tracked here on the window instead. - actions::save_action_timing(); - let Some((name, start)) = self.active_actions.pop() else { - debug_assert!(false, "action handler must be begun before it ends"); - journal::end_foreground_turn(); - return; - }; - journal::record_action(ActionTiming { - name, - start, - end: Instant::now(), - }); - journal::end_foreground_turn(); - } - - /// Records the beginning of a window draw. - pub fn begin_draw(&mut self) { - journal::begin_foreground_turn(); - let started_at = Instant::now(); - journal::record_frame_pending(self.window_id, started_at); - self.active_activities - .push(WindowActivity::Draw { started_at }); - } - - /// Records the end of a window draw and returns the draw duration. - pub fn end_draw(&mut self, dirty_at: Option, invalidations: u64) -> Duration { - let Some(WindowActivity::Draw { - started_at: draw_start, - }) = self.active_activities.pop() - else { - debug_assert!(false, "draw activity must be the current window activity"); - journal::end_foreground_turn(); - return Duration::ZERO; - }; - - let draw_end = Instant::now(); - let frame_timing = FrameTiming { - window_id: self.window_id, - dirty_at, - invalidations, - draw_start, - draw_end, - }; - let draw_duration = frame_timing.draw_duration(); - self.record_draw_timing(frame_timing); - journal::end_foreground_turn(); - draw_duration - } - - /// Records that a frame was presented. - /// - /// `next_frame_scheduled` marks the animation state for the interval ending - /// at the next newly drawn frame's presentation. - pub fn record_present( - &mut self, - present_start: Instant, - present_end: Instant, - window_active: bool, - next_frame_scheduled: bool, - ) { - self.record_present_at( - present_start, - present_end, - window_active, - next_frame_scheduled, - ); - } - - /// Returns a snapshot of the current input-latency histograms. - pub fn input_latency_snapshot(&self) -> InputLatencySnapshot { - InputLatencySnapshot { - latency_histogram: self.input_latency_histogram.clone(), - events_per_frame_histogram: self.events_per_frame_histogram.clone(), - mid_draw_events_dropped: self.mid_draw_events_dropped, - } - } - - /// Returns a snapshot of the current frame-duration histograms. - pub fn frame_duration_snapshot(&self) -> FrameDurationSnapshot { - FrameDurationSnapshot { - dirty_to_present_histogram: self.dirty_to_present_histogram.clone(), - draw_duration_histogram: self.draw_duration_histogram.clone(), - present_interval_histogram: self.present_interval_histogram.clone(), - } - } - - fn record_present_at( - &mut self, - present_start: Instant, - present_end: Instant, - window_active: bool, - next_frame_scheduled: bool, - ) { - if let Some(first_input_at) = self.first_input_at.take() { - let latency_nanos = present_end.duration_since(first_input_at).as_nanos() as u64; - self.input_latency_histogram.record(latency_nanos).ok(); - } - if self.pending_input_count > 0 { - self.events_per_frame_histogram - .record(self.pending_input_count) - .ok(); - self.pending_input_count = 0; - } - - let frame = self.pending_frame.take(); - let animation_interval = - if frame.is_some() && self.animating_at_last_present && window_active { - self.last_present_at - .map(|last_present_at| present_end.duration_since(last_present_at)) - } else { - None - }; - let present_timing = PresentTiming { - window_id: self.window_id, - present_start, - present_end, - animation_interval, - }; - journal::record_present(present_timing, frame); - - let Some(frame) = frame else { - return; - }; - - if let Some(dirty_at) = frame.dirty_at - && let Err(error) = self - .dirty_to_present_histogram - .record(present_end.duration_since(dirty_at).as_nanos() as u64) - { - log::error!("failed to record dirty-to-present frame timing: {error}"); - } - - if let Some(animation_interval) = animation_interval { - self.present_interval_histogram - .record(animation_interval.as_nanos() as u64) - .ok(); - } - record_frame_event(FrameEvent::Present(present_timing)); - - self.last_present_at = Some(present_end); - self.animating_at_last_present = next_frame_scheduled && window_active; - } - - fn record_draw_timing(&mut self, timing: FrameTiming) { - self.record_draw_duration(timing.draw_duration()); - self.pending_frame = Some(timing); - record_frame_event(FrameEvent::Draw(timing)); - journal::record_draw(timing); - } - - fn record_draw_duration(&mut self, duration: Duration) { - self.draw_duration_histogram - .record(duration.as_nanos() as u64) - .ok(); - } -} - -#[cfg(feature = "profiler")] -impl Drop for WindowProfiler { - fn drop(&mut self) { - journal::record_window_closed(self.window_id); - } -} - -// Allow 16MiB of frame event entries. -#[cfg(feature = "profiler")] -const MAX_FRAME_TIMINGS: usize = (16 * 1024 * 1024) / core::mem::size_of::(); - -#[cfg(feature = "profiler")] -struct FrameTimings { - timings: VecDeque, - total_pushed: u64, -} - -#[cfg(feature = "profiler")] -static FRAME_TIMINGS: spin::Mutex = spin::Mutex::new(FrameTimings { - timings: VecDeque::new(), - total_pushed: 0, -}); - -/// Records a frame event. -/// -/// No-op unless profiler tracing is enabled via [`set_trace_enabled`]. -#[cfg(feature = "profiler")] -pub fn record_frame_event(event: FrameEvent) { - if !trace_enabled() { - return; - } - std::hint::cold_path(); // optimize for when profiling is off - - let mut frames = FRAME_TIMINGS.lock(); - if frames.timings.len() >= MAX_FRAME_TIMINGS { - frames.timings.pop_front(); - } - frames.timings.push_back(event); - frames.total_pushed += 1; -} - -/// Drains frame events recorded after this collector was created, tracking a -/// cursor so each call to [`Self::collect_unseen`] returns only new entries. -#[cfg(feature = "profiler")] -pub struct FrameTimingCollector { - cursor: u64, -} - -#[cfg(feature = "profiler")] -impl Default for FrameTimingCollector { - fn default() -> Self { - Self::new() - } -} - -#[cfg(feature = "profiler")] -impl FrameTimingCollector { - /// Creates a collector that only sees frame events recorded from this point on. - pub fn new() -> Self { - Self { - cursor: FRAME_TIMINGS.lock().total_pushed, - } - } - - /// Returns frame events recorded since the previous call (or since the - /// collector was created). If the ring buffer wrapped around since the - /// previous poll, the evicted entries are lost. - pub fn collect_unseen(&mut self) -> Vec { - let frames = FRAME_TIMINGS.lock(); - let buffer_len = frames.timings.len() as u64; - let buffer_start = frames.total_pushed.saturating_sub(buffer_len); - let skip = self.cursor.saturating_sub(buffer_start) as usize; - let unseen = frames - .timings - .iter() - .skip(skip.min(frames.timings.len())) - .copied() - .collect(); - self.cursor = frames.total_pushed; - unseen - } -} - -#[cfg(all(test, feature = "profiler"))] -mod tests { - use super::*; - use std::sync::{Mutex, MutexGuard}; - - #[test] - fn records_draw_events_only_while_tracing() { - let _trace_test_guard = TraceTestGuard::new(); - let window_id = WindowId::from(0xD0A0); - let mut window_profiler = - WindowProfiler::new(window_id).expect("window profiler should initialize"); - let dirty_at = Instant::now(); - let mut collector = FrameTimingCollector::new(); - - window_profiler.begin_draw(); - window_profiler.end_draw(Some(dirty_at), 3); - assert!( - collector - .collect_unseen() - .iter() - .all(|event| !event_matches_window(*event, window_id)) - ); - - set_trace_enabled(true); - let mut collector = FrameTimingCollector::new(); - window_profiler.begin_draw(); - window_profiler.end_draw(Some(dirty_at), 3); - - let timing = collector - .collect_unseen() - .into_iter() - .find_map(|event| match event { - FrameEvent::Draw(timing) if timing.window_id == window_id => Some(timing), - _ => None, - }) - .expect("draw event should be recorded while tracing"); - assert_eq!(timing.dirty_at, Some(dirty_at)); - assert_eq!(timing.invalidations, 3); - assert!(timing.draw_start >= dirty_at); - } - - #[test] - fn records_present_events_for_newly_drawn_frames() { - let _trace_test_guard = TraceTestGuard::new(); - set_trace_enabled(true); - let window_id = WindowId::from(0xA11E); - let mut window_profiler = - WindowProfiler::new(window_id).expect("window profiler should initialize"); - let start = Instant::now(); - let mut collector = FrameTimingCollector::new(); - - record_test_draw(&mut window_profiler, start); - window_profiler.record_present_at(start, start, true, true); - record_test_draw(&mut window_profiler, start + FRAME); - window_profiler.record_present_at(start + FRAME, start + FRAME, true, true); - window_profiler.record_present_at( - start + FRAME + FRAME / 2, - start + FRAME + FRAME / 2, - true, - true, - ); - - let present_timings = collector - .collect_unseen() - .into_iter() - .filter_map(|event| match event { - FrameEvent::Present(timing) if timing.window_id == window_id => Some(timing), - _ => None, - }) - .collect::>(); - let [first_present, second_present] = present_timings.as_slice() else { - panic!("expected exactly two present events, got {present_timings:?}"); - }; - assert_eq!(first_present.animation_interval, None); - assert_eq!(second_present.animation_interval, Some(FRAME)); - - #[cfg(feature = "profiler")] - { - assert_eq!(window_profiler.present_interval_histogram.len(), 1); - assert!( - window_profiler.present_interval_histogram.max() - >= second_present - .animation_interval - .expect("second present should have an animation interval") - .as_nanos() as u64 - ); - } - } - - #[test] - fn disabling_tracing_clears_frame_events() { - let _trace_test_guard = TraceTestGuard::new(); - set_trace_enabled(true); - let window_id = WindowId::from(0xC1EA); - let mut window_profiler = - WindowProfiler::new(window_id).expect("window profiler should initialize"); - let mut collector = FrameTimingCollector::new(); - - window_profiler.begin_draw(); - window_profiler.end_draw(None, 0); - assert!( - FRAME_TIMINGS - .lock() - .timings - .iter() - .copied() - .any(|event| event_matches_window(event, window_id)) - ); - - set_trace_enabled(false); - assert!( - collector - .collect_unseen() - .iter() - .all(|event| !event_matches_window(*event, window_id)) - ); - } - - #[cfg(feature = "profiler")] - #[test] - fn records_intervals_only_between_animation_frames() { - let mut window_profiler = - WindowProfiler::new(WindowId::from(1)).expect("window profiler should initialize"); - let start = Instant::now(); - - draw_and_present(&mut window_profiler, start, true, true); - assert_eq!(window_profiler.present_interval_histogram.len(), 0); - - draw_and_present(&mut window_profiler, start + FRAME, true, true); - assert_eq!(window_profiler.present_interval_histogram.len(), 1); - - draw_and_present(&mut window_profiler, start + FRAME * 2, true, false); - assert_eq!(window_profiler.present_interval_histogram.len(), 2); - - draw_and_present(&mut window_profiler, start + FRAME * 100, true, true); - assert_eq!(window_profiler.present_interval_histogram.len(), 2); - } - - #[cfg(feature = "profiler")] - #[test] - fn missed_frames_stretch_the_recorded_interval() { - let mut window_profiler = - WindowProfiler::new(WindowId::from(2)).expect("window profiler should initialize"); - let start = Instant::now(); - - draw_and_present(&mut window_profiler, start, true, true); - draw_and_present(&mut window_profiler, start + FRAME * 5, true, true); - - let recorded = window_profiler.present_interval_histogram.max(); - assert!(recorded >= (FRAME * 4).as_nanos() as u64); - } - - #[cfg(feature = "profiler")] - #[test] - fn ignores_re_presents_of_unchanged_frames() { - let mut window_profiler = - WindowProfiler::new(WindowId::from(3)).expect("window profiler should initialize"); - let start = Instant::now(); - - draw_and_present(&mut window_profiler, start, true, true); - window_profiler.record_present_at(start + FRAME / 2, start + FRAME / 2, true, true); - draw_and_present(&mut window_profiler, start + FRAME, true, true); - - assert_eq!(window_profiler.present_interval_histogram.len(), 1); - assert!( - window_profiler.present_interval_histogram.max() >= (FRAME * 3 / 4).as_nanos() as u64 - ); - } - - #[cfg(feature = "profiler")] - #[test] - fn skips_intervals_for_inactive_windows() { - let mut window_profiler = - WindowProfiler::new(WindowId::from(4)).expect("window profiler should initialize"); - let start = Instant::now(); - - draw_and_present(&mut window_profiler, start, false, true); - draw_and_present(&mut window_profiler, start + FRAME, false, true); - assert_eq!(window_profiler.present_interval_histogram.len(), 0); - - draw_and_present(&mut window_profiler, start + FRAME * 2, true, true); - assert_eq!(window_profiler.present_interval_histogram.len(), 0); - - draw_and_present(&mut window_profiler, start + FRAME * 3, true, true); - assert_eq!(window_profiler.present_interval_histogram.len(), 1); - } - - #[test] - fn records_dirty_to_present_durations() { - let mut window_profiler = - WindowProfiler::new(WindowId::from(8)).expect("window profiler should initialize"); - let draw_end = Instant::now(); - let present_end = draw_end + Duration::from_millis(6); - - record_test_draw(&mut window_profiler, draw_end); - window_profiler.record_present_at(present_end, present_end, true, false); - - let snapshot = window_profiler.frame_duration_snapshot(); - let histogram = snapshot.dirty_to_present_histogram; - assert_eq!(histogram.len(), 1); - assert!(histogram.max() >= Duration::from_millis(10).as_nanos() as u64); - } - - #[cfg(feature = "profiler")] - #[test] - fn records_every_draw_duration() { - let mut window_profiler = - WindowProfiler::new(WindowId::from(5)).expect("window profiler should initialize"); - - window_profiler.record_draw_duration(Duration::from_millis(2)); - window_profiler.record_draw_duration(Duration::from_millis(40)); - - let snapshot = window_profiler.frame_duration_snapshot(); - assert_eq!(snapshot.draw_duration_histogram.len(), 2); - assert!(snapshot.draw_duration_histogram.max() >= 39_000_000); - } - - #[test] - fn records_input_latency_at_the_frame_presentation_timestamp() { - let mut window_profiler = - WindowProfiler::new(WindowId::from(6)).expect("window profiler should initialize"); - let first_input_at = Instant::now(); - let presented_at = first_input_at + Duration::from_millis(12); - - begin_input_at(&mut window_profiler, first_input_at); - window_profiler.end_input(true); - begin_input_at( - &mut window_profiler, - first_input_at + Duration::from_millis(2), - ); - window_profiler.end_input(true); - record_test_draw(&mut window_profiler, presented_at); - window_profiler.record_present_at(presented_at, presented_at, true, false); - - let snapshot = window_profiler.input_latency_snapshot(); - assert_eq!(snapshot.latency_histogram.len(), 1); - assert!(snapshot.latency_histogram.max() >= Duration::from_millis(12).as_nanos() as u64); - assert_eq!(snapshot.events_per_frame_histogram.len(), 1); - assert_eq!(snapshot.events_per_frame_histogram.max(), 2); - assert_eq!(snapshot.mid_draw_events_dropped, 0); - } - - #[test] - fn excludes_input_that_arrives_during_a_draw() { - let mut window_profiler = - WindowProfiler::new(WindowId::from(7)).expect("window profiler should initialize"); - - window_profiler.begin_draw(); - begin_input_at(&mut window_profiler, Instant::now()); - window_profiler.end_input(true); - window_profiler.end_draw(None, 0); - - let snapshot = window_profiler.input_latency_snapshot(); - assert!(snapshot.latency_histogram.is_empty()); - assert!(snapshot.events_per_frame_histogram.is_empty()); - assert_eq!(snapshot.mid_draw_events_dropped, 1); - } - - #[test] - fn overlapping_trace_scopes_keep_tracing_enabled() { - let _trace_test_guard = TraceTestGuard::new(); - let first_scope = trace_scope(); - let second_scope = trace_scope(); - - assert!(trace_enabled()); - drop(first_scope); - assert!(trace_enabled()); - drop(second_scope); - assert!(!trace_enabled()); - } - - const FRAME: Duration = Duration::from_millis(16); - static TRACE_TEST_LOCK: Mutex<()> = Mutex::new(()); - - struct TraceTestGuard { - was_enabled: bool, - _lock: MutexGuard<'static, ()>, - } - - impl TraceTestGuard { - fn new() -> Self { - let lock = TRACE_TEST_LOCK - .lock() - .unwrap_or_else(|poisoned| poisoned.into_inner()); - let was_enabled = trace_enabled(); - set_trace_enabled(false); - Self { - was_enabled, - _lock: lock, - } - } - } - - impl Drop for TraceTestGuard { - fn drop(&mut self) { - set_trace_enabled(false); - if self.was_enabled { - set_trace_enabled(true); - } - } - } - - fn event_matches_window(event: FrameEvent, window_id: WindowId) -> bool { - match event { - FrameEvent::Draw(timing) => timing.window_id == window_id, - FrameEvent::Present(timing) => timing.window_id == window_id, - } - } - - fn begin_input_at(window_profiler: &mut WindowProfiler, started_at: Instant) { - window_profiler - .active_activities - .push(WindowActivity::Input { - started_at, - kind: "test", - }); - } - - #[cfg(feature = "profiler")] - fn draw_and_present( - window_profiler: &mut WindowProfiler, - presented_at: Instant, - window_active: bool, - next_frame_scheduled: bool, - ) { - record_test_draw(window_profiler, presented_at); - window_profiler.record_present_at( - presented_at, - presented_at, - window_active, - next_frame_scheduled, - ); - } - - fn record_test_draw(window_profiler: &mut WindowProfiler, draw_end: Instant) { - window_profiler.record_draw_timing(FrameTiming { - window_id: window_profiler.window_id, - dirty_at: Some(draw_end - Duration::from_millis(4)), - invalidations: 1, - draw_start: draw_end - Duration::from_millis(2), - draw_end, - }); - } -} diff --git a/crates/gpui_pre/src/profiler/actions.rs b/crates/gpui_pre/src/profiler/actions.rs deleted file mode 100644 index a4892f8a..00000000 --- a/crates/gpui_pre/src/profiler/actions.rs +++ /dev/null @@ -1,213 +0,0 @@ -use std::time::Duration; - -use itertools::Itertools; -use scheduler::Instant; - -#[cfg(feature = "profiler")] -use crate::action::Action; - -#[doc(hidden)] -#[derive(Clone)] -pub struct ActionStatistics { - runtime_to_beat: Duration, - - longest_runtimes: heapless::Vec, - running: Option<(&'static str, Instant)>, -} - -impl std::fmt::Debug for ActionStatistics { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ActionStatistics") - .field("runtime_to_beat", &self.runtime_to_beat) - .field("longest_runtimes", &self.longest_runtimes) - .field( - "running", - &self.running.map(|(id, started)| (id, started.elapsed())), - ) - .finish() - } -} - -impl std::fmt::Display for ActionStatistics { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("Actions that blocked the longest\n")?; - for action in self - .longest_runtimes(true) - .sorted_by_key(|action| action.runtime()) - .rev() - { - f.write_fmt(format_args!( - "{:<20} - {}", - format!("{:?}", action.runtime()), // impl dbg does not support alignment - action.name - ))?; - writeln!(f)?; - } - Ok(()) - } -} - -impl Default for ActionStatistics { - fn default() -> Self { - Self::new() - } -} - -impl ActionStatistics { - const fn new() -> Self { - Self { - // This keeps more calls on the fast path by only tracking - // problematic polls - runtime_to_beat: Duration::from_micros(100), - longest_runtimes: heapless::Vec::new(), - running: None, - } - } - - pub fn take(&mut self) -> Self { - let taken = std::mem::take(self); - self.running = taken.running; - taken - } - - pub fn is_empty(&self) -> bool { - self.longest_runtimes.is_empty() - } - - #[cfg(feature = "profiler")] - pub fn update_running_action(&mut self, action: &'static str, started: Instant) { - self.running = Some((action, started)); - } - #[cfg(not(feature = "profiler"))] - pub fn update_running_action(&mut self, _action: &'static str, _started: Instant) {} - - #[cfg(feature = "profiler")] - pub fn save_action_timing(&mut self) { - let now = Instant::now(); - - let Some((action, started)) = self.running.take() else { - // Actions are ran only on the foreground executor and therefore - // sequentially _except_ in tests where they can run concurrently. - // - // When ran sequentially self.running will always be Some. When ran - // concurrently that is no longer true. But that is fine, we do not - // need to track action timings in tests. - std::hint::cold_path(); - return; - }; - - let timing = ActionTiming { - name: action, - start: started, - end: now, - }; - - let runtime = now.duration_since(started); - if runtime >= self.runtime_to_beat { - std::hint::cold_path(); // most actions are not the worst, optimize for that - - if self.longest_runtimes.is_full() - && let Some(to_replace) = self - .longest_runtimes - .iter_mut() - .min_by_key(|action| runtime >= action.runtime()) - { - *to_replace = timing; - } else { - self.longest_runtimes - .push(timing) - .expect("just checked it is not full"); - }; - - self.runtime_to_beat = self - .longest_runtimes - .iter() - .map(|action| action.runtime()) - .min() - .expect("never empty"); - } - } - #[cfg(not(feature = "profiler"))] - pub fn save_action_timing(&mut self) {} - - pub fn longest_runtimes(&self, include_running: bool) -> impl Iterator { - self.longest_runtimes.iter().copied().chain( - self.running - .into_iter() - .filter(move |_| include_running) - .map(|(name, start)| ActionTiming { - name, - start, - end: Instant::now(), - }), - ) - } -} - -#[doc(hidden)] -/// UNSTABLE only for use in the profiler and zed-reliability -#[derive(Copy, Clone)] -pub struct ActionTiming { - pub name: &'static str, - pub start: Instant, - pub end: Instant, -} - -impl core::fmt::Debug for ActionTiming { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ActionTiming") - .field("name", &self.name) - .field("runtime", &self.runtime()) - .finish() - } -} - -impl ActionTiming { - pub fn duration(&self) -> Duration { - self.end.saturating_duration_since(self.start) - } -} - -impl ActionTiming { - #[doc(hidden)] - pub fn runtime(&self) -> Duration { - self.end - self.start - } -} - -// The profiler is careful to never block when the lock is held, therefore a -// spinlock is optimal. -#[cfg(feature = "profiler")] -static ACTION_STATISTICS: spin::Mutex = - const { spin::Mutex::new(ActionStatistics::new()) }; - -#[doc(hidden)] -#[cfg(feature = "profiler")] -pub(crate) fn update_running_action( - action: &(dyn Action + 'static), - cx: &mut crate::App, -) -> &'static str { - let now = Instant::now(); - let action = action.type_id(); - let action = cx.actions.try_resolve_action(&action).unwrap_or("un-named"); - ACTION_STATISTICS.lock().update_running_action(action, now); - action -} - -#[doc(hidden)] -#[cfg(feature = "profiler")] -pub(crate) fn save_action_timing() { - ACTION_STATISTICS.lock().save_action_timing(); -} - -#[doc(hidden)] -#[cfg(feature = "profiler")] -pub fn take_action_stats() -> ActionStatistics { - ACTION_STATISTICS.lock().take() -} - -#[doc(hidden)] -#[cfg(not(feature = "profiler"))] -pub fn take_action_stats() -> ActionStatistics { - ActionStatistics::default() -} diff --git a/crates/gpui_pre/src/profiler/hang.rs b/crates/gpui_pre/src/profiler/hang.rs deleted file mode 100644 index 7aea645b..00000000 --- a/crates/gpui_pre/src/profiler/hang.rs +++ /dev/null @@ -1,1297 +0,0 @@ -//! Post-hoc detection of foreground hangs from the journal's event stream. -//! -//! A hang is any single piece of foreground work — a task poll, an action -//! handler, an input dispatch, a window draw, or platform presentation — that -//! blocked the foreground thread for at least a threshold duration. An -//! interval whose total foreground spend reached the frame budget also -//! counts, even when no single event crossed the threshold: many small -//! pieces of work can drop a frame — or starve a headless app — as -//! thoroughly as one long stall. [`HangDetector`] drains the journal and -//! reports each completed activity interval containing hangs as a -//! [`HangIncident`]. - -use std::time::Duration; - -use scheduler::Instant; -use serde::Serialize; - -use super::SerializedLocation; -use super::journal::{ - ForegroundEvent, ForegroundJournal, ForegroundJournalCollector, ForegroundJournalEntry, - FrameSnapshot, IntervalBoundary, IntervalSealer, -}; - -/// Detects foreground hangs by polling the journal. -/// -/// Detection is post-hoc: a hang is reported once an explicit presentation -/// or foreground-idle boundary completes its interval. Work that never -/// yields back to the foreground is not observed until it does. -pub struct HangDetector { - collector: ForegroundJournalCollector, - sealer: IntervalSealer, - threshold: Duration, - frame_budget: Duration, - first_present_at: Option, -} - -/// One sealed interval that contained at least one hang. -#[derive(Debug, Clone)] -pub struct HangIncident { - /// The interval the hangs occurred in, including all non-hang foreground - /// work recorded alongside them. - pub snapshot: FrameSnapshot, - /// Which detection rule qualified the interval. - pub trigger: HangTrigger, - /// For [`HangTrigger::Threshold`], the events that blocked the foreground - /// for at least the detector's threshold, longest first. For - /// [`HangTrigger::Budget`], no event crossed the threshold and this instead - /// holds every event in the interval, longest first. - pub contributors: Vec, -} - -/// The detection rule that qualified an interval as a [`HangIncident`]. -/// -/// Recorded explicitly so consumers can separate the two classes without -/// re-deriving them from `stall_ms`, which stops working whenever the -/// detector's thresholds change. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum HangTrigger { - /// A single event blocked the foreground for at least the hang threshold. - Threshold, - /// No single event crossed the threshold, but the interval's total - /// foreground spend reached the frame budget. - Budget, -} - -impl HangDetector { - /// Creates a detector reporting single events at or above `threshold` - /// and intervals whose total foreground spend reached `frame_budget`. - /// Only events recorded from this point on are observed. - pub fn new(journal: ForegroundJournal, threshold: Duration, frame_budget: Duration) -> Self { - Self { - collector: journal.collector(), - sealer: IntervalSealer::new(Instant::now()), - threshold, - frame_budget, - first_present_at: None, - } - } - - /// When the first newly drawn frame observed by this detector finished - /// platform submission. `None` until a presentation boundary is observed. - pub fn first_present_at(&self) -> Option { - self.first_present_at - } - - /// Drains newly recorded events and returns the incidents sealed since - /// the previous poll. - pub fn poll(&mut self) -> Vec { - let drained = self.collector.collect_unseen(); - if self.first_present_at.is_none() { - self.first_present_at = drained.entries.iter().find_map(|entry| match entry { - ForegroundJournalEntry::Boundary(IntervalBoundary::Presented(presented)) => { - Some(presented.presentation.present_end) - } - _ => None, - }); - } - self.sealer - .push_entries(drained.entries) - .into_iter() - .filter_map(|snapshot| { - HangIncident::detect(snapshot, self.threshold, self.frame_budget) - }) - .collect() - } -} - -/// A [`HangIncident`] in a telemetry-friendly form: timestamps and durations -/// in fractional milliseconds since app startup (microsecond precision), -/// locations as plain data, contributor count capped by the converter. -#[derive(Debug, Clone, Serialize)] -pub struct SerializedHangIncident { - /// `"startup"` when the active window began before the first observed - /// newly drawn frame finished platform submission (see - /// [`HangDetector::first_present_at`]), otherwise `"steady"`. - pub phase: &'static str, - /// `"threshold"` or `"budget"` (see [`HangTrigger`]). - pub trigger: HangTrigger, - /// When the incident's active window started, in milliseconds since app - /// startup: the sealing frame's first invalidation, or the earliest - /// contributor's start when nothing was pending a repaint. Foreground - /// idle time between the previous frame and the cause is excluded. - pub start_ms: f64, - /// Length of the active window in milliseconds: from the cause to the - /// seal. Exceeds `stall_ms` when several stalls piled up on one frame. - pub active_ms: f64, - /// The longest single block of foreground work, in milliseconds: the - /// best estimate of the freeze a user perceived. Below the hang - /// threshold (possibly zero) when the frame budget alone triggered the - /// incident. - pub stall_ms: f64, - /// For presentation-sealed incidents, how long the submitted frame had - /// been dirty, in milliseconds. - pub dirty_to_present_ms: Option, - /// What closed the incident: `"present"` or `"idle"`. This labels the - /// boundary, not the hang's cause — the cause is the first contributor. - pub sealed_by: &'static str, - /// Fraction of the active window the foreground spent working, - /// `0.0..=1.0`. Low values with a high `dirty_to_present_ms` indicate - /// throttling or scheduling delay rather than application work. - pub busy_fraction: f64, - /// Total events recorded in the interval (before the contributor cap). - pub event_count: usize, - /// Count of task polls below the journal's floor. - pub small_poll_count: u64, - /// Total duration of task polls below the journal's floor, in milliseconds. - pub small_poll_total_ms: f64, - /// Events lost to caps or ring overwrites. - pub dropped_events: u64, - /// Whether one or more journal entries were unavailable in the interval. - /// Threshold-qualified contributors remain valid, but cumulative budget - /// conclusions and boundary attribution are not trusted across the gap. - pub journal_discontinuous: bool, - /// The incident's contributors (see [`HangIncident::contributors`]) in - /// start order. The cap keeps the longest ones; `stall_ms` is always - /// among them. - pub contributors: Vec, - /// Contributors elided by the cap. - pub contributors_elided: usize, -} - -/// One hang contributor in serialized form. -/// -/// Foreground work nests: an input dispatch can synchronously draw a window, -/// a draw can poll a task. `depth` expresses that containment — a depth-1 -/// event's time is already inside some depth-0 event's duration, so summing -/// sibling durations across depths double-counts. -#[derive(Debug, Clone, Serialize)] -#[serde(tag = "kind", rename_all = "snake_case")] -pub enum SerializedHangContributor { - /// A foreground task poll. - TaskPoll { - /// Where the task was spawned. - location: SerializedLocation, - /// When the poll started, in milliseconds since app startup. - start_ms: f64, - /// How long the poll blocked the foreground, in milliseconds. - duration_ms: f64, - /// How many other events in the interval contain this one. - depth: usize, - }, - /// An action handler. - Action { - /// The action's name. - name: &'static str, - /// When the handler started, in milliseconds since app startup. - start_ms: f64, - /// How long the handler ran, in milliseconds. - duration_ms: f64, - /// How many other events in the interval contain this one. - depth: usize, - }, - /// A platform input dispatch. - Input { - /// The platform input variant dispatched, e.g. `"key_down"`. - /// Named `input_kind` because `kind` is this enum's serde tag. - input_kind: &'static str, - /// When the dispatch started, in milliseconds since app startup. - start_ms: f64, - /// How long the dispatch ran, in milliseconds. - duration_ms: f64, - /// Whether handling the input invalidated a window. - caused_invalidation: bool, - /// How many other events in the interval contain this one. - depth: usize, - }, - /// A window draw. - Draw { - /// The window that was drawn. - window_id: u64, - /// When the draw started, in milliseconds since app startup. - start_ms: f64, - /// How long the draw took, in milliseconds. - duration_ms: f64, - /// Time from the frame's first invalidation to the end of its draw, - /// in milliseconds. - dirty_to_draw_ms: Option, - /// Invalidations coalesced into the frame. - invalidations: u64, - /// How many other events in the interval contain this one. - depth: usize, - }, - /// Work spent submitting a frame to the platform. - Present { - /// The window whose frame was submitted. - window_id: u64, - /// When submission began, in milliseconds since app startup. - start_ms: f64, - /// How long platform submission took, in milliseconds. - duration_ms: f64, - /// How many other events in the interval contain this one. - depth: usize, - }, -} - -/// Milliseconds with microsecond precision: keeps `dbg!`/JSON output short -/// (`115.954` rather than `115.95400000000001` or `115954`). -fn as_millis(duration: Duration) -> f64 { - duration.as_micros() as f64 / 1000.0 -} - -impl SerializedHangIncident { - /// Converts an incident, keeping at most `max_contributors` contributors. - /// `first_present_at` is the end of the first observed newly drawn frame's - /// platform submission (typically [`HangDetector::first_present_at`]); - /// incidents whose active window begins before it are tagged `"startup"`. - pub fn convert( - startup: Instant, - incident: &HangIncident, - max_contributors: usize, - first_present_at: Option, - ) -> Self { - let since_startup = - |instant: Instant| as_millis(instant.saturating_duration_since(startup)); - let snapshot = &incident.snapshot; - let (active_start, active_end) = incident.active_window(); - let active = active_end.duration_since(active_start); - let busy_fraction = if active.is_zero() { - 1.0 - } else { - snapshot - .occupancy_within(active_start, active_end) - .div_duration_f64(active) - .min(1.0) - }; - Self { - phase: match first_present_at { - Some(first_present_at) if active_start >= first_present_at => "steady", - _ => "startup", - }, - trigger: incident.trigger, - start_ms: since_startup(active_start), - active_ms: as_millis(active), - stall_ms: incident - .contributors - .first() - .map(|event| as_millis(event.duration())) - .unwrap_or(0.0), - dirty_to_present_ms: match snapshot.boundary { - IntervalBoundary::Presented(presented) => { - presented.dirty_to_present_duration().map(as_millis) - } - IntervalBoundary::Idle { .. } => None, - }, - sealed_by: match snapshot.boundary { - IntervalBoundary::Presented(_) => "present", - IntervalBoundary::Idle { .. } => "idle", - }, - busy_fraction: (busy_fraction * 1000.0).round() / 1000.0, - event_count: snapshot.events.len(), - small_poll_count: snapshot.small_poll_summary().count, - small_poll_total_ms: as_millis(snapshot.small_poll_summary().total), - dropped_events: snapshot.dropped_events, - journal_discontinuous: snapshot.journal_discontinuous, - contributors: { - let mut kept: Vec<&ForegroundEvent> = incident - .contributors - .iter() - .take(max_contributors) - .collect(); - kept.sort_by_key(|event| event.start_time()); - kept.into_iter() - .map(|event| { - SerializedHangContributor::convert( - startup, - event, - nesting_depth(event, &snapshot.events), - ) - }) - .collect() - }, - contributors_elided: incident.contributors.len().saturating_sub(max_contributors), - } - } -} - -/// How many events in `events` strictly contain `event`'s span. Events with -/// identical spans don't count as containing each other, so `event`'s own -/// presence in `events` contributes nothing. -fn nesting_depth(event: &ForegroundEvent, events: &[ForegroundEvent]) -> usize { - let (start, end) = (event.start_time(), event.end_time()); - events - .iter() - .filter(|other| { - let (other_start, other_end) = (other.start_time(), other.end_time()); - other_start <= start && end <= other_end && (other_start < start || end < other_end) - }) - .count() -} - -impl SerializedHangContributor { - fn convert(startup: Instant, event: &ForegroundEvent, depth: usize) -> Self { - let since_startup = - |instant: Instant| as_millis(instant.saturating_duration_since(startup)); - let duration_ms = as_millis(event.duration()); - match event { - ForegroundEvent::TaskPoll(timing) => Self::TaskPoll { - location: timing.location.into(), - start_ms: since_startup(timing.start), - duration_ms, - depth, - }, - ForegroundEvent::Action(timing) => Self::Action { - name: timing.name, - start_ms: since_startup(timing.start), - duration_ms, - depth, - }, - ForegroundEvent::Input(timing) => Self::Input { - input_kind: timing.kind, - start_ms: since_startup(timing.start), - duration_ms, - caused_invalidation: timing.caused_invalidation, - depth, - }, - ForegroundEvent::Draw(timing) => Self::Draw { - window_id: timing.window_id.as_u64(), - start_ms: since_startup(timing.draw_start), - duration_ms, - dirty_to_draw_ms: timing.dirty_to_draw_duration().map(as_millis), - invalidations: timing.invalidations, - depth, - }, - ForegroundEvent::Present(timing) => Self::Present { - window_id: timing.window_id.as_u64(), - start_ms: since_startup(timing.present_start), - duration_ms, - depth, - }, - ForegroundEvent::SmallPolls(flush) => { - // The sealer folds these out of snapshot events; a contributor - // can therefore never be one, but serialize defensively as an - // unnamed poll spanning the flush rather than panicking. - Self::TaskPoll { - location: SerializedLocation { - file: "".into(), - line: 0, - column: 0, - }, - start_ms: since_startup(flush.since), - duration_ms, - depth, - } - } - } - } -} - -impl HangIncident { - /// The incident's reporting window: from its earliest cause — the - /// sealing frame's first invalidation, or the earliest contributor's - /// start when no repaint was pending — to the seal. This trims - /// foreground-idle time between the previous frame and the cause, and - /// may begin before the underlying snapshot when a contributor was - /// already running at the previous seal. - pub fn active_window(&self) -> (Instant, Instant) { - let snapshot = &self.snapshot; - let dirty_at = snapshot.boundary.dirty_at(); - let earliest_contributor = self - .contributors - .iter() - .map(|event| event.start_time()) - .min(); - let start = [dirty_at, earliest_contributor] - .into_iter() - .flatten() - .min() - .unwrap_or(snapshot.interval_start); - (start, snapshot.interval_end()) - } - - /// Returns an incident when the snapshot contains at least one event - /// that blocked the foreground for `threshold` or longer, or when the - /// interval's total foreground spend — event time plus folded - /// small-poll time — reached `frame_budget` even though no single event - /// did. In the latter case every event in the interval becomes a - /// contributor, since no single stall explains the busy interval. - pub fn detect( - snapshot: FrameSnapshot, - threshold: Duration, - frame_budget: Duration, - ) -> Option { - let mut contributors: Vec = snapshot - .events - .iter() - .filter(|event| event.duration() >= threshold) - .copied() - .collect(); - let trigger = if contributors.is_empty() { - if snapshot.journal_discontinuous { - return None; - } - let spend = snapshot.occupancy_within(snapshot.interval_start, snapshot.interval_end()); - if spend < frame_budget { - return None; - } - contributors = snapshot.events.clone(); - HangTrigger::Budget - } else { - HangTrigger::Threshold - }; - contributors.sort_by_key(|event| std::cmp::Reverse(event.duration())); - Some(Self { - snapshot, - trigger, - contributors, - }) - } -} - -#[cfg(test)] -mod tests { - use std::cell::Cell; - use std::rc::Rc; - use std::thread; - use std::time::Duration; - - use proptest::prelude::*; - use rand::prelude::*; - use scheduler::SpawnTime; - - use crate::{ - self as gpui, Context, FocusHandle, InteractiveElement, IntoElement, Modifiers, - MouseButton, Render, Styled, TestAppContext, VisualTestContext, Window, WindowId, div, - point, px, - }; - - use crate::profiler::{ActionTiming, FrameTiming, PresentTiming, TaskTiming, YieldTime}; - - use super::super::journal::{ - FRAME_DEADLINE, ForegroundEvent, ForegroundJournalEntry, FrameSnapshot, FrameStateChange, - InputTiming, IntervalBoundary, PollSummary, PresentedFrame, SmallPollFlush, - install_test_foreground_journal, record_present, - }; - use super::{ - HangDetector, HangIncident, HangTrigger, SerializedHangContributor, SerializedHangIncident, - }; - - actions!(hang_test, [HangyAction]); - - // Well above legitimate per-event work in a test app (layout of one div, - // empty polls), well below the injected hangs. - const HANG_THRESHOLD: Duration = Duration::from_millis(10); - // Equal to the threshold, mirroring how production wires the detector - // today. - const FRAME_BUDGET: Duration = HANG_THRESHOLD; - - /// A hang that outlives the frame deadline stays in one incident with - /// the frame it starved: the deadline only unblocks idle boundaries, so - /// the eventual presentation seals the hang together with its - /// dirty-to-present association. - #[test] - fn a_hang_outliving_the_frame_deadline_keeps_its_frame_association() { - let start = scheduler::Instant::now(); - let window_id = WindowId::from(0x51E17); - let hang_end = start + FRAME_DEADLINE * 5; - let presented_at = hang_end + Duration::from_millis(16); - let (journal, _journal_guard) = install_test_foreground_journal(64, 8); - let mut detector = HangDetector::new(journal, HANG_THRESHOLD, FRAME_BUDGET); - - let snapshots = detector.sealer.push_entries([ - ForegroundJournalEntry::FrameState(FrameStateChange::Pending { - window_id, - dirty_at: start, - }), - ForegroundJournalEntry::Event(task_poll_event(start, hang_end)), - ]); - assert!(snapshots.is_empty(), "nothing seals mid-hang"); - - let snapshots = detector - .sealer - .push_entries([ForegroundJournalEntry::Boundary( - IntervalBoundary::Presented(PresentedFrame { - frame: frame(window_id, presented_at), - presentation: PresentTiming { - window_id, - present_start: presented_at - Duration::from_millis(1), - present_end: presented_at, - animation_interval: None, - }, - }), - )]); - let [snapshot] = snapshots.as_slice() else { - panic!("expected one presented snapshot, got {snapshots:?}"); - }; - let incident = HangIncident::detect(snapshot.clone(), HANG_THRESHOLD, FRAME_BUDGET) - .expect("the hang qualifies"); - assert!(matches!( - incident.contributors[0], - ForegroundEvent::TaskPoll(timing) if timing.end.0 == hang_end - )); - assert!(matches!( - incident.snapshot.boundary, - IntervalBoundary::Presented(_) - )); - } - - #[test] - fn serialized_incident_reports_presented_seal_fields() { - let startup = scheduler::Instant::now(); - let at = |ms: u64| startup + Duration::from_millis(ms); - let window_id = WindowId::from(0xF1E1D); - - let presentation = PresentTiming { - window_id, - present_start: at(380), - present_end: at(400), - animation_interval: None, - }; - let frame = FrameTiming { - window_id, - dirty_at: Some(at(100)), - invalidations: 3, - draw_start: at(350), - draw_end: at(380), - }; - let snapshot = FrameSnapshot { - interval_start: at(150), - boundary: IntervalBoundary::Presented(PresentedFrame { - frame, - presentation, - }), - events: vec![ - task_poll_event(at(150), at(300)), - ForegroundEvent::Action(ActionTiming { - name: "test::SlowAction", - start: at(300), - end: at(340), - }), - ForegroundEvent::Input(InputTiming { - kind: "test", - start: at(340), - end: at(345), - caused_invalidation: false, - }), - ForegroundEvent::Present(presentation), - ], - small_polls: vec![SmallPollFlush { - summary: PollSummary { - count: 2, - total: Duration::from_millis(1), - }, - since: at(105), - until: at(145), - }], - dropped_events: 0, - journal_discontinuous: false, - }; - - let incident = - HangIncident::detect(snapshot, HANG_THRESHOLD, FRAME_BUDGET).expect("has contributors"); - let serialized = SerializedHangIncident::convert(startup, &incident, 1, Some(at(50))); - - assert_eq!(serialized.phase, "steady"); - assert_eq!(serialized.trigger, HangTrigger::Threshold); - // The frame's first invalidation anchors the active window, not the - // interval start or the first contributor. - assert_eq!(serialized.start_ms, 100.0); - assert_eq!(serialized.active_ms, 300.0); - assert_eq!(serialized.stall_ms, 150.0); - assert_eq!(serialized.dirty_to_present_ms, Some(300.0)); - assert_eq!(serialized.sealed_by, "present"); - // Occupancy: poll 150ms + action 40ms + input 5ms + present 20ms + - // folded polls 1ms = 216ms of the 300ms window. - assert_eq!(serialized.busy_fraction, 0.72); - assert_eq!(serialized.event_count, 4); - assert_eq!(serialized.small_poll_count, 2); - assert_eq!(serialized.small_poll_total_ms, 1.0); - assert_eq!(serialized.dropped_events, 0); - // Three contributors qualify (poll, action, present); the cap keeps - // the longest and counts the rest. - assert_eq!(serialized.contributors.len(), 1); - assert_eq!(serialized.contributors_elided, 2); - assert!(matches!( - serialized.contributors[0], - SerializedHangContributor::TaskPoll { - start_ms, - duration_ms, - .. - } if start_ms == 150.0 && duration_ms == 150.0 - )); - } - - #[test] - fn serialized_incident_reports_idle_seal_fields() { - let startup = scheduler::Instant::now(); - let at = |ms: u64| startup + Duration::from_millis(ms); - - let idle = FrameSnapshot { - interval_start: at(200), - boundary: IntervalBoundary::Idle { ended_at: at(260) }, - events: vec![task_poll_event(at(200), at(260))], - small_polls: Vec::new(), - dropped_events: 0, - journal_discontinuous: false, - }; - let incident = - HangIncident::detect(idle, HANG_THRESHOLD, FRAME_BUDGET).expect("has contributors"); - let serialized = SerializedHangIncident::convert(startup, &incident, 8, None); - assert_eq!(serialized.phase, "startup"); - assert_eq!(serialized.sealed_by, "idle"); - assert_eq!(serialized.dirty_to_present_ms, None); - // With no frame, the earliest contributor anchors the active window. - assert_eq!(serialized.start_ms, 200.0); - assert_eq!(serialized.active_ms, 60.0); - assert_eq!(serialized.stall_ms, 60.0); - assert_eq!(serialized.busy_fraction, 1.0); - } - - #[test] - fn phase_is_startup_until_the_first_present() { - let startup = scheduler::Instant::now(); - let at = |ms: u64| startup + Duration::from_millis(ms); - let snapshot = FrameSnapshot { - interval_start: at(500), - boundary: IntervalBoundary::Idle { ended_at: at(600) }, - events: vec![task_poll_event(at(500), at(600))], - small_polls: Vec::new(), - dropped_events: 0, - journal_discontinuous: false, - }; - let incident = - HangIncident::detect(snapshot, HANG_THRESHOLD, FRAME_BUDGET).expect("has contributors"); - - let phase = |first_present_at| { - SerializedHangIncident::convert(startup, &incident, 8, first_present_at).phase - }; - assert_eq!(phase(None), "startup", "nothing has been presented yet"); - assert_eq!( - phase(Some(at(700))), - "startup", - "the incident began before the first presentation" - ); - assert_eq!( - phase(Some(at(500))), - "steady", - "an incident beginning exactly at first presentation is steady" - ); - assert_eq!(phase(Some(at(100))), "steady"); - } - - /// The detector must latch the first presentation it observes and never - /// move it. - #[test] - fn first_present_at_latches_on_the_first_observed_presentation() { - let (journal, _journal_guard) = install_test_foreground_journal(64, 8); - let mut detector = HangDetector::new(journal, HANG_THRESHOLD, FRAME_BUDGET); - let window_id = WindowId::from(0x1A7C4); - - let first_present_end = scheduler::Instant::now(); - record_present( - presentation(window_id, first_present_end), - Some(frame(window_id, first_present_end)), - ); - detector.poll(); - let latched = detector - .first_present_at() - .expect("a presentation was recorded"); - assert_eq!(latched, first_present_end); - - record_present( - presentation(window_id, first_present_end + Duration::from_millis(16)), - Some(frame( - window_id, - first_present_end + Duration::from_millis(16), - )), - ); - detector.poll(); - assert_eq!(detector.first_present_at(), Some(latched)); - } - - /// The only work inside the 100ms active window here is the 50ms poll, - /// so the busy fraction is 0.5: the folded polls' flush span lies - /// entirely before the window and must contribute nothing to it - /// (PR #62779 review finding 5). - #[test] - fn busy_fraction_excludes_small_polls_outside_the_active_window() { - let startup = scheduler::Instant::now(); - let at = |ms: u64| startup + Duration::from_millis(ms); - let snapshot = FrameSnapshot { - interval_start: at(0), - boundary: IntervalBoundary::Idle { ended_at: at(1000) }, - events: vec![task_poll_event(at(900), at(950))], - // Folded polls that ran during at(0)..at(800), long before the - // active window opens at the contributor's start. - small_polls: vec![SmallPollFlush { - summary: PollSummary { - count: 500, - total: Duration::from_millis(400), - }, - since: at(0), - until: at(800), - }], - dropped_events: 0, - journal_discontinuous: false, - }; - - let incident = - HangIncident::detect(snapshot, HANG_THRESHOLD, FRAME_BUDGET).expect("has contributors"); - let serialized = SerializedHangIncident::convert(startup, &incident, 8, None); - - assert_eq!(serialized.busy_fraction, 0.5); - } - - /// Many sub-threshold pieces of work can drop a frame as thoroughly as - /// one long stall: an interval whose total foreground spend reaches the - /// budget is an incident even when no single event crosses the hang - /// threshold, and every event becomes a contributor so the report shows - /// what filled the interval. - #[test] - fn an_interval_of_small_work_over_budget_is_an_incident() { - let startup = scheduler::Instant::now(); - let at = |ms: u64| startup + Duration::from_millis(ms); - let window_id = WindowId::from(0xB0D6E7); - let snapshot = FrameSnapshot { - interval_start: at(0), - boundary: IntervalBoundary::Presented(PresentedFrame { - frame: FrameTiming { - window_id, - dirty_at: Some(at(0)), - invalidations: 1, - draw_start: at(140), - draw_end: at(145), - }, - presentation: PresentTiming { - window_id, - present_start: at(145), - present_end: at(150), - animation_interval: None, - }, - }), - events: vec![ - task_poll_event(at(0), at(5)), - task_poll_event(at(5), at(13)), - task_poll_event(at(13), at(18)), - ], - small_polls: Vec::new(), - dropped_events: 0, - journal_discontinuous: false, - }; - - let incident = HangIncident::detect(snapshot, HANG_THRESHOLD, FRAME_BUDGET) - .expect("foreground spend exceeded the frame budget"); - assert_eq!(incident.trigger, HangTrigger::Budget); - assert_eq!(incident.contributors.len(), 3); - assert_eq!( - incident.contributors[0].duration(), - Duration::from_millis(8) - ); - let serialized = SerializedHangIncident::convert(startup, &incident, 8, Some(startup)); - assert_eq!(serialized.trigger, HangTrigger::Budget); - assert_eq!(serialized.stall_ms, 8.0); - assert_eq!(serialized.dirty_to_present_ms, Some(150.0)); - assert_eq!(serialized.sealed_by, "present"); - } - - #[test] - fn a_journal_gap_suppresses_budget_inference_but_retains_observed_hangs() { - let start = scheduler::Instant::now(); - let at = |ms: u64| start + Duration::from_millis(ms); - let mut sealer = super::super::journal::IntervalSealer::new(start); - let snapshots = sealer.push_entries([ - ForegroundJournalEntry::Event(task_poll_event(at(0), at(5))), - ForegroundJournalEntry::Discontinuity { lost: 1 }, - ForegroundJournalEntry::Event(task_poll_event(at(10), at(15))), - ForegroundJournalEntry::Boundary(IntervalBoundary::Idle { ended_at: at(15) }), - ForegroundJournalEntry::Discontinuity { lost: 1 }, - ForegroundJournalEntry::Event(task_poll_event(at(20), at(32))), - ForegroundJournalEntry::Boundary(IntervalBoundary::Idle { ended_at: at(32) }), - ForegroundJournalEntry::Event(task_poll_event(at(40), at(52))), - ForegroundJournalEntry::Boundary(IntervalBoundary::Idle { ended_at: at(52) }), - ]); - let [budget_only, observed_hang, clean] = snapshots.as_slice() else { - panic!("expected two discontinuous snapshots followed by a clean one"); - }; - - assert!(budget_only.journal_discontinuous); - assert_eq!(budget_only.dropped_events, 1); - assert!(HangIncident::detect(budget_only.clone(), HANG_THRESHOLD, FRAME_BUDGET).is_none()); - - let observed_hang = - HangIncident::detect(observed_hang.clone(), HANG_THRESHOLD, FRAME_BUDGET) - .expect("the directly observed threshold-qualified hang remains valid"); - assert!(observed_hang.snapshot.journal_discontinuous); - assert_eq!(observed_hang.contributors.len(), 1); - let serialized = SerializedHangIncident::convert(start, &observed_hang, 8, None); - assert!(serialized.journal_discontinuous); - - assert!(!clean.journal_discontinuous); - assert!(HangIncident::detect(clean.clone(), HANG_THRESHOLD, FRAME_BUDGET).is_some()); - } - - /// A frame that reaches the screen late with almost no foreground work - /// behind it — pure scheduling or presentation delay — is not a hang: - /// the budget measures foreground spend, not dirty-to-present time. - #[test] - fn a_slow_frame_with_little_foreground_spend_is_not_an_incident() { - let startup = scheduler::Instant::now(); - let at = |ms: u64| startup + Duration::from_millis(ms); - let window_id = WindowId::from(0xFA57); - let snapshot = FrameSnapshot { - interval_start: at(0), - boundary: IntervalBoundary::Presented(PresentedFrame { - frame: FrameTiming { - window_id, - dirty_at: Some(at(0)), - invalidations: 1, - draw_start: at(145), - draw_end: at(147), - }, - presentation: PresentTiming { - window_id, - present_start: at(149), - present_end: at(150), - animation_interval: None, - }, - }), - events: vec![task_poll_event(at(0), at(5))], - small_polls: Vec::new(), - dropped_events: 0, - journal_discontinuous: false, - }; - - assert!(HangIncident::detect(snapshot, HANG_THRESHOLD, FRAME_BUDGET).is_none()); - } - - /// The budget applies to idle-sealed intervals too: a headless app (or - /// a stretch with no repaint pending) can still starve the foreground - /// with accumulated sub-threshold work. - #[test] - fn an_idle_sealed_interval_over_budget_is_an_incident() { - let startup = scheduler::Instant::now(); - let at = |ms: u64| startup + Duration::from_millis(ms); - let snapshot = FrameSnapshot { - interval_start: at(0), - boundary: IntervalBoundary::Idle { ended_at: at(200) }, - events: (0..10) - .map(|i| task_poll_event(at(i * 20), at(i * 20 + 5))) - .collect(), - small_polls: Vec::new(), - dropped_events: 0, - journal_discontinuous: false, - }; - - let incident = HangIncident::detect(snapshot, HANG_THRESHOLD, FRAME_BUDGET) - .expect("foreground spend exceeded the frame budget"); - assert_eq!(incident.contributors.len(), 10); - let serialized = SerializedHangIncident::convert(startup, &incident, 8, Some(startup)); - assert_eq!(serialized.sealed_by, "idle"); - assert_eq!(serialized.dirty_to_present_ms, None); - assert_eq!(serialized.stall_ms, 5.0); - assert_eq!(serialized.contributors_elided, 2); - } - - /// Contributors serialize in start order with their nesting depth: an - /// input dispatch that synchronously drew a window reads as the input at - /// depth 0 followed by the draw at depth 1, rather than two unrelated - /// blocks of equal wall time. - #[test] - fn serialized_contributors_are_chronological_with_nesting_depths() { - let startup = scheduler::Instant::now(); - let at = |ms: u64| startup + Duration::from_millis(ms); - let window_id = WindowId::from(0x2E57ED); - let snapshot = FrameSnapshot { - interval_start: at(0), - boundary: IntervalBoundary::Idle { ended_at: at(80) }, - events: vec![ - task_poll_event(at(50), at(70)), - ForegroundEvent::Input(InputTiming { - kind: "mouse_move", - start: at(0), - end: at(40), - caused_invalidation: true, - }), - ForegroundEvent::Draw(FrameTiming { - window_id, - dirty_at: Some(at(0)), - invalidations: 1, - draw_start: at(1), - draw_end: at(39), - }), - ], - small_polls: Vec::new(), - dropped_events: 0, - journal_discontinuous: false, - }; - - let incident = - HangIncident::detect(snapshot, HANG_THRESHOLD, FRAME_BUDGET).expect("has contributors"); - let serialized = SerializedHangIncident::convert(startup, &incident, 8, Some(startup)); - - assert_eq!(serialized.stall_ms, 40.0); - assert!(matches!( - serialized.contributors.as_slice(), - [ - SerializedHangContributor::Input { - input_kind: "mouse_move", - depth: 0, - .. - }, - SerializedHangContributor::Draw { depth: 1, .. }, - SerializedHangContributor::TaskPoll { depth: 0, .. }, - ] - )); - } - - /// Folded small polls count toward foreground spend, so an interval can - /// reach the budget with no retained events at all. The incident then - /// has no contributors and the small-poll summary carries the story. - #[test] - fn small_poll_spend_alone_can_reach_the_budget() { - let startup = scheduler::Instant::now(); - let at = |ms: u64| startup + Duration::from_millis(ms); - let window_id = WindowId::from(0xDE1A7); - let snapshot = FrameSnapshot { - interval_start: at(0), - boundary: IntervalBoundary::Presented(PresentedFrame { - frame: FrameTiming { - window_id, - dirty_at: Some(at(0)), - invalidations: 1, - draw_start: at(148), - draw_end: at(149), - }, - presentation: PresentTiming { - window_id, - present_start: at(149), - present_end: at(150), - animation_interval: None, - }, - }), - events: Vec::new(), - small_polls: vec![SmallPollFlush { - summary: PollSummary { - count: 40, - total: Duration::from_millis(12), - }, - since: at(0), - until: at(150), - }], - dropped_events: 0, - journal_discontinuous: false, - }; - - let incident = HangIncident::detect(snapshot, HANG_THRESHOLD, FRAME_BUDGET) - .expect("small-poll spend exceeded the frame budget"); - assert!(incident.contributors.is_empty()); - let serialized = SerializedHangIncident::convert(startup, &incident, 8, Some(startup)); - assert_eq!(serialized.stall_ms, 0.0); - assert_eq!(serialized.event_count, 0); - assert_eq!(serialized.small_poll_count, 40); - assert_eq!(serialized.dirty_to_present_ms, Some(150.0)); - assert_eq!(serialized.busy_fraction, 0.08); - } - - proptest! { - #![proptest_config(ProptestConfig { - failure_persistence: None, - ..ProptestConfig::default() - })] - - #[test] - fn detection_matches_threshold_and_budget_model_at_boundaries( - durations_ms in prop::collection::vec(0u16..=50, 0..=16), - threshold_ms in 0u16..=60, - frame_budget_ms in 0u16..=500, - small_poll_total_ms in 0u16..=100, - ) { - let origin = scheduler::Instant::now(); - let mut cursor_ms = 0u64; - let events = durations_ms - .iter() - .map(|duration_ms| { - let start = origin + Duration::from_millis(cursor_ms); - cursor_ms += u64::from(*duration_ms); - let end = origin + Duration::from_millis(cursor_ms); - cursor_ms += 1; - ForegroundEvent::Input(InputTiming { - kind: "test", - start, - end, - caused_invalidation: false, - }) - }) - .collect::>(); - let interval_end_ms = cursor_ms.max(1); - let snapshot = FrameSnapshot { - interval_start: origin, - boundary: IntervalBoundary::Idle { - ended_at: origin + Duration::from_millis(interval_end_ms), - }, - events, - small_polls: vec![SmallPollFlush { - summary: PollSummary { - count: u64::from(small_poll_total_ms > 0), - total: Duration::from_millis(u64::from(small_poll_total_ms)), - }, - since: origin, - until: origin + Duration::from_millis(interval_end_ms), - }], - dropped_events: 0, - journal_discontinuous: false, - }; - let qualifying_durations = durations_ms - .iter() - .copied() - .filter(|duration| *duration >= threshold_ms) - .collect::>(); - let occupancy_ms = durations_ms - .iter() - .map(|duration| u64::from(*duration)) - .sum::() - + u64::from(small_poll_total_ms); - let expected_incident = - !qualifying_durations.is_empty() || occupancy_ms >= u64::from(frame_budget_ms); - - let incident = HangIncident::detect( - snapshot, - Duration::from_millis(u64::from(threshold_ms)), - Duration::from_millis(u64::from(frame_budget_ms)), - ); - prop_assert_eq!(incident.is_some(), expected_incident); - - if let Some(incident) = incident { - let mut expected_contributors = if qualifying_durations.is_empty() { - durations_ms - } else { - qualifying_durations - }; - expected_contributors.sort_unstable_by(|first, second| second.cmp(first)); - let observed_contributors = incident - .contributors - .iter() - .map(|event| event.duration().as_millis() as u16) - .collect::>(); - prop_assert_eq!(observed_contributors, expected_contributors); - } - } - } - - #[derive(Clone, Default)] - struct HangControls { - render: Rc>>, - input: Rc>>, - action: Rc>>, - } - - struct HangyView { - controls: HangControls, - focus_handle: FocusHandle, - } - - impl Render for HangyView { - fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { - if let Some(duration) = self.controls.render.take() { - thread::sleep(duration); - } - let action_controls = self.controls.clone(); - let input_controls = self.controls.clone(); - div() - .size_full() - .track_focus(&self.focus_handle) - .on_action(cx.listener(move |_, _: &HangyAction, _, _| { - if let Some(duration) = action_controls.action.take() { - thread::sleep(duration); - } - })) - .on_mouse_down(MouseButton::Left, move |_, _, _| { - if let Some(duration) = input_controls.input.take() { - thread::sleep(duration); - } - }) - } - } - - #[derive(Debug, Copy, Clone, PartialEq, Eq)] - enum HangKind { - Render, - Input, - Action, - Poll, - } - - /// Renders a real element tree and injects randomly ordered, randomly - /// sized hangs through the production dispatch paths: view rendering - /// (window draw), platform input dispatch, action dispatch, and a - /// foreground task poll. Asserts the detector reports every one of them. - #[gpui::test(iterations = 5)] - fn detects_randomly_placed_foreground_hangs(mut rng: StdRng, cx: &mut TestAppContext) { - let (journal, _journal_guard) = install_test_foreground_journal(1024, 16); - let mut detector = HangDetector::new(journal, HANG_THRESHOLD, FRAME_BUDGET); - - let controls = HangControls::default(); - let (view, cx) = cx.add_window_view(|_, cx| HangyView { - controls: controls.clone(), - focus_handle: cx.focus_handle(), - }); - view.update_in(cx, |view, window, cx| { - window.focus(&view.focus_handle, cx); - }); - draw_window(cx); - - let mut injected: Vec<(HangKind, Duration)> = Vec::new(); - for _ in 0..rng.random_range(1..=4) { - let duration = HANG_THRESHOLD + Duration::from_millis(rng.random_range(5..25)); - let kind = match rng.random_range(0..4) { - 0 => { - controls.render.set(Some(duration)); - view.update_in(cx, |_, _, cx| cx.notify()); - draw_window(cx); - HangKind::Render - } - 1 => { - controls.input.set(Some(duration)); - cx.simulate_mouse_down( - point(px(5.), px(5.)), - MouseButton::Left, - Modifiers::none(), - ); - HangKind::Input - } - 2 => { - controls.action.set(Some(duration)); - cx.dispatch_action(HangyAction); - HangKind::Action - } - _ => { - simulate_blocked_foreground_poll(duration); - HangKind::Poll - } - }; - injected.push((kind, duration)); - - // Innocent interleaved activity that must not confuse detection. - if rng.random_bool(0.5) { - draw_window(cx); - } - if rng.random_bool(0.5) { - cx.simulate_mouse_up(point(px(5.), px(5.)), MouseButton::Left, Modifiers::none()); - } - } - - // A final presentation seals whatever interval is still open. - draw_window(cx); - - let incidents = detector.poll(); - let contributors: Vec = incidents - .iter() - .flat_map(|incident| incident.contributors.iter().copied()) - .collect(); - - for kind in [ - HangKind::Render, - HangKind::Input, - HangKind::Action, - HangKind::Poll, - ] { - let expected: Vec = injected - .iter() - .filter(|(injected_kind, _)| *injected_kind == kind) - .map(|(_, duration)| *duration) - .collect(); - let observed: Vec = contributors - .iter() - .filter(|event| matches_kind(event, kind) && event.duration() >= HANG_THRESHOLD) - .map(|event| event.duration()) - .collect(); - assert_all_matched(kind, expected, observed); - } - } - - /// Every injected hang must be covered by a distinct observed contributor - /// at least as long as the injected sleep (sleeps never wake early). - fn assert_all_matched( - kind: HangKind, - mut expected: Vec, - mut observed: Vec, - ) { - assert_eq!( - observed.len(), - expected.len(), - "expected every observed {kind:?} hang to correspond to one injection; \ - expected {expected:?}, observed {observed:?}" - ); - expected.sort_unstable_by(|a, b| b.cmp(a)); - observed.sort_unstable_by(|a, b| b.cmp(a)); - let mut observed = observed.into_iter(); - for expected_duration in expected { - let matched = observed.find(|observed| *observed >= expected_duration); - assert!( - matched.is_some(), - "injected {kind:?} hang of {expected_duration:?} was not detected" - ); - } - } - - fn matches_kind(event: &ForegroundEvent, kind: HangKind) -> bool { - match (event, kind) { - (ForegroundEvent::Draw(_), HangKind::Render) => true, - (ForegroundEvent::Input(_), HangKind::Input) => true, - (ForegroundEvent::Action(timing), HangKind::Action) => { - timing.name.ends_with("HangyAction") - } - (ForegroundEvent::TaskPoll(timing), HangKind::Poll) => { - timing.location.file() == file!() - } - _ => false, - } - } - - fn draw_window(cx: &mut VisualTestContext) { - cx.update(|window, cx| { - let arena_clear = window.draw(cx); - window.present_if_needed(); - arena_clear.clear(cx); - }); - } - - /// The deterministic test scheduler does not bracket runnables with the - /// profiler hooks, so drive the same public hooks the platform - /// dispatchers call around a poll that blocks the foreground. - fn simulate_blocked_foreground_poll(duration: Duration) { - let location = std::panic::Location::caller(); - crate::profiler::update_running_task(SpawnTime(scheduler::Instant::now()), location); - thread::sleep(duration); - crate::profiler::save_task_timing(); - } - - fn task_poll_event(start: scheduler::Instant, end: scheduler::Instant) -> ForegroundEvent { - ForegroundEvent::TaskPoll(TaskTiming { - location: std::panic::Location::caller(), - spawned: SpawnTime(start), - start, - end: YieldTime(end), - }) - } - - fn presentation(window_id: WindowId, present_end: scheduler::Instant) -> PresentTiming { - PresentTiming { - window_id, - present_start: present_end - Duration::from_millis(1), - present_end, - animation_interval: None, - } - } - - fn frame(window_id: WindowId, draw_end: scheduler::Instant) -> FrameTiming { - FrameTiming { - window_id, - dirty_at: Some(draw_end - Duration::from_millis(2)), - invalidations: 1, - draw_start: draw_end - Duration::from_millis(1), - draw_end, - } - } -} diff --git a/crates/gpui_pre/src/profiler/journal.rs b/crates/gpui_pre/src/profiler/journal.rs deleted file mode 100644 index 761181a0..00000000 --- a/crates/gpui_pre/src/profiler/journal.rs +++ /dev/null @@ -1,2766 +0,0 @@ -//! A journal of foreground work and the semantic boundaries between activity intervals. -//! -//! The foreground thread records task polls, action handlers, input dispatches, -//! draws, and presentations as [`ForegroundEvent`]s in a bounded journal ring. -//! Task polls shorter than [`TASK_POLL_FLOOR`] are folded into summaries, which -//! bound the stream by the number of slow polls while preserving their exact -//! count and total duration. -//! -//! Presentation and the foreground going idle are explicit [`IntervalBoundary`] -//! entries in the same stream. Independent [`ForegroundJournalCollector`]s -//! feed entries to [`IntervalSealer`], a pure state machine that groups all work -//! preceding each boundary into a [`FrameSnapshot`]. The sealer does not infer -//! boundaries from elapsed time or from incidental event kinds such as draws. - -use std::cell::{RefCell, UnsafeCell}; -use std::collections::{HashMap, VecDeque}; -use std::mem::MaybeUninit; -use std::sync::{ - Arc, - atomic::{AtomicU64, AtomicUsize, Ordering}, -}; -use std::time::Duration; - -use scheduler::Instant; - -use super::{ActionTiming, FrameTiming, PresentTiming, TaskTiming}; -use crate::WindowId; - -/// Task polls shorter than this are folded into a [`PollSummary`] instead of -/// being recorded individually. This keeps the stream bounded by the number -/// of *slow* polls while preserving their exact count and total duration. -pub const TASK_POLL_FLOOR: Duration = Duration::from_micros(100); - -/// A dirty frame that has not been presented by this deadline stops blocking -/// foreground-idle boundaries, so a window that never presents (hidden or -/// occluded windows receive no frame callbacks) cannot suppress boundaries -/// indefinitely. Expiry only unblocks: the open interval still seals at a -/// real presentation or idle boundary, keeping a starving hang and the frame -/// it starved in one interval. -pub const FRAME_DEADLINE: Duration = Duration::from_secs(1); - -// Backstop against pathological event storms within a single interval. At the -// 100us floor, a fully hung second can produce at most ~10k recordable polls, -// so this bound is only reachable when something is already deeply wrong. -const MAX_INTERVAL_EVENTS: usize = 16 * 1024; - -// Allow 4MiB for the fixed ring allocation, including each slot's atomic -// metadata. The poll floor and frame cadence bound the event rate to roughly -// 10k per second in the worst case, so this holds several seconds of -// worst-case traffic between consumer drains. -const MAX_JOURNAL_ENTRIES: usize = (4 * 1024 * 1024) / core::mem::size_of::(); - -// Absorbs brief collisions with a collector reading the exact slot being -// wrapped. The foreground never waits for a reader; queued entries are retried -// in order on the next publication. -const MAX_PENDING_JOURNAL_ENTRIES: usize = 64; - -/// One entry in the foreground stream. -/// -/// Events are recorded in completion order (ordered by their end time). An -/// event that was in progress across a frame boundary (e.g. the task poll -/// enclosing a draw) is recorded when it *ends*, with timestamps that may -/// precede events recorded before it. -#[derive(Debug, Copy, Clone)] -pub enum ForegroundEvent { - /// A foreground task poll at least [`TASK_POLL_FLOOR`] long. - TaskPoll(TaskTiming), - /// A completed action handler. - Action(ActionTiming), - /// A dispatched platform input event. - Input(InputTiming), - /// A completed window draw. - Draw(FrameTiming), - /// Work spent submitting a frame to the platform. - Present(PresentTiming), - /// Aggregate of task polls below [`TASK_POLL_FLOOR`], flushed before the - /// next individually retained event or interval boundary. - SmallPolls(SmallPollFlush), -} - -impl ForegroundEvent { - /// When the work described by this event began. For draws this is the - /// start of the draw itself, not the frame's first invalidation. - pub fn start_time(&self) -> Instant { - match self { - Self::TaskPoll(timing) => timing.start, - Self::Action(timing) => timing.start, - Self::Input(timing) => timing.start, - Self::Draw(timing) => timing.draw_start, - Self::Present(timing) => timing.present_start, - Self::SmallPolls(flush) => flush.since, - } - } - - /// When the work described by this event ended. - pub fn end_time(&self) -> Instant { - match self { - Self::TaskPoll(timing) => timing.end.0, - Self::Action(timing) => timing.end, - Self::Input(timing) => timing.end, - Self::Draw(timing) => timing.draw_end, - Self::Present(timing) => timing.present_end, - Self::SmallPolls(flush) => flush.until, - } - } - - /// How long the work described by this event took. For - /// [`Self::SmallPolls`] this is the span the summary covers, not time - /// spent polling; use the summary's `total` for occupancy. - pub fn duration(&self) -> Duration { - self.end_time().duration_since(self.start_time()) - } -} - -/// Timing of one platform input dispatch on a window. -#[derive(Debug, Copy, Clone)] -pub struct InputTiming { - /// The platform input variant dispatched (see - /// [`crate::PlatformInput::kind_name`]). - pub kind: &'static str, - /// When the input dispatch started. - pub start: Instant, - /// When the input dispatch finished. - pub end: Instant, - /// Whether handling the input invalidated a window. - pub caused_invalidation: bool, -} - -/// Exact count and total duration of task polls below [`TASK_POLL_FLOOR`]. -#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] -pub struct PollSummary { - /// Number of polls below the floor. - pub count: u64, - /// Total duration of polls below the floor. - pub total: Duration, -} - -impl PollSummary { - fn add(&mut self, other: Self) { - self.count += other.count; - self.total += other.total; - } -} - -/// A flushed [`PollSummary`] and the tightest span containing its folded polls. -#[derive(Debug, Copy, Clone)] -pub struct SmallPollFlush { - /// The folded polls. - pub summary: PollSummary, - /// When the first folded poll began. - pub since: Instant, - /// When the last folded poll ended. - pub until: Instant, -} - -/// A newly drawn frame and the platform submission that completed its interval. -#[derive(Debug, Copy, Clone)] -pub struct PresentedFrame { - /// The draw whose rendered scene was submitted. - pub frame: FrameTiming, - /// Work spent submitting that scene to the platform. - pub presentation: PresentTiming, -} - -impl PresentedFrame { - /// Time from the frame's first invalidation through platform submission. - pub fn dirty_to_present_duration(&self) -> Option { - self.frame - .dirty_at - .map(|dirty_at| self.presentation.present_end.duration_since(dirty_at)) - } -} - -/// The semantic event that completed a foreground activity interval. -#[derive(Debug, Copy, Clone)] -pub enum IntervalBoundary { - /// A newly drawn frame was submitted to the platform. - Presented(PresentedFrame), - /// The foreground returned to an idle platform loop with no unexpired - /// frame pending. - Idle { - /// When the foreground went idle. - ended_at: Instant, - }, -} - -impl IntervalBoundary { - /// When the interval ended. - pub fn end_time(&self) -> Instant { - match self { - Self::Presented(presented) => presented.presentation.present_end, - Self::Idle { ended_at } => *ended_at, - } - } - - /// The first invalidation of the frame satisfied by this boundary, if any. - pub fn dirty_at(&self) -> Option { - match self { - Self::Presented(presented) => presented.frame.dirty_at, - Self::Idle { .. } => None, - } - } -} - -/// A control-plane change to one window's pending-frame state. -#[derive(Debug, Copy, Clone)] -pub enum FrameStateChange { - /// The window has a frame waiting to be presented. - Pending { - /// The window waiting for presentation. - window_id: WindowId, - /// When this frame generation first became dirty. - dirty_at: Instant, - }, - /// The window closed, so any pending frame no longer blocks idle - /// boundaries. - Closed { - /// The window that closed. - window_id: WindowId, - /// When the window closed. - at: Instant, - }, -} - -/// One item retained in the foreground journal. -#[derive(Debug, Copy, Clone)] -pub enum ForegroundJournalEntry { - /// A completed piece of foreground work. - Event(ForegroundEvent), - /// A semantic interval boundary. - Boundary(IntervalBoundary), - /// A change to pending-frame state. This is metadata, not foreground work. - FrameState(FrameStateChange), - /// One or more logical entries were unavailable at this point in the - /// stream. Consumers must not infer interval boundaries across this gap. - Discontinuity { - /// Number of unavailable logical entries. - lost: u64, - }, -} - -/// An immutable view of one sealed foreground interval, produced by -/// [`IntervalSealer`]. -#[derive(Debug, Clone)] -pub struct FrameSnapshot { - /// When the interval started (the previous seal, or the end of the idle - /// stretch preceding the interval's first event). - pub interval_start: Instant, - /// The semantic event that completed the interval, including its metadata. - pub boundary: IntervalBoundary, - /// Foreground work recorded during the interval, in completion order. - /// [`ForegroundEvent::SmallPolls`] entries are collected into - /// `small_polls` instead of appearing here. - pub events: Vec, - /// Span-tagged aggregates of task polls below [`TASK_POLL_FLOOR`]. Spans - /// are retained so occupancy can apportion folded poll time to reporting - /// windows narrower than the interval. - pub small_polls: Vec, - /// Events lost to the interval's event cap, plus ring losses reported - /// via [`IntervalSealer::note_lost`]. - pub dropped_events: u64, - /// Whether the journal had an unobserved gap during this interval. - pub journal_discontinuous: bool, -} - -impl FrameSnapshot { - /// When the interval ended. - pub fn interval_end(&self) -> Instant { - self.boundary.end_time() - } - - /// The combined count and total of all folded sub-floor polls in the - /// interval. - pub fn small_poll_summary(&self) -> PollSummary { - let mut summary = PollSummary::default(); - for flush in &self.small_polls { - summary.add(flush.summary); - } - summary - } - - /// Total foreground time occupied within the interval: the union of the - /// recorded events' spans (clamped to the interval, so nested work like an - /// action inside an input dispatch is not double counted) plus the folded - /// small polls. Folded polls cannot be unioned with nested individually - /// timed work, so this remains a close approximation. - pub fn occupancy(&self) -> Duration { - self.occupancy_within(self.interval_start, self.interval_end()) - } - - /// Like [`Self::occupancy`], but measured against an arbitrary window - /// (e.g. a reporting window anchored at a frame's first invalidation). - /// Event spans are clamped to the window. Folded polls carry no - /// individual timestamps, so each flush's total is apportioned by how - /// much of its span overlaps the window — assuming the folded time is - /// spread uniformly across the span. - pub fn occupancy_within(&self, window_start: Instant, window_end: Instant) -> Duration { - let mut spans: Vec<(Instant, Instant)> = self - .events - .iter() - .map(|event| { - let start = event.start_time().max(window_start); - let end = event.end_time().min(window_end).max(start); - (start, end) - }) - .collect(); - spans.sort_by_key(|(start, _)| *start); - - let mut occupied = Duration::ZERO; - let mut merged_until: Option = None; - for (start, end) in spans { - let start = match merged_until { - Some(merged_until) => start.max(merged_until), - None => start, - }; - occupied += end.duration_since(start); - merged_until = Some(match merged_until { - Some(merged_until) => merged_until.max(end), - None => end, - }); - } - - let apportioned_small_polls: Duration = self - .small_polls - .iter() - .map(|flush| { - let span = flush.until.duration_since(flush.since); - if span.is_zero() { - // A point-like flush lies either inside or outside the - // window. - if flush.since >= window_start && flush.since <= window_end { - flush.summary.total - } else { - Duration::ZERO - } - } else { - let overlap_start = flush.since.max(window_start); - let overlap_end = flush.until.min(window_end).max(overlap_start); - let overlap = overlap_end.duration_since(overlap_start); - flush.summary.total.mul_f64(overlap.div_duration_f64(span)) - } - }) - .sum(); - occupied + apportioned_small_polls - } - - /// The fraction of the interval the foreground spent working, in `0.0..=1.0`. - pub fn busy_fraction(&self) -> f64 { - let interval = self.interval_end().duration_since(self.interval_start); - if interval.is_zero() { - return 1.0; - } - (self.occupancy().div_duration_f64(interval)).min(1.0) - } -} - -#[derive(Clone)] -pub(crate) struct ForegroundRunnableCounter(Arc); - -impl ForegroundRunnableCounter { - fn new() -> Self { - Self(Arc::new(AtomicUsize::new(0))) - } - - pub(crate) fn queued(&self) { - self.0.fetch_add(1, Ordering::Release); - } - - fn finished(&self) { - let _ = self - .0 - .fetch_update(Ordering::AcqRel, Ordering::Acquire, |count| { - count.checked_sub(1) - }); - } - - fn has_runnables(&self) -> bool { - self.0.load(Ordering::Acquire) > 0 - } -} - -struct ForegroundJournalWriter { - foreground_runnables: ForegroundRunnableCounter, - publisher: JournalPublisher, - turn_depth: usize, - pending_frames: HashMap, - retained_since_boundary: bool, - small_polls: Option, -} - -impl ForegroundJournalWriter { - fn new(foreground_runnables: ForegroundRunnableCounter, publisher: JournalPublisher) -> Self { - Self { - foreground_runnables, - publisher, - turn_depth: 0, - pending_frames: HashMap::new(), - retained_since_boundary: false, - small_polls: None, - } - } - - fn begin_turn(&mut self) { - self.turn_depth += 1; - } - - // Idle boundaries require a retained event since the last boundary, not - // merely folded sub-floor polls. Sporadic wake-ups (timers, file - // watchers) leave the foreground idle after every tiny poll; a boundary - // for each would re-admit to the ring the very polls the fold keeps out - // of it, wrapping the ring within seconds. Folded-only work is discarded - // at true idle so unrelated wake-ups cannot accumulate toward a later - // frame budget. Polls remain folded while a frame or runnable is pending. - fn end_turn(&mut self, ended_at: Instant) { - let Some(turn_depth) = self.turn_depth.checked_sub(1) else { - debug_assert!(false, "foreground turn must be begun before it ends"); - return; - }; - self.turn_depth = turn_depth; - if self.turn_depth > 0 - || self.foreground_runnables.has_runnables() - || self.has_unexpired_pending_frame(ended_at) - { - return; - } - if !self.retained_since_boundary { - self.small_polls = None; - return; - } - - self.record_entry(ForegroundJournalEntry::Boundary(IntervalBoundary::Idle { - ended_at, - })); - } - - fn has_unexpired_pending_frame(&mut self, now: Instant) -> bool { - if self.pending_frames.is_empty() { - return false; - } - self.pending_frames - .retain(|_, dirty_at| now.saturating_duration_since(*dirty_at) < FRAME_DEADLINE); - !self.pending_frames.is_empty() - } - - fn fold_small_poll(&mut self, timing: TaskTiming) { - let flush = self.small_polls.get_or_insert(SmallPollFlush { - summary: PollSummary::default(), - since: timing.start, - until: timing.end.0, - }); - flush.summary.count += 1; - flush.summary.total += timing.poll_duration(); - flush.since = flush.since.min(timing.start); - flush.until = flush.until.max(timing.end.0); - } - - fn take_small_polls(&mut self) -> Option { - self.small_polls.take() - } - - fn record_event(&mut self, event: ForegroundEvent) { - self.retained_since_boundary = true; - self.record_entry(ForegroundJournalEntry::Event(event)); - } - - fn record_entry(&mut self, entry: ForegroundJournalEntry) { - let small_polls = self - .take_small_polls() - .map(ForegroundEvent::SmallPolls) - .map(ForegroundJournalEntry::Event); - self.publisher - .publish([small_polls, Some(entry)].into_iter().flatten()); - if matches!(entry, ForegroundJournalEntry::Boundary(_)) { - self.retained_since_boundary = false; - } - } - - fn record_frame_state(&mut self, change: FrameStateChange) { - self.record_entry(ForegroundJournalEntry::FrameState(change)); - } - - fn record_frame_pending(&mut self, window_id: WindowId, dirty_at: Instant) { - let should_record = match self.pending_frames.get(&window_id) { - Some(previous_dirty_at) => { - dirty_at.saturating_duration_since(*previous_dirty_at) >= FRAME_DEADLINE - } - None => true, - }; - if !should_record { - return; - } - - self.pending_frames.insert(window_id, dirty_at); - self.record_frame_state(FrameStateChange::Pending { - window_id, - dirty_at, - }); - } - - fn record_window_closed(&mut self, window_id: WindowId, at: Instant) { - self.pending_frames.remove(&window_id); - self.record_frame_state(FrameStateChange::Closed { window_id, at }); - } - - fn record_present(&mut self, timing: PresentTiming, frame: Option) { - match frame { - Some(frame) => { - self.pending_frames.remove(&frame.window_id); - self.record_entry(ForegroundJournalEntry::Boundary( - IntervalBoundary::Presented(PresentedFrame { - frame, - presentation: timing, - }), - )); - } - None => { - self.pending_frames.remove(&timing.window_id); - self.record_event(ForegroundEvent::Present(timing)); - } - } - } -} - -thread_local! { - static FOREGROUND_RUNNABLES: ForegroundRunnableCounter = ForegroundRunnableCounter::new(); - static FOREGROUND_JOURNAL: RefCell> = const { RefCell::new(None) }; -} - -pub(crate) fn foreground_runnable_counter() -> ForegroundRunnableCounter { - FOREGROUND_RUNNABLES.with(Clone::clone) -} - -/// Starts journaling on the calling thread. Called once by `App` construction -/// on the main thread; every other thread's recording calls are no-ops. -/// Idempotent so that multiple `App`s on one thread (tests) share one journal. -pub(crate) fn install_foreground_journal() -> ForegroundJournal { - let foreground_runnables = foreground_runnable_counter(); - FOREGROUND_JOURNAL.with(|journal| { - let mut journal = journal.borrow_mut(); - if let Some(journal) = journal.as_ref() { - return ForegroundJournal { - ring: Arc::clone(&journal.publisher.ring), - }; - } - - let (handle, publisher) = - ForegroundJournal::new(MAX_JOURNAL_ENTRIES, MAX_PENDING_JOURNAL_ENTRIES); - *journal = Some(ForegroundJournalWriter::new( - foreground_runnables, - publisher, - )); - handle - }) -} - -/// Restores the previous foreground journal on drop, so a test's writer -/// (turn depth, pending frames, retained-since-boundary state) never leaks -/// into a later test that happens to reuse the same test-harness thread. -#[cfg(test)] -pub(crate) struct TestForegroundJournalGuard { - previous: Option, - _not_send: std::marker::PhantomData>, -} - -#[cfg(test)] -impl Drop for TestForegroundJournalGuard { - fn drop(&mut self) { - FOREGROUND_JOURNAL.with(|journal| { - *journal.borrow_mut() = self.previous.take(); - }); - } -} - -/// Installs a fresh journal for the duration of a test, isolated from -/// whatever a previous test left on this thread. Used by tests outside this -/// module (e.g. `bench_context`) that need to observe journal entries -/// without interference from the shared per-thread journal. -#[cfg(test)] -pub(crate) fn install_test_foreground_journal( - capacity: usize, - pending_capacity: usize, -) -> (ForegroundJournal, TestForegroundJournalGuard) { - let foreground_runnables = foreground_runnable_counter(); - let (handle, publisher) = ForegroundJournal::new(capacity, pending_capacity); - let previous = FOREGROUND_JOURNAL.with(|journal| { - journal.borrow_mut().replace(ForegroundJournalWriter::new( - foreground_runnables, - publisher, - )) - }); - ( - handle, - TestForegroundJournalGuard { - previous, - _not_send: std::marker::PhantomData, - }, - ) -} - -fn with_journal(f: impl FnOnce(&mut ForegroundJournalWriter)) { - FOREGROUND_JOURNAL.with(|journal| { - if let Some(journal) = journal.borrow_mut().as_mut() { - f(journal); - } - }); -} - -// TODO(gpui-profiler): the turn brackets in the dispatchers and -// WindowProfiler are bare begin/end call pairs rather than uses of this -// guard. A caught unwind between a pair leaves `turn_depth` (and potentially -// the runnable counter) permanently unbalanced, which silently disables -// idle boundaries for the rest of the process. Zed aborts on panics so -// this is latent today, but gpui-as-a-library callers may catch unwinds; -// route all bracketing through this guard. -pub(crate) struct ForegroundTurnGuard; - -impl Drop for ForegroundTurnGuard { - fn drop(&mut self) { - end_foreground_turn(); - } -} - -pub(crate) fn foreground_turn() -> ForegroundTurnGuard { - begin_foreground_turn(); - ForegroundTurnGuard -} - -pub(crate) fn begin_foreground_turn() { - with_journal(ForegroundJournalWriter::begin_turn); -} - -pub(crate) fn end_foreground_turn() { - with_journal(|journal| journal.end_turn(Instant::now())); -} - -pub(crate) fn record_task_poll(timing: TaskTiming) { - FOREGROUND_RUNNABLES.with(ForegroundRunnableCounter::finished); - with_journal(|journal| { - if timing.poll_duration() >= TASK_POLL_FLOOR { - journal.record_event(ForegroundEvent::TaskPoll(timing)); - } else { - journal.fold_small_poll(timing); - } - journal.end_turn(timing.end.0); - }); -} - -pub(crate) fn record_action(timing: ActionTiming) { - with_journal(|journal| journal.record_event(ForegroundEvent::Action(timing))); -} - -pub(crate) fn record_input(timing: InputTiming) { - with_journal(|journal| journal.record_event(ForegroundEvent::Input(timing))); -} - -pub(crate) fn record_draw(timing: FrameTiming) { - with_journal(|journal| journal.record_event(ForegroundEvent::Draw(timing))); -} - -pub(crate) fn record_present(timing: PresentTiming, frame: Option) { - with_journal(|journal| journal.record_present(timing, frame)); -} - -pub(crate) fn record_frame_pending(window_id: WindowId, dirty_at: Instant) { - with_journal(|journal| journal.record_frame_pending(window_id, dirty_at)); -} - -pub(crate) fn record_window_closed(window_id: WindowId) { - let at = Instant::now(); - with_journal(|journal| journal.record_window_closed(window_id, at)); -} - -const SLOT_WRITER: usize = 1 << (usize::BITS - 1); -const SLOT_READER_MASK: usize = !SLOT_WRITER; -const EMPTY_SEQUENCE: u64 = u64::MAX; - -struct JournalSlot { - users: AtomicUsize, - sequence: AtomicU64, - entry: UnsafeCell>, -} - -// `users` ensures the entry is only read while no writer owns the slot and is -// only written while no readers own it. -unsafe impl Sync for JournalSlot {} - -impl JournalSlot { - fn new() -> Self { - Self { - users: AtomicUsize::new(0), - sequence: AtomicU64::new(EMPTY_SEQUENCE), - entry: UnsafeCell::new(MaybeUninit::uninit()), - } - } - - fn try_publish(&self, sequence: u64, entry: ForegroundJournalEntry) -> bool { - if self - .users - .compare_exchange(0, SLOT_WRITER, Ordering::Acquire, Ordering::Relaxed) - .is_err() - { - return false; - } - - // SAFETY: setting SLOT_WRITER from zero gives this writer exclusive - // access to the slot until the Release store below. - unsafe { - (*self.entry.get()).write(entry); - } - self.sequence.store(sequence, Ordering::Relaxed); - self.users.store(0, Ordering::Release); - true - } - - fn try_read(&self, expected_sequence: u64) -> Option { - let _guard = JournalSlotReadGuard::try_new(self)?; - if self.sequence.load(Ordering::Relaxed) != expected_sequence { - return None; - } - - // SAFETY: a matching sequence means the slot was initialized for this - // logical entry, and `guard` prevents the writer from overwriting it - // until after the Copy. - Some(unsafe { *(*self.entry.get()).assume_init_ref() }) - } - - fn try_add_reader(&self) -> bool { - let mut users = self.users.load(Ordering::Relaxed); - loop { - if users & SLOT_WRITER != 0 || users == SLOT_READER_MASK { - return false; - } - match self.users.compare_exchange_weak( - users, - users + 1, - Ordering::Acquire, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(updated_users) => users = updated_users, - } - } - } - - fn remove_reader(&self) { - let previous = self.users.fetch_sub(1, Ordering::Release); - debug_assert!( - previous > 0 && previous & SLOT_WRITER == 0, - "invalid slot reader state: {previous:#x}" - ); - } -} - -struct JournalSlotReadGuard<'a> { - slot: &'a JournalSlot, -} - -impl<'a> JournalSlotReadGuard<'a> { - fn try_new(slot: &'a JournalSlot) -> Option { - if slot.try_add_reader() { - Some(Self { slot }) - } else { - None - } - } -} - -impl Drop for JournalSlotReadGuard<'_> { - fn drop(&mut self) { - self.slot.remove_reader(); - } -} - -struct JournalRing { - slots: Box<[JournalSlot]>, - finalized: AtomicU64, - offered: AtomicU64, -} - -impl JournalRing { - fn new(capacity: usize) -> Self { - let capacity = capacity.max(1); - Self { - slots: (0..capacity).map(|_| JournalSlot::new()).collect(), - finalized: AtomicU64::new(0), - offered: AtomicU64::new(0), - } - } - - fn capacity(&self) -> usize { - self.slots.len() - } - - fn try_publish(&self, sequence: u64, entry: ForegroundJournalEntry) -> bool { - let index = (sequence % self.slots.len() as u64) as usize; - self.slots[index].try_publish(sequence, entry) - } - - fn read(&self, sequence: u64) -> Option { - let index = (sequence % self.slots.len() as u64) as usize; - self.slots[index].try_read(sequence) - } -} - -struct PendingJournalEntry { - sequence: u64, - entry: ForegroundJournalEntry, -} - -struct JournalPublisher { - ring: Arc, - next_sequence: u64, - pending: VecDeque, - dropped_after_pending: u64, - pending_capacity: usize, -} - -impl JournalPublisher { - fn new(ring: Arc, pending_capacity: usize) -> Self { - Self { - ring, - next_sequence: 0, - pending: VecDeque::with_capacity(pending_capacity), - dropped_after_pending: 0, - pending_capacity, - } - } - - fn publish(&mut self, entries: impl IntoIterator) { - for entry in entries { - self.flush_pending(); - self.publish_one(entry); - } - self.flush_pending(); - } - - fn publish_one(&mut self, entry: ForegroundJournalEntry) { - let sequence = self.next_sequence; - self.next_sequence += 1; - self.ring - .offered - .store(self.next_sequence, Ordering::Release); - - if !self.pending.is_empty() || self.dropped_after_pending > 0 { - if self.dropped_after_pending == 0 && self.pending.len() < self.pending_capacity { - self.pending - .push_back(PendingJournalEntry { sequence, entry }); - } else { - self.dropped_after_pending += 1; - } - return; - } - - if self.ring.try_publish(sequence, entry) { - self.ring.finalized.store(sequence + 1, Ordering::Release); - } else if self.pending_capacity > 0 { - self.pending - .push_back(PendingJournalEntry { sequence, entry }); - } else { - self.dropped_after_pending = 1; - } - } - - fn flush_pending(&mut self) { - while let Some(pending) = self.pending.front() { - if !self.ring.try_publish(pending.sequence, pending.entry) { - return; - } - let sequence = pending.sequence; - self.pending.pop_front(); - self.ring.finalized.store(sequence + 1, Ordering::Release); - } - - if self.dropped_after_pending > 0 { - self.ring - .finalized - .store(self.next_sequence, Ordering::Release); - self.dropped_after_pending = 0; - } - } -} - -/// A cloneable handle to one foreground journal stream. -/// -/// Each collector has an independent cursor. Collectors briefly pin one slot -/// at a time and never block recording; entries they could not observe are -/// reported as discontinuities. Apps on the same foreground thread share the -/// stream. -#[derive(Clone)] -pub struct ForegroundJournal { - ring: Arc, -} - -impl ForegroundJournal { - fn new(capacity: usize, pending_capacity: usize) -> (Self, JournalPublisher) { - let ring = Arc::new(JournalRing::new(capacity)); - ( - Self { - ring: Arc::clone(&ring), - }, - JournalPublisher::new(ring, pending_capacity), - ) - } - - /// Creates an independent collector that observes entries offered after - /// this call. - pub fn collector(&self) -> ForegroundJournalCollector { - ForegroundJournalCollector { - cursor: self.ring.offered.load(Ordering::Acquire), - ring: Arc::clone(&self.ring), - } - } -} - -/// Entries returned by one [`ForegroundJournalCollector::collect_unseen`] call. -#[derive(Debug, Default)] -pub struct DrainedEntries { - /// Journal entries recorded since the previous drain, in recording order, - /// including synthetic [`ForegroundJournalEntry::Discontinuity`] markers at - /// unavailable logical positions. - pub entries: Vec, - /// Entries unavailable to this collector because they were overwritten or - /// could not be published after the fixed pending queue filled. This is the - /// aggregate of the discontinuity markers in `entries`. - pub lost: u64, -} - -/// Reads the foreground stream, tracking a cursor so each call to -/// [`Self::collect_unseen`] returns only entries recorded since the previous -/// call. Independent collectors do not affect each other. -pub struct ForegroundJournalCollector { - cursor: u64, - ring: Arc, -} - -impl ForegroundJournalCollector { - /// Returns entries recorded since the previous call (or since the - /// collector was created), reporting how many were unavailable before this - /// drain observed them. - pub fn collect_unseen(&mut self) -> DrainedEntries { - let end = self.ring.finalized.load(Ordering::Acquire); - if self.cursor >= end { - return DrainedEntries::default(); - } - let retained_start = end.saturating_sub(self.ring.capacity() as u64); - let mut lost = retained_start.saturating_sub(self.cursor); - self.cursor = self.cursor.max(retained_start); - let mut entries = Vec::with_capacity((end - self.cursor) as usize + usize::from(lost > 0)); - if lost > 0 { - entries.push(ForegroundJournalEntry::Discontinuity { lost }); - } - - while self.cursor < end { - if let Some(entry) = self.ring.read(self.cursor) { - entries.push(entry); - } else { - lost += 1; - match entries.last_mut() { - Some(ForegroundJournalEntry::Discontinuity { lost }) => *lost += 1, - _ => entries.push(ForegroundJournalEntry::Discontinuity { lost: 1 }), - } - } - self.cursor += 1; - } - - DrainedEntries { entries, lost } - } -} - -/// A pure state machine that groups foreground journal entries into -/// [`FrameSnapshot`]s. -/// -/// Feed it drained entries in recording order. Work is carried across calls -/// until an explicit [`IntervalBoundary`] arrives. Idle time before an -/// interval's first event is excluded without relying on an elapsed-time -/// heuristic. -#[derive(Debug)] -pub struct IntervalSealer { - interval_start: Instant, - events: Vec, - small_polls: Vec, - dropped_events: u64, - journal_discontinuous: bool, -} - -impl IntervalSealer { - /// Creates a sealer whose first interval starts at `start` (typically - /// the moment the consumer's collector was created). - pub fn new(start: Instant) -> Self { - Self { - interval_start: start, - events: Vec::new(), - small_polls: Vec::new(), - dropped_events: 0, - journal_discontinuous: false, - } - } - - /// Accounts for a loss supplied outside a collector drain. Collector losses - /// are already represented by ordered discontinuity entries. - pub fn note_lost(&mut self, lost: u64) { - self.dropped_events += lost; - self.journal_discontinuous |= lost > 0; - } - - /// Processes a batch of drained entries, returning the snapshots completed - /// by explicit boundaries. Work without a following boundary is carried - /// over to subsequent calls. - pub fn push_entries( - &mut self, - entries: impl IntoIterator, - ) -> Vec { - let mut snapshots = Vec::new(); - for entry in entries { - match entry { - ForegroundJournalEntry::Event(event) => { - if self.is_empty() { - self.interval_start = self.interval_start.max(event.start_time()); - } - match event { - ForegroundEvent::SmallPolls(flush) => self.push_small_polls(flush), - event => self.push_event(event), - } - } - ForegroundJournalEntry::Boundary(boundary) => { - if let IntervalBoundary::Presented(presented) = boundary { - if self.is_empty() { - self.interval_start = self - .interval_start - .max(presented.presentation.present_start); - } - self.push_event(ForegroundEvent::Present(presented.presentation)); - } - if self.is_empty() { - self.interval_start = self.interval_start.max(boundary.end_time()); - } else { - snapshots.push(self.seal(boundary)); - } - } - // Pending-frame state gates boundaries on the writer side; - // the entries remain in the stream for consumers that want - // dirty timing, but the sealer has no use for them. - ForegroundJournalEntry::FrameState(_) => {} - ForegroundJournalEntry::Discontinuity { lost } => self.note_lost(lost), - } - } - snapshots - } - - fn is_empty(&self) -> bool { - self.events.is_empty() - && self.small_polls.is_empty() - && self.dropped_events == 0 - && !self.journal_discontinuous - } - - fn push_event(&mut self, event: ForegroundEvent) { - if self.events.len() >= MAX_INTERVAL_EVENTS { - self.dropped_events += 1; - } else { - self.events.push(event); - } - } - - fn push_small_polls(&mut self, flush: SmallPollFlush) { - if self.small_polls.len() >= MAX_INTERVAL_EVENTS - && let Some(last) = self.small_polls.last_mut() - { - // Degrade gracefully at the cap: widen the last flush's span - // instead of dropping poll time, at the cost of coarser - // apportioning. - last.summary.add(flush.summary); - last.since = last.since.min(flush.since); - last.until = last.until.max(flush.until); - } else { - self.small_polls.push(flush); - } - } - - fn seal(&mut self, boundary: IntervalBoundary) -> FrameSnapshot { - let ended = boundary.end_time(); - let snapshot = FrameSnapshot { - interval_start: self.interval_start, - boundary, - events: std::mem::take(&mut self.events), - small_polls: std::mem::take(&mut self.small_polls), - dropped_events: std::mem::take(&mut self.dropped_events), - journal_discontinuous: std::mem::take(&mut self.journal_discontinuous), - }; - self.interval_start = ended; - snapshot - } -} - -#[cfg(test)] -mod tests { - use proptest::prelude::*; - use scheduler::SpawnTime; - - use super::*; - use crate::{WindowId, profiler::YieldTime}; - - #[test] - fn draw_waits_for_its_presentation_boundary() { - let start = Instant::now(); - let input = InputTiming { - kind: "test", - start: start + Duration::from_millis(1), - end: start + Duration::from_millis(2), - caused_invalidation: true, - }; - let frame = FrameTiming { - window_id: WindowId::from(1), - dirty_at: Some(input.start), - invalidations: 1, - draw_start: start + Duration::from_millis(3), - draw_end: start + Duration::from_millis(4), - }; - let presentation = PresentTiming { - window_id: frame.window_id, - present_start: start + Duration::from_millis(5), - present_end: start + Duration::from_millis(6), - animation_interval: None, - }; - let mut sealer = IntervalSealer::new(start); - - let snapshots = sealer.push_entries([ - ForegroundJournalEntry::Event(ForegroundEvent::Input(input)), - ForegroundJournalEntry::Event(ForegroundEvent::Draw(frame)), - ]); - assert!(snapshots.is_empty()); - - let snapshots = sealer.push_entries([ForegroundJournalEntry::Boundary( - IntervalBoundary::Presented(PresentedFrame { - frame, - presentation, - }), - )]); - let [snapshot] = snapshots.as_slice() else { - panic!("expected one presentation-sealed snapshot, got {snapshots:?}"); - }; - assert_eq!(snapshot.interval_start, input.start); - assert_eq!(snapshot.interval_end(), presentation.present_end); - assert_eq!(snapshot.events.len(), 3); - assert!(matches!( - snapshot.events.last(), - Some(ForegroundEvent::Present(timing)) - if timing.present_duration() == Duration::from_millis(1) - )); - assert_eq!( - match snapshot.boundary { - IntervalBoundary::Presented(presented) => { - presented.dirty_to_present_duration() - } - IntervalBoundary::Idle { .. } => None, - }, - Some(Duration::from_millis(5)) - ); - } - - #[test] - fn outermost_turn_seals_no_frame_work_when_idle() { - let start = Instant::now(); - let counter = ForegroundRunnableCounter::new(); - let (mut journal, mut collector) = test_journal(counter); - let events = [ - ForegroundEvent::Input(InputTiming { - kind: "test", - start, - end: start + Duration::from_millis(1), - caused_invalidation: false, - }), - ForegroundEvent::Action(ActionTiming { - name: "test.action", - start: start + Duration::from_millis(2), - end: start + Duration::from_millis(3), - }), - ForegroundEvent::TaskPoll(task_timing( - start + Duration::from_millis(4), - start + Duration::from_millis(5), - )), - ]; - - for event in events { - journal.begin_turn(); - journal.record_event(event); - journal.end_turn(event.end_time()); - } - - let entries = collector.collect_unseen().entries; - for event in events { - assert!(entries.iter().any(|entry| { - matches!( - entry, - ForegroundJournalEntry::Boundary(IntervalBoundary::Idle { ended_at }) - if *ended_at == event.end_time() - ) - })); - } - } - - #[test] - fn nested_turns_only_seal_after_the_outermost_turn() { - let start = Instant::now(); - let (mut journal, mut collector) = test_journal(ForegroundRunnableCounter::new()); - let action = ForegroundEvent::Action(ActionTiming { - name: "test.action", - start, - end: start + Duration::from_millis(1), - }); - let input = ForegroundEvent::Input(InputTiming { - kind: "test", - start, - end: start + Duration::from_millis(2), - caused_invalidation: false, - }); - - journal.begin_turn(); - journal.begin_turn(); - journal.record_event(action); - journal.end_turn(action.end_time()); - assert!(!has_boundary_at( - &collector.collect_unseen().entries, - action.end_time() - )); - - journal.record_event(input); - journal.end_turn(input.end_time()); - assert!(collector.collect_unseen().entries.iter().any(|entry| { - matches!( - entry, - ForegroundJournalEntry::Boundary(IntervalBoundary::Idle { ended_at }) - if *ended_at == input.end_time() - ) - })); - } - - #[test] - fn an_immediately_ready_runnable_prevents_an_idle_boundary_between_polls() { - let start = Instant::now(); - let counter = ForegroundRunnableCounter::new(); - let (mut journal, mut collector) = test_journal(counter.clone()); - counter.queued(); - counter.queued(); - - let first = ForegroundEvent::TaskPoll(task_timing(start, start + Duration::from_millis(1))); - journal.begin_turn(); - journal.record_event(first); - counter.finished(); - journal.end_turn(first.end_time()); - assert!(!has_boundary_at( - &collector.collect_unseen().entries, - first.end_time() - )); - - let second = ForegroundEvent::TaskPoll(task_timing( - start + Duration::from_millis(2), - start + Duration::from_millis(3), - )); - journal.begin_turn(); - journal.record_event(second); - counter.finished(); - journal.end_turn(second.end_time()); - assert!(collector.collect_unseen().entries.iter().any(|entry| { - matches!( - entry, - ForegroundJournalEntry::Boundary(IntervalBoundary::Idle { ended_at }) - if *ended_at == second.end_time() - ) - })); - } - - #[test] - fn a_pending_frame_prevents_idle_until_presentation() { - let start = Instant::now(); - let window_id = WindowId::from(0xD17A); - let (mut journal, mut collector) = test_journal(ForegroundRunnableCounter::new()); - journal.record_frame_pending(window_id, start); - journal.begin_turn(); - journal.record_event(ForegroundEvent::Input(InputTiming { - kind: "test", - start, - end: start + Duration::from_millis(1), - caused_invalidation: true, - })); - let input_end = start + Duration::from_millis(1); - journal.end_turn(input_end); - assert!(!has_boundary_at( - &collector.collect_unseen().entries, - input_end - )); - - let frame = frame_timing(window_id, start, start + Duration::from_millis(2)); - let presentation = presentation_timing(window_id, start + Duration::from_millis(3)); - journal.record_present(presentation, Some(frame)); - assert!(collector.collect_unseen().entries.iter().any(|entry| { - matches!( - entry, - ForegroundJournalEntry::Boundary(IntervalBoundary::Presented(presented)) - if presented.frame.window_id == window_id - ) - })); - } - - #[test] - fn a_present_without_a_draw_clears_the_window_pending_state() { - let start = Instant::now(); - let presented_at = start + Duration::from_millis(1); - let window_id = WindowId::from(0xD17B); - let (mut journal, mut collector) = test_journal(ForegroundRunnableCounter::new()); - journal.record_frame_pending(window_id, start); - journal.begin_turn(); - journal.record_present(presentation_timing(window_id, presented_at), None); - journal.end_turn(presented_at); - - let entries = collector.collect_unseen().entries; - assert!(entries.iter().any(|entry| { - matches!( - entry, - ForegroundJournalEntry::Event(ForegroundEvent::Present(timing)) - if timing.window_id == window_id - ) - })); - assert!(has_boundary_at(&entries, presented_at)); - } - - /// A frame that outlives [`FRAME_DEADLINE`] no longer seals an interval - /// of its own: the work that starved it and its eventual presentation - /// stay in one interval, preserving the dirty-to-present association. - #[test] - fn a_presentation_after_the_deadline_seals_the_whole_interval() { - let start = Instant::now(); - let window_id = WindowId::from(0xDEA1); - let presented_at = start + FRAME_DEADLINE + Duration::from_millis(250); - let mut sealer = IntervalSealer::new(start); - let snapshots = sealer.push_entries([ - pending_frame(window_id, start), - input_entry(start, start + Duration::from_millis(20)), - ForegroundJournalEntry::Boundary(presented_boundary(window_id, start, presented_at)), - ]); - let [snapshot] = snapshots.as_slice() else { - panic!("expected one presented snapshot, got {snapshots:?}"); - }; - assert!(matches!( - snapshot.boundary, - IntervalBoundary::Presented(presented) - if presented.frame.window_id == window_id - && presented.dirty_to_present_duration() - == Some(presented_at.duration_since(start)) - )); - assert_eq!(snapshot.interval_end(), presented_at); - assert_eq!(snapshot.events.len(), 2); - } - - /// Frame expiry unblocks idle boundaries on the writer side: retained - /// work after the deadline seals at its turn's end even though the window - /// never presented. - #[test] - fn an_expired_frame_no_longer_blocks_idle_boundaries() { - let start = Instant::now(); - let window_id = WindowId::from(0xDEA2); - let (mut journal, mut collector) = test_journal(ForegroundRunnableCounter::new()); - journal.record_frame_pending(window_id, start); - - let blocked_end = start + Duration::from_millis(20); - journal.begin_turn(); - journal.record_event(ForegroundEvent::Input(InputTiming { - kind: "test", - start, - end: blocked_end, - caused_invalidation: true, - })); - journal.end_turn(blocked_end); - assert!(!has_boundary_at( - &collector.collect_unseen().entries, - blocked_end - )); - - let unblocked_end = start + FRAME_DEADLINE + Duration::from_millis(1); - journal.begin_turn(); - journal.record_event(ForegroundEvent::Input(InputTiming { - kind: "test", - start: start + FRAME_DEADLINE, - end: unblocked_end, - caused_invalidation: false, - })); - journal.end_turn(unblocked_end); - assert!(has_boundary_at( - &collector.collect_unseen().entries, - unblocked_end - )); - } - - /// Closing a window clears its pending frame, so idle boundaries resume - /// without waiting for the deadline. - #[test] - fn closing_a_window_unblocks_idle_boundaries() { - let start = Instant::now(); - let window_id = WindowId::from(0xDEA7); - let (mut journal, mut collector) = test_journal(ForegroundRunnableCounter::new()); - journal.record_frame_pending(window_id, start); - journal.record_window_closed(window_id, start + Duration::from_millis(1)); - - let event_end = start + Duration::from_millis(2); - journal.begin_turn(); - journal.record_event(ForegroundEvent::Input(InputTiming { - kind: "test", - start: start + Duration::from_millis(1), - end: event_end, - caused_invalidation: false, - })); - journal.end_turn(event_end); - assert!(has_boundary_at( - &collector.collect_unseen().entries, - event_end - )); - } - - /// One window presenting must not unblock idle boundaries while another - /// window's unexpired frame is still pending. - #[test] - fn presenting_one_window_does_not_clear_another_pending_window() { - let start = Instant::now(); - let first_window = WindowId::from(0xDEA5); - let second_window = WindowId::from(0xDEA6); - let (mut journal, mut collector) = test_journal(ForegroundRunnableCounter::new()); - journal.record_frame_pending(first_window, start); - journal.record_frame_pending(second_window, start + Duration::from_millis(100)); - journal.record_present( - presentation_timing(first_window, start + Duration::from_millis(500)), - Some(frame_timing( - first_window, - start, - start + Duration::from_millis(500), - )), - ); - - let event_end = start + Duration::from_millis(620); - journal.begin_turn(); - journal.record_event(ForegroundEvent::Input(InputTiming { - kind: "test", - start: start + Duration::from_millis(600), - end: event_end, - caused_invalidation: false, - })); - journal.end_turn(event_end); - - let entries = collector.collect_unseen().entries; - assert!(entries.iter().any(|entry| { - matches!( - entry, - ForegroundJournalEntry::Boundary(IntervalBoundary::Presented(presented)) - if presented.frame.window_id == first_window - ) - })); - assert!(!has_boundary_at(&entries, event_end)); - } - - #[test] - fn small_polls_are_flushed_immediately_before_a_retained_event() { - let start = Instant::now(); - let first = task_timing( - start + Duration::from_millis(1), - start + Duration::from_millis(1) + Duration::from_micros(20), - ); - let second = task_timing( - start + Duration::from_millis(3), - start + Duration::from_millis(3) + Duration::from_micros(30), - ); - let input = InputTiming { - kind: "test", - start: start + Duration::from_millis(5), - end: start + Duration::from_millis(6), - caused_invalidation: false, - }; - let (mut journal, mut collector) = test_journal(ForegroundRunnableCounter::new()); - - journal.fold_small_poll(first); - journal.fold_small_poll(second); - journal.record_event(ForegroundEvent::Input(input)); - - let drained = collector.collect_unseen(); - let input_index = drained - .entries - .iter() - .position(|entry| { - matches!( - entry, - ForegroundJournalEntry::Event(ForegroundEvent::Input(timing)) - if timing.start == input.start - ) - }) - .expect("input event should be retained"); - let Some(ForegroundJournalEntry::Event(ForegroundEvent::SmallPolls(flush))) = input_index - .checked_sub(1) - .and_then(|index| drained.entries.get(index)) - else { - panic!("small-poll summary should immediately precede the input event"); - }; - assert_eq!(flush.summary.count, 2); - assert_eq!(flush.summary.total, Duration::from_micros(50)); - assert_eq!(flush.since, first.start); - assert_eq!(flush.until, second.end.0); - } - - #[test] - fn small_polls_are_flushed_before_frame_state_changes() { - let start = Instant::now(); - let window_id = WindowId::from(0xDEA9); - let poll = task_timing(start, start + Duration::from_micros(50)); - let (mut journal, mut collector) = test_journal(ForegroundRunnableCounter::new()); - journal.fold_small_poll(poll); - journal.record_frame_pending(window_id, poll.end.0); - - let entries = collector.collect_unseen().entries; - let pending_index = entries - .iter() - .position(|entry| { - matches!( - entry, - ForegroundJournalEntry::FrameState(FrameStateChange::Pending { - window_id: pending_window, - .. - }) if *pending_window == window_id - ) - }) - .expect("pending frame should be retained"); - assert!(matches!( - pending_index - .checked_sub(1) - .and_then(|index| entries.get(index)), - Some(ForegroundJournalEntry::Event(ForegroundEvent::SmallPolls(flush))) - if flush.since == poll.start && flush.until == poll.end.0 - )); - } - - /// Sporadic wake-ups (a tiny folded poll after which the foreground goes - /// idle, repeatedly) must write nothing to the ring and must not accumulate - /// toward a later retained interval. - #[test] - fn sparse_small_polls_are_discarded_when_the_foreground_returns_to_idle() { - let start = Instant::now(); - let (mut journal, mut collector) = test_journal(ForegroundRunnableCounter::new()); - let mut tiny_wake = |ended_at: Instant| { - journal.begin_turn(); - journal.fold_small_poll(task_timing(ended_at - Duration::from_micros(50), ended_at)); - journal.end_turn(ended_at); - }; - - for second in 1..=160 { - tiny_wake(start + Duration::from_secs(second)); - } - let quiet = collector.collect_unseen().entries; - assert!(quiet.is_empty()); - - // A later retained event seals normally without inheriting the earlier - // folded-only wake-ups. - let retained = ForegroundEvent::TaskPoll(task_timing( - start + Duration::from_secs(161), - start + Duration::from_secs(161) + Duration::from_millis(1), - )); - journal.begin_turn(); - journal.record_event(retained); - journal.end_turn(retained.end_time()); - let entries = collector.collect_unseen().entries; - assert!(has_boundary_at(&entries, retained.end_time())); - assert!(!entries.iter().any(|entry| matches!( - entry, - ForegroundJournalEntry::Event(ForegroundEvent::SmallPolls(_)) - ))); - } - - #[test] - fn note_lost_is_reported_on_the_next_snapshot_only() { - let start = Instant::now(); - let mut sealer = IntervalSealer::new(start); - sealer.note_lost(7); - - let snapshots = sealer.push_entries([ - input_entry(start, start + Duration::from_millis(1)), - ForegroundJournalEntry::Boundary(IntervalBoundary::Idle { - ended_at: start + Duration::from_millis(1), - }), - input_entry( - start + Duration::from_millis(2), - start + Duration::from_millis(3), - ), - ForegroundJournalEntry::Boundary(IntervalBoundary::Idle { - ended_at: start + Duration::from_millis(3), - }), - ]); - - assert_eq!(snapshots.len(), 2); - assert_eq!(snapshots[0].dropped_events, 7); - assert_eq!(snapshots[1].dropped_events, 0); - } - - /// Ring losses must be able to surface even when every retained entry was - /// among the losses: a loss-only interval still seals at the next - /// boundary rather than sliding away as empty. - #[test] - fn losses_alone_seal_a_snapshot_at_the_next_boundary() { - let start = Instant::now(); - let mut sealer = IntervalSealer::new(start); - sealer.note_lost(3); - - let snapshots = - sealer.push_entries([ForegroundJournalEntry::Boundary(IntervalBoundary::Idle { - ended_at: start + Duration::from_millis(1), - })]); - - assert_eq!(snapshots.len(), 1); - assert_eq!(snapshots[0].dropped_events, 3); - assert!(snapshots[0].events.is_empty()); - } - - #[test] - fn interval_event_cap_counts_overflowing_events() { - let start = Instant::now(); - let mut sealer = IntervalSealer::new(start); - let overflow = 5; - let mut entries: Vec = (0..(MAX_INTERVAL_EVENTS + overflow) as u64) - .map(|i| { - input_entry( - start + Duration::from_micros(i), - start + Duration::from_micros(i + 1), - ) - }) - .collect(); - entries.push(ForegroundJournalEntry::Boundary(IntervalBoundary::Idle { - ended_at: start + Duration::from_micros((MAX_INTERVAL_EVENTS + overflow) as u64 + 1), - })); - - let snapshots = sealer.push_entries(entries); - - assert_eq!(snapshots.len(), 1); - assert_eq!(snapshots[0].events.len(), MAX_INTERVAL_EVENTS); - assert_eq!(snapshots[0].dropped_events, overflow as u64); - } - - #[test] - fn collect_unseen_returns_each_entry_once_in_order() { - let start = Instant::now(); - let (journal, mut publisher) = ForegroundJournal::new(32, 4); - let mut collector = journal.collector(); - let timestamps: Vec = (0..19) - .map(|i| start + Duration::from_micros(i as u64 + 1)) - .collect(); - publisher.publish(timestamps.iter().map(|&at| input_entry(at, at))); - - let ours = |entries: &[ForegroundJournalEntry]| -> Vec { - entries - .iter() - .filter_map(|entry| match entry { - ForegroundJournalEntry::Event(ForegroundEvent::Input(timing)) - if timestamps.contains(&timing.end) => - { - Some(timing.end) - } - _ => None, - }) - .collect() - }; - - let drained = collector.collect_unseen(); - assert_eq!(ours(&drained.entries), timestamps); - - // The cursor advanced past everything: a second drain sees none of - // our entries again. - let drained = collector.collect_unseen(); - assert!(ours(&drained.entries).is_empty()); - } - - #[test] - fn concurrent_collection_preserves_the_complete_logical_sequence() { - const ENTRY_COUNT: u64 = 20_000; - - let origin = Instant::now(); - let (journal, mut publisher) = ForegroundJournal::new(8, 4); - let mut collector = journal.collector(); - let done = Arc::new(std::sync::atomic::AtomicBool::new(false)); - let collector_done = Arc::clone(&done); - let collector_task = std::thread::spawn(move || { - let mut observed = Vec::new(); - loop { - let drained = collector.collect_unseen(); - observed.extend(normalize_ring_drain(origin, drained.entries)); - if collector_done.load(Ordering::Acquire) { - let drained = collector.collect_unseen(); - observed.extend(normalize_ring_drain(origin, drained.entries)); - return (collector, observed); - } - std::thread::yield_now(); - } - }); - - for sequence in 0..ENTRY_COUNT { - publisher.publish([ring_entry(origin, sequence)]); - } - done.store(true, Ordering::Release); - - let (mut collector, mut observed) = collector_task - .join() - .expect("collector thread should not panic"); - publisher.flush_pending(); - observed.extend(normalize_ring_drain( - origin, - collector.collect_unseen().entries, - )); - - let mut expected_sequence = 0; - for entry in observed { - match entry { - ModelDrainEntry::Entry(sequence) => { - assert_eq!(sequence, expected_sequence); - expected_sequence += 1; - } - ModelDrainEntry::Discontinuity(lost) => expected_sequence += lost, - } - } - assert_eq!(expected_sequence, ENTRY_COUNT); - assert_eq!( - publisher.ring.finalized.load(Ordering::Acquire), - ENTRY_COUNT - ); - } - - #[derive(Clone, Debug)] - enum RingOperation { - Publish, - Collect(u8), - NewCollector, - Pin(u8), - Unpin(u8), - } - - #[derive(Clone, Debug, PartialEq, Eq)] - enum ModelDrainEntry { - Entry(u64), - Discontinuity(u64), - } - - struct ModelRing { - capacity: usize, - pending_capacity: usize, - next_sequence: u64, - finalized: u64, - offered: u64, - slots: Vec>, - pinned: Vec, - pending: VecDeque<(u64, u64)>, - dropped_after_pending: u64, - } - - impl ModelRing { - fn new(capacity: usize, pending_capacity: usize) -> Self { - Self { - capacity, - pending_capacity, - next_sequence: 0, - finalized: 0, - offered: 0, - slots: vec![None; capacity], - pinned: vec![false; capacity], - pending: VecDeque::new(), - dropped_after_pending: 0, - } - } - - fn publish(&mut self) { - self.flush_pending(); - let sequence = self.next_sequence; - self.next_sequence += 1; - self.offered = self.next_sequence; - - if !self.pending.is_empty() || self.dropped_after_pending > 0 { - if self.dropped_after_pending == 0 && self.pending.len() < self.pending_capacity { - self.pending.push_back((sequence, sequence)); - } else { - self.dropped_after_pending += 1; - } - } else { - let index = sequence as usize % self.capacity; - if self.pinned[index] { - if self.pending_capacity > 0 { - self.pending.push_back((sequence, sequence)); - } else { - self.dropped_after_pending = 1; - } - } else { - self.slots[index] = Some((sequence, sequence)); - self.finalized = sequence + 1; - } - } - self.flush_pending(); - } - - fn flush_pending(&mut self) { - while let Some(&(sequence, value)) = self.pending.front() { - let index = sequence as usize % self.capacity; - if self.pinned[index] { - return; - } - self.slots[index] = Some((sequence, value)); - self.pending.pop_front(); - self.finalized = sequence + 1; - } - - if self.dropped_after_pending > 0 { - self.finalized = self.next_sequence; - self.dropped_after_pending = 0; - } - } - - fn collect(&self, cursor: &mut u64) -> (Vec, u64) { - let end = self.finalized; - if *cursor >= end { - return (Vec::new(), 0); - } - - let retained_start = end.saturating_sub(self.capacity as u64); - let mut lost = retained_start.saturating_sub(*cursor); - *cursor = (*cursor).max(retained_start); - let mut entries = Vec::new(); - if lost > 0 { - entries.push(ModelDrainEntry::Discontinuity(lost)); - } - while *cursor < end { - let index = *cursor as usize % self.capacity; - match self.slots[index] { - Some((sequence, value)) if sequence == *cursor => { - entries.push(ModelDrainEntry::Entry(value)); - } - _ => { - lost += 1; - match entries.last_mut() { - Some(ModelDrainEntry::Discontinuity(lost)) => *lost += 1, - _ => entries.push(ModelDrainEntry::Discontinuity(1)), - } - } - } - *cursor += 1; - } - (entries, lost) - } - } - - fn ring_entry(origin: Instant, sequence: u64) -> ForegroundJournalEntry { - let at = origin + Duration::from_micros(sequence); - input_entry(at, at) - } - - fn normalize_ring_drain( - origin: Instant, - entries: Vec, - ) -> Vec { - entries - .into_iter() - .map(|entry| match entry { - ForegroundJournalEntry::Event(ForegroundEvent::Input(timing)) => { - ModelDrainEntry::Entry(timing.start.duration_since(origin).as_micros() as u64) - } - ForegroundJournalEntry::Discontinuity { lost } => { - ModelDrainEntry::Discontinuity(lost) - } - other => panic!("ring property observed an unexpected entry: {other:?}"), - }) - .collect() - } - - #[derive(Debug, Clone, PartialEq, Eq)] - struct NormalizedEvent { - kind: u8, - start: u64, - end: u64, - } - - #[derive(Debug, Clone, PartialEq, Eq)] - struct NormalizedSmallPolls { - count: u64, - total_micros: u64, - since: u64, - until: u64, - } - - #[derive(Debug, Clone, PartialEq, Eq)] - struct NormalizedSnapshot { - interval_start: u64, - boundary_kind: u8, - interval_end: u64, - events: Vec, - small_polls: Vec, - dropped_events: u64, - journal_discontinuous: bool, - } - - #[derive(Debug, Clone, PartialEq, Eq)] - struct NormalizedSealerTail { - interval_start: u64, - events: Vec, - small_polls: Vec, - dropped_events: u64, - journal_discontinuous: bool, - } - - fn completion_order_entries( - origin: Instant, - specifications: &[(u8, u8, u8)], - ) -> Vec { - let mut completed_at_micros = 1u64; - specifications - .iter() - .map(|(advance, span, kind)| { - completed_at_micros += u64::from(*advance) + 1; - let started_at_micros = completed_at_micros.saturating_sub(u64::from(*span)); - let start = origin + Duration::from_micros(started_at_micros); - let end = origin + Duration::from_micros(completed_at_micros); - match kind % 7 { - 0 => input_entry(start, end), - 1 => ForegroundJournalEntry::Event(ForegroundEvent::Action(ActionTiming { - name: "test.action", - start, - end, - })), - 2 => ForegroundJournalEntry::Event(ForegroundEvent::TaskPoll(task_timing( - start, end, - ))), - 3 => { - ForegroundJournalEntry::Event(ForegroundEvent::SmallPolls(SmallPollFlush { - summary: PollSummary { - count: u64::from(*span % 5) + 1, - total: Duration::from_micros(u64::from(*span)), - }, - since: start, - until: end, - })) - } - 4 => pending_frame(WindowId::from(completed_at_micros), start), - 5 => ForegroundJournalEntry::Boundary(IntervalBoundary::Idle { ended_at: end }), - _ => ForegroundJournalEntry::Boundary(presented_boundary( - WindowId::from(completed_at_micros), - start, - end, - )), - } - }) - .collect() - } - - fn normalize_event(origin: Instant, event: ForegroundEvent) -> NormalizedEvent { - let kind = match event { - ForegroundEvent::TaskPoll(_) => 0, - ForegroundEvent::Action(_) => 1, - ForegroundEvent::Input(_) => 2, - ForegroundEvent::Draw(_) => 3, - ForegroundEvent::Present(_) => 4, - ForegroundEvent::SmallPolls(_) => 5, - }; - NormalizedEvent { - kind, - start: event.start_time().duration_since(origin).as_micros() as u64, - end: event.end_time().duration_since(origin).as_micros() as u64, - } - } - - fn normalize_small_polls(origin: Instant, flush: SmallPollFlush) -> NormalizedSmallPolls { - NormalizedSmallPolls { - count: flush.summary.count, - total_micros: flush.summary.total.as_micros() as u64, - since: flush.since.duration_since(origin).as_micros() as u64, - until: flush.until.duration_since(origin).as_micros() as u64, - } - } - - fn normalize_snapshot(origin: Instant, snapshot: &FrameSnapshot) -> NormalizedSnapshot { - NormalizedSnapshot { - interval_start: snapshot.interval_start.duration_since(origin).as_micros() as u64, - boundary_kind: match snapshot.boundary { - IntervalBoundary::Idle { .. } => 0, - IntervalBoundary::Presented(_) => 1, - }, - interval_end: snapshot.interval_end().duration_since(origin).as_micros() as u64, - events: snapshot - .events - .iter() - .copied() - .map(|event| normalize_event(origin, event)) - .collect(), - small_polls: snapshot - .small_polls - .iter() - .copied() - .map(|flush| normalize_small_polls(origin, flush)) - .collect(), - dropped_events: snapshot.dropped_events, - journal_discontinuous: snapshot.journal_discontinuous, - } - } - - fn normalize_tail(origin: Instant, sealer: &IntervalSealer) -> NormalizedSealerTail { - NormalizedSealerTail { - interval_start: sealer.interval_start.duration_since(origin).as_micros() as u64, - events: sealer - .events - .iter() - .copied() - .map(|event| normalize_event(origin, event)) - .collect(), - small_polls: sealer - .small_polls - .iter() - .copied() - .map(|flush| normalize_small_polls(origin, flush)) - .collect(), - dropped_events: sealer.dropped_events, - journal_discontinuous: sealer.journal_discontinuous, - } - } - - fn reference_seal( - origin: Instant, - entries: &[ForegroundJournalEntry], - ) -> (Vec, NormalizedSealerTail) { - let mut interval_start = 0; - let mut events = Vec::new(); - let mut small_polls = Vec::new(); - let mut snapshots = Vec::new(); - let mut dropped_events = 0; - let mut journal_discontinuous = false; - - for entry in entries { - match *entry { - ForegroundJournalEntry::Event(event) => { - let event_start = event.start_time().duration_since(origin).as_micros() as u64; - if events.is_empty() - && small_polls.is_empty() - && dropped_events == 0 - && !journal_discontinuous - { - interval_start = interval_start.max(event_start); - } - match event { - ForegroundEvent::SmallPolls(flush) => { - small_polls.push(normalize_small_polls(origin, flush)); - } - event => events.push(normalize_event(origin, event)), - } - } - ForegroundJournalEntry::Boundary(boundary) => { - if let IntervalBoundary::Presented(presented) = boundary { - let present = ForegroundEvent::Present(presented.presentation); - if events.is_empty() - && small_polls.is_empty() - && dropped_events == 0 - && !journal_discontinuous - { - interval_start = - interval_start - .max(present.start_time().duration_since(origin).as_micros() - as u64); - } - events.push(normalize_event(origin, present)); - } - - let interval_end = - boundary.end_time().duration_since(origin).as_micros() as u64; - if events.is_empty() - && small_polls.is_empty() - && dropped_events == 0 - && !journal_discontinuous - { - interval_start = interval_start.max(interval_end); - } else { - snapshots.push(NormalizedSnapshot { - interval_start, - boundary_kind: match boundary { - IntervalBoundary::Idle { .. } => 0, - IntervalBoundary::Presented(_) => 1, - }, - interval_end, - events: std::mem::take(&mut events), - small_polls: std::mem::take(&mut small_polls), - dropped_events: std::mem::take(&mut dropped_events), - journal_discontinuous: std::mem::take(&mut journal_discontinuous), - }); - interval_start = interval_end; - } - } - ForegroundJournalEntry::FrameState(_) => {} - ForegroundJournalEntry::Discontinuity { lost } => { - dropped_events += lost; - journal_discontinuous = true; - } - } - } - - ( - snapshots, - NormalizedSealerTail { - interval_start, - events, - small_polls, - dropped_events, - journal_discontinuous, - }, - ) - } - - fn reference_occupancy_micros( - event_spans: &[(u8, u8)], - small_poll_spans: &[(u8, u8, u8)], - window_start: u8, - window_end: u8, - ) -> u64 { - let mut clamped_spans = event_spans - .iter() - .map(|&(first, second)| { - let start = first.min(second).max(window_start); - let end = first.max(second).min(window_end).max(start); - (start, end) - }) - .collect::>(); - clamped_spans.sort_unstable(); - - let mut occupied = 0u64; - let mut merged_until = None; - for (start, end) in clamped_spans { - let start = merged_until.map_or(start, |until| start.max(until)); - occupied += u64::from(end.saturating_sub(start)); - merged_until = Some(merged_until.map_or(end, |until| until.max(end))); - } - - occupied - + small_poll_spans - .iter() - .map(|&(first, second, factor)| { - let since = first.min(second); - let until = first.max(second); - if since == until { - if since >= window_start && since <= window_end { - u64::from(factor) - } else { - 0 - } - } else { - let overlap_start = since.max(window_start); - let overlap_end = until.min(window_end).max(overlap_start); - u64::from(overlap_end - overlap_start) * u64::from(factor) - } - }) - .sum::() - } - - #[derive(Debug, Clone)] - struct WriterOperation { - kind: u8, - advance: u16, - argument: u8, - } - - #[derive(Debug, Clone, PartialEq, Eq)] - enum WriterEntry { - Input(u64), - SmallPolls { - count: u64, - total_micros: u64, - since: u64, - until: u64, - }, - Pending { - window: u64, - at: u64, - }, - Closed { - window: u64, - at: u64, - }, - Present { - window: u64, - at: u64, - }, - Presented { - window: u64, - at: u64, - }, - Idle(u64), - } - - #[derive(Default)] - struct ModelSmallPolls { - count: u64, - total_micros: u64, - since: u64, - until: u64, - } - - #[derive(Default)] - struct ModelWriter { - turn_depth: usize, - runnables: usize, - pending_frames: HashMap, - retained_since_boundary: bool, - small_polls: Option, - entries: Vec, - } - - impl ModelWriter { - fn record_entry(&mut self, entry: WriterEntry) { - if let Some(small_polls) = self.small_polls.take() { - self.entries.push(WriterEntry::SmallPolls { - count: small_polls.count, - total_micros: small_polls.total_micros, - since: small_polls.since, - until: small_polls.until, - }); - } - let boundary = matches!(entry, WriterEntry::Presented { .. } | WriterEntry::Idle(_)); - self.entries.push(entry); - if boundary { - self.retained_since_boundary = false; - } - } - - fn record_input(&mut self, at: u64) { - self.retained_since_boundary = true; - self.record_entry(WriterEntry::Input(at)); - } - - fn fold_small_poll(&mut self, since: u64, until: u64) { - let small_polls = self.small_polls.get_or_insert(ModelSmallPolls { - since, - until, - ..ModelSmallPolls::default() - }); - small_polls.count += 1; - small_polls.total_micros += until - since; - small_polls.since = small_polls.since.min(since); - small_polls.until = small_polls.until.max(until); - } - - fn record_pending(&mut self, window: u64, at: u64) { - let should_record = self - .pending_frames - .get(&window) - .is_none_or(|previous| at.saturating_sub(*previous) >= 1_000_000); - if should_record { - self.pending_frames.insert(window, at); - self.record_entry(WriterEntry::Pending { window, at }); - } - } - - fn record_closed(&mut self, window: u64, at: u64) { - self.pending_frames.remove(&window); - self.record_entry(WriterEntry::Closed { window, at }); - } - - fn record_present(&mut self, window: u64, at: u64, has_frame: bool) { - if has_frame { - self.pending_frames.remove(&window); - self.record_entry(WriterEntry::Presented { window, at }); - } else { - self.pending_frames.remove(&window); - self.retained_since_boundary = true; - self.record_entry(WriterEntry::Present { window, at }); - } - } - - fn end_turn(&mut self, at: u64) { - if self.turn_depth == 0 { - return; - } - self.turn_depth -= 1; - if self.turn_depth > 0 || self.runnables > 0 { - return; - } - self.pending_frames - .retain(|_, dirty_at| at.saturating_sub(*dirty_at) < 1_000_000); - if !self.pending_frames.is_empty() { - return; - } - if !self.retained_since_boundary { - self.small_polls = None; - return; - } - self.record_entry(WriterEntry::Idle(at)); - } - } - - fn normalize_writer_entries( - origin: Instant, - entries: Vec, - ) -> Vec { - let at = |instant: Instant| instant.duration_since(origin).as_micros() as u64; - entries - .into_iter() - .map(|entry| match entry { - ForegroundJournalEntry::Event(ForegroundEvent::Input(timing)) => { - WriterEntry::Input(at(timing.end)) - } - ForegroundJournalEntry::Event(ForegroundEvent::SmallPolls(flush)) => { - WriterEntry::SmallPolls { - count: flush.summary.count, - total_micros: flush.summary.total.as_micros() as u64, - since: at(flush.since), - until: at(flush.until), - } - } - ForegroundJournalEntry::Event(ForegroundEvent::Present(timing)) => { - WriterEntry::Present { - window: timing.window_id.as_u64(), - at: at(timing.present_end), - } - } - ForegroundJournalEntry::FrameState(FrameStateChange::Pending { - window_id, - dirty_at, - }) => WriterEntry::Pending { - window: window_id.as_u64(), - at: at(dirty_at), - }, - ForegroundJournalEntry::FrameState(FrameStateChange::Closed { - window_id, - at: closed_at, - }) => WriterEntry::Closed { - window: window_id.as_u64(), - at: at(closed_at), - }, - ForegroundJournalEntry::Boundary(IntervalBoundary::Presented(presented)) => { - WriterEntry::Presented { - window: presented.frame.window_id.as_u64(), - at: at(presented.presentation.present_end), - } - } - ForegroundJournalEntry::Boundary(IntervalBoundary::Idle { ended_at }) => { - WriterEntry::Idle(at(ended_at)) - } - other => panic!("writer property emitted an unexpected entry: {other:?}"), - }) - .collect() - } - - proptest! { - #![proptest_config(ProptestConfig { - failure_persistence: None, - ..ProptestConfig::default() - })] - - #[test] - fn ring_matches_reference_model_under_wrap_collisions_and_independent_cursors( - capacity in 1usize..=8, - pending_capacity in 0usize..=4, - operations in prop::collection::vec( - prop_oneof![ - 5 => Just(RingOperation::Publish), - 3 => any::().prop_map(RingOperation::Collect), - 1 => Just(RingOperation::NewCollector), - 2 => any::().prop_map(RingOperation::Pin), - 2 => any::().prop_map(RingOperation::Unpin), - ], - 1..=128, - ), - ) { - let origin = Instant::now(); - let (journal, mut publisher) = ForegroundJournal::new(capacity, pending_capacity); - let mut collectors = vec![journal.collector()]; - let mut model_collectors = vec![0]; - let mut model = ModelRing::new(capacity, pending_capacity); - let mut pins: Vec>> = - (0..capacity).map(|_| None).collect(); - - for operation in operations { - match operation { - RingOperation::Publish => { - let sequence = model.next_sequence; - publisher.publish([ring_entry(origin, sequence)]); - model.publish(); - } - RingOperation::Collect(collector) => { - let index = usize::from(collector) % collectors.len(); - let drained = collectors[index].collect_unseen(); - let (expected_entries, expected_lost) = - model.collect(&mut model_collectors[index]); - let observed_entries = normalize_ring_drain(origin, drained.entries); - prop_assert_eq!(observed_entries, expected_entries); - prop_assert_eq!(drained.lost, expected_lost); - } - RingOperation::NewCollector if collectors.len() < 4 => { - collectors.push(journal.collector()); - model_collectors.push(model.offered); - } - RingOperation::NewCollector => {} - RingOperation::Pin(slot) => { - let index = usize::from(slot) % capacity; - if pins[index].is_none() { - pins[index] = JournalSlotReadGuard::try_new(&journal.ring.slots[index]); - prop_assert!(pins[index].is_some()); - model.pinned[index] = true; - } - } - RingOperation::Unpin(slot) => { - let index = usize::from(slot) % capacity; - pins[index].take(); - model.pinned[index] = false; - } - } - - prop_assert_eq!(publisher.next_sequence, model.next_sequence); - prop_assert_eq!( - publisher.ring.offered.load(Ordering::Acquire), - model.offered - ); - prop_assert_eq!( - publisher.ring.finalized.load(Ordering::Acquire), - model.finalized - ); - prop_assert_eq!( - publisher - .pending - .iter() - .map(|entry| entry.sequence) - .collect::>(), - model - .pending - .iter() - .map(|(sequence, _)| *sequence) - .collect::>() - ); - prop_assert_eq!( - publisher.dropped_after_pending, - model.dropped_after_pending - ); - } - - for pin in &mut pins { - pin.take(); - } - model.pinned.fill(false); - publisher.flush_pending(); - model.flush_pending(); - - for (collector, model_cursor) in collectors.iter_mut().zip(&mut model_collectors) { - let drained = collector.collect_unseen(); - let (expected_entries, expected_lost) = model.collect(model_cursor); - let observed_entries = normalize_ring_drain(origin, drained.entries); - prop_assert_eq!(observed_entries, expected_entries); - prop_assert_eq!(drained.lost, expected_lost); - } - } - - #[test] - fn a_presentation_seals_exactly_once_regardless_of_delay( - present_delay_micros in 1u64..=2_000_000 - ) { - let start = Instant::now(); - let window_id = WindowId::from(0xDEA8); - let presented_at = start + Duration::from_micros(present_delay_micros); - let mut sealer = IntervalSealer::new(start); - let snapshots = sealer.push_entries([ - pending_frame(window_id, start), - input_entry(start, start + Duration::from_micros(1)), - ForegroundJournalEntry::Boundary(presented_boundary( - window_id, - start, - presented_at, - )), - ]); - - prop_assert_eq!(snapshots.len(), 1); - prop_assert!(matches!( - snapshots[0].boundary, - IntervalBoundary::Presented(_) - )); - prop_assert_eq!(snapshots[0].interval_end(), presented_at); - } - - #[test] - fn completion_order_sealer_matches_reference_under_arbitrary_batching( - specifications in prop::collection::vec((0u8..16, 0u8..128, any::()), 1..=96), - batch_sizes in prop::collection::vec(1usize..=12, 1..=24), - ) { - let origin = Instant::now(); - let entries = completion_order_entries(origin, &specifications); - let (expected_snapshots, expected_tail) = reference_seal(origin, &entries); - - let mut one_batch_sealer = IntervalSealer::new(origin); - let one_batch_snapshots = one_batch_sealer - .push_entries(entries.iter().copied()) - .iter() - .map(|snapshot| normalize_snapshot(origin, snapshot)) - .collect::>(); - prop_assert_eq!(&one_batch_snapshots, &expected_snapshots); - prop_assert_eq!(&normalize_tail(origin, &one_batch_sealer), &expected_tail); - - let mut batched_sealer = IntervalSealer::new(origin); - let mut batched_snapshots = Vec::new(); - let mut offset = 0; - let mut batch_index = 0; - while offset < entries.len() { - let batch_size = batch_sizes[batch_index % batch_sizes.len()]; - let batch_end = (offset + batch_size).min(entries.len()); - batched_snapshots.extend( - batched_sealer - .push_entries(entries[offset..batch_end].iter().copied()) - .iter() - .map(|snapshot| normalize_snapshot(origin, snapshot)), - ); - offset = batch_end; - batch_index += 1; - } - prop_assert_eq!(batched_snapshots, expected_snapshots); - prop_assert_eq!(normalize_tail(origin, &batched_sealer), expected_tail); - } - - #[test] - fn occupancy_matches_interval_union_and_fold_apportionment( - event_spans in prop::collection::vec((0u8..=128, 0u8..=128), 0..=16), - small_poll_spans in prop::collection::vec( - (0u8..=128, 0u8..=128, 0u8..=4), - 0..=12, - ), - window in (0u8..=128, 0u8..=128), - ) { - let origin = Instant::now(); - let window_start_micros = window.0.min(window.1); - let window_end_micros = window.0.max(window.1); - let events = event_spans - .iter() - .map(|&(first, second)| { - let start = origin + Duration::from_micros(u64::from(first.min(second))); - let end = origin + Duration::from_micros(u64::from(first.max(second))); - ForegroundEvent::Input(InputTiming { - kind: "test", - start, - end, - caused_invalidation: false, - }) - }) - .collect(); - let small_polls = small_poll_spans - .iter() - .map(|&(first, second, factor)| { - let since_micros = first.min(second); - let until_micros = first.max(second); - let span_micros = u64::from(until_micros - since_micros); - let total_micros = if span_micros == 0 { - u64::from(factor) - } else { - span_micros * u64::from(factor) - }; - SmallPollFlush { - summary: PollSummary { - count: 1, - total: Duration::from_micros(total_micros), - }, - since: origin + Duration::from_micros(u64::from(since_micros)), - until: origin + Duration::from_micros(u64::from(until_micros)), - } - }) - .collect(); - let snapshot = FrameSnapshot { - interval_start: origin, - boundary: IntervalBoundary::Idle { - ended_at: origin + Duration::from_micros(128), - }, - events, - small_polls, - dropped_events: 0, - journal_discontinuous: false, - }; - let expected_micros = reference_occupancy_micros( - &event_spans, - &small_poll_spans, - window_start_micros, - window_end_micros, - ); - let observed = snapshot.occupancy_within( - origin + Duration::from_micros(u64::from(window_start_micros)), - origin + Duration::from_micros(u64::from(window_end_micros)), - ); - - prop_assert_eq!(observed, Duration::from_micros(expected_micros)); - - let full_expected = - reference_occupancy_micros(&event_spans, &small_poll_spans, 0, 128); - prop_assert_eq!(snapshot.occupancy(), Duration::from_micros(full_expected)); - prop_assert!((0.0..=1.0).contains(&snapshot.busy_fraction())); - prop_assert_eq!( - snapshot.busy_fraction(), - (full_expected as f64 / 128.0).min(1.0) - ); - } - - #[test] - fn writer_matches_state_model_across_turn_frame_and_runnable_transitions( - operations in prop::collection::vec( - (any::(), any::(), any::()).prop_map( - |(kind, advance, argument)| WriterOperation { - kind, - advance, - argument, - }, - ), - 1..=128, - ), - ) { - let origin = Instant::now(); - let counter = ForegroundRunnableCounter::new(); - let (mut writer, mut collector) = test_journal(counter.clone()); - let mut model = ModelWriter::default(); - let mut at_micros = 100u64; - - for operation in operations { - if operation.advance % 16 == 0 { - at_micros += FRAME_DEADLINE.as_micros() as u64; - } else { - at_micros += u64::from(operation.advance) + 1; - } - let at = origin + Duration::from_micros(at_micros); - let window_id = WindowId::from(u64::from(operation.argument % 4)); - let window = window_id.as_u64(); - - match operation.kind % 10 { - 0 => { - writer.begin_turn(); - model.turn_depth += 1; - } - 1 if model.turn_depth > 0 => { - writer.end_turn(at); - model.end_turn(at_micros); - } - 1 => {} - 2 => { - writer.record_event(ForegroundEvent::Input(InputTiming { - kind: "test", - start: at, - end: at, - caused_invalidation: false, - })); - model.record_input(at_micros); - } - 3 => { - let duration_micros = u64::from(operation.argument % 100); - writer.fold_small_poll(task_timing( - at - Duration::from_micros(duration_micros), - at, - )); - model.fold_small_poll( - at_micros - duration_micros, - at_micros, - ); - } - 4 => { - counter.queued(); - model.runnables += 1; - } - 5 if model.runnables > 0 => { - counter.finished(); - model.runnables -= 1; - } - 5 => {} - 6 => { - writer.record_frame_pending(window_id, at); - model.record_pending(window, at_micros); - } - 7 => { - writer.record_window_closed(window_id, at); - model.record_closed(window, at_micros); - } - 8 => { - writer.record_present( - presentation_timing(window_id, at), - Some(frame_timing(window_id, at, at)), - ); - model.record_present(window, at_micros, true); - } - _ => { - writer.record_present(presentation_timing(window_id, at), None); - model.record_present(window, at_micros, false); - } - } - - let drained = collector.collect_unseen(); - prop_assert_eq!(drained.lost, 0); - prop_assert_eq!( - normalize_writer_entries(origin, drained.entries), - std::mem::take(&mut model.entries) - ); - prop_assert_eq!(writer.turn_depth, model.turn_depth); - prop_assert_eq!(writer.retained_since_boundary, model.retained_since_boundary); - prop_assert_eq!(writer.pending_frames.len(), model.pending_frames.len()); - } - } - } - - fn test_journal( - foreground_runnables: ForegroundRunnableCounter, - ) -> (ForegroundJournalWriter, ForegroundJournalCollector) { - let (journal, publisher) = ForegroundJournal::new(256, 8); - let collector = journal.collector(); - ( - ForegroundJournalWriter::new(foreground_runnables, publisher), - collector, - ) - } - - fn has_boundary_at(entries: &[ForegroundJournalEntry], at: Instant) -> bool { - entries.iter().any(|entry| { - matches!(entry, ForegroundJournalEntry::Boundary(boundary) if boundary.end_time() == at) - }) - } - - fn input_entry(start: Instant, end: Instant) -> ForegroundJournalEntry { - ForegroundJournalEntry::Event(ForegroundEvent::Input(InputTiming { - kind: "test", - start, - end, - caused_invalidation: false, - })) - } - - fn pending_frame(window_id: WindowId, dirty_at: Instant) -> ForegroundJournalEntry { - ForegroundJournalEntry::FrameState(FrameStateChange::Pending { - window_id, - dirty_at, - }) - } - - fn frame_timing(window_id: WindowId, dirty_at: Instant, draw_end: Instant) -> FrameTiming { - FrameTiming { - window_id, - dirty_at: Some(dirty_at), - invalidations: 1, - draw_start: draw_end, - draw_end, - } - } - - fn presentation_timing(window_id: WindowId, present_end: Instant) -> PresentTiming { - PresentTiming { - window_id, - present_start: present_end, - present_end, - animation_interval: None, - } - } - - fn presented_boundary( - window_id: WindowId, - dirty_at: Instant, - present_end: Instant, - ) -> IntervalBoundary { - IntervalBoundary::Presented(PresentedFrame { - frame: frame_timing(window_id, dirty_at, present_end), - presentation: presentation_timing(window_id, present_end), - }) - } - - fn task_timing(start: Instant, end: Instant) -> TaskTiming { - TaskTiming { - location: std::panic::Location::caller(), - spawned: SpawnTime(start), - start, - end: YieldTime(end), - } - } -} diff --git a/crates/gpui_pre/src/queue.rs b/crates/gpui_pre/src/queue.rs deleted file mode 100644 index 76f6d67a..00000000 --- a/crates/gpui_pre/src/queue.rs +++ /dev/null @@ -1,435 +0,0 @@ -use std::{ - collections::VecDeque, - fmt, - iter::FusedIterator, - sync::{Arc, atomic::AtomicUsize}, -}; - -use rand::{Rng, SeedableRng, rngs::SmallRng}; - -use crate::Priority; - -struct PriorityQueues { - high_priority: VecDeque, - medium_priority: VecDeque, - low_priority: VecDeque, -} - -impl PriorityQueues { - fn is_empty(&self) -> bool { - self.high_priority.is_empty() - && self.medium_priority.is_empty() - && self.low_priority.is_empty() - } -} - -struct PriorityQueueState { - queues: parking_lot::Mutex>, - condvar: parking_lot::Condvar, - receiver_count: AtomicUsize, - sender_count: AtomicUsize, -} - -impl PriorityQueueState { - fn send(&self, priority: Priority, item: T) -> Result<(), SendError> { - if self - .receiver_count - .load(std::sync::atomic::Ordering::Relaxed) - == 0 - { - return Err(SendError(item)); - } - - let mut queues = self.queues.lock(); - Self::push(&mut queues, priority, item); - self.condvar.notify_one(); - Ok(()) - } - - fn spin_send(&self, priority: Priority, item: T) -> Result<(), SendError> { - if self - .receiver_count - .load(std::sync::atomic::Ordering::Relaxed) - == 0 - { - return Err(SendError(item)); - } - - let mut queues = loop { - if let Some(guard) = self.queues.try_lock() { - break guard; - } - std::hint::spin_loop(); - }; - Self::push(&mut queues, priority, item); - self.condvar.notify_one(); - Ok(()) - } - - fn push(queues: &mut PriorityQueues, priority: Priority, item: T) { - match priority { - Priority::RealtimeAudio => unreachable!( - "Realtime audio priority runs on a dedicated thread and is never queued" - ), - Priority::High => queues.high_priority.push_back(item), - Priority::Medium => queues.medium_priority.push_back(item), - Priority::Low => queues.low_priority.push_back(item), - }; - } - - fn recv<'a>(&'a self) -> Result>, RecvError> { - let mut queues = self.queues.lock(); - - let sender_count = self.sender_count.load(std::sync::atomic::Ordering::Relaxed); - if queues.is_empty() && sender_count == 0 { - return Err(crate::queue::RecvError); - } - - while queues.is_empty() { - self.condvar.wait(&mut queues); - } - - Ok(queues) - } - - fn try_recv<'a>( - &'a self, - ) -> Result>>, RecvError> { - let mut queues = self.queues.lock(); - - let sender_count = self.sender_count.load(std::sync::atomic::Ordering::Relaxed); - if queues.is_empty() && sender_count == 0 { - return Err(crate::queue::RecvError); - } - - if queues.is_empty() { - Ok(None) - } else { - Ok(Some(queues)) - } - } - - fn spin_try_recv<'a>( - &'a self, - ) -> Result>>, RecvError> { - let queues = loop { - if let Some(guard) = self.queues.try_lock() { - break guard; - } - std::hint::spin_loop(); - }; - - let sender_count = self.sender_count.load(std::sync::atomic::Ordering::Relaxed); - if queues.is_empty() && sender_count == 0 { - return Err(crate::queue::RecvError); - } - - if queues.is_empty() { - Ok(None) - } else { - Ok(Some(queues)) - } - } -} - -#[doc(hidden)] -pub struct PriorityQueueSender { - state: Arc>, -} - -impl PriorityQueueSender { - fn new(state: Arc>) -> Self { - Self { state } - } - - pub fn send(&self, priority: Priority, item: T) -> Result<(), SendError> { - self.state.send(priority, item)?; - Ok(()) - } - - pub fn spin_send(&self, priority: Priority, item: T) -> Result<(), SendError> { - self.state.spin_send(priority, item)?; - Ok(()) - } -} - -impl Drop for PriorityQueueSender { - fn drop(&mut self) { - self.state - .sender_count - .fetch_sub(1, std::sync::atomic::Ordering::AcqRel); - } -} - -#[doc(hidden)] -pub struct PriorityQueueReceiver { - state: Arc>, - rand: SmallRng, - disconnected: bool, -} - -impl Clone for PriorityQueueReceiver { - fn clone(&self) -> Self { - self.state - .receiver_count - .fetch_add(1, std::sync::atomic::Ordering::AcqRel); - Self { - state: Arc::clone(&self.state), - rand: SmallRng::seed_from_u64(0), - disconnected: self.disconnected, - } - } -} - -#[doc(hidden)] -pub struct SendError(pub T); - -impl fmt::Debug for SendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("SendError").field(&self.0).finish() - } -} - -#[derive(Debug)] -#[doc(hidden)] -pub struct RecvError; - -#[allow(dead_code)] -impl PriorityQueueReceiver { - pub fn new() -> (PriorityQueueSender, Self) { - let state = PriorityQueueState { - queues: parking_lot::Mutex::new(PriorityQueues { - high_priority: VecDeque::new(), - medium_priority: VecDeque::new(), - low_priority: VecDeque::new(), - }), - condvar: parking_lot::Condvar::new(), - receiver_count: AtomicUsize::new(1), - sender_count: AtomicUsize::new(1), - }; - let state = Arc::new(state); - - let sender = PriorityQueueSender::new(Arc::clone(&state)); - - let receiver = PriorityQueueReceiver { - state, - rand: SmallRng::seed_from_u64(0), - disconnected: false, - }; - - (sender, receiver) - } - - /// Returns whether the queue currently contains no elements. - pub fn is_empty(&self) -> bool { - self.state.queues.lock().is_empty() - } - - /// Returns the number of queued elements across all priorities. - pub(crate) fn len(&self) -> usize { - let queues = self.state.queues.lock(); - queues.high_priority.len() + queues.medium_priority.len() + queues.low_priority.len() - } - - /// Tries to pop one element from the priority queue without blocking. - /// - /// This will early return if there are no elements in the queue. - /// - /// This method is best suited if you only intend to pop one element, for better performance - /// on large queues see [`Self::try_iter`] - /// - /// # Errors - /// - /// If the sender was dropped - pub fn try_pop(&mut self) -> Result, RecvError> { - self.pop_inner(false) - } - - pub fn spin_try_pop(&mut self) -> Result, RecvError> { - use Priority as P; - - let Some(mut queues) = self.state.spin_try_recv()? else { - return Ok(None); - }; - - let high = P::High.weight() * !queues.high_priority.is_empty() as u32; - let medium = P::Medium.weight() * !queues.medium_priority.is_empty() as u32; - let low = P::Low.weight() * !queues.low_priority.is_empty() as u32; - let mut mass = high + medium + low; - - if !queues.high_priority.is_empty() { - let flip = self.rand.random_ratio(P::High.weight(), mass); - if flip { - return Ok(queues.high_priority.pop_front()); - } - mass -= P::High.weight(); - } - - if !queues.medium_priority.is_empty() { - let flip = self.rand.random_ratio(P::Medium.weight(), mass); - if flip { - return Ok(queues.medium_priority.pop_front()); - } - mass -= P::Medium.weight(); - } - - if !queues.low_priority.is_empty() { - let flip = self.rand.random_ratio(P::Low.weight(), mass); - if flip { - return Ok(queues.low_priority.pop_front()); - } - } - - Ok(None) - } - - /// Pops an element from the priority queue blocking if necessary. - /// - /// This method is best suited if you only intend to pop one element, for better performance - /// on large queues see [`Self::iter``] - /// - /// # Errors - /// - /// If the sender was dropped - pub fn pop(&mut self) -> Result { - self.pop_inner(true).map(|e| e.unwrap()) - } - - /// Returns an iterator over the elements of the queue - /// this iterator will end when all elements have been consumed and will not wait for new ones. - pub fn try_iter(self) -> TryIter { - TryIter { - receiver: self, - ended: false, - } - } - - /// Returns an iterator over the elements of the queue - /// this iterator will wait for new elements if the queue is empty. - pub fn iter(self) -> Iter { - Iter(self) - } - - #[inline(always)] - // algorithm is the loaded die from biased coin from - // https://www.keithschwarz.com/darts-dice-coins/ - fn pop_inner(&mut self, block: bool) -> Result, RecvError> { - use Priority as P; - - let mut queues = if !block { - let Some(queues) = self.state.try_recv()? else { - return Ok(None); - }; - queues - } else { - self.state.recv()? - }; - - let high = P::High.weight() * !queues.high_priority.is_empty() as u32; - let medium = P::Medium.weight() * !queues.medium_priority.is_empty() as u32; - let low = P::Low.weight() * !queues.low_priority.is_empty() as u32; - let mut mass = high + medium + low; //% - - if !queues.high_priority.is_empty() { - let flip = self.rand.random_ratio(P::High.weight(), mass); - if flip { - return Ok(queues.high_priority.pop_front()); - } - mass -= P::High.weight(); - } - - if !queues.medium_priority.is_empty() { - let flip = self.rand.random_ratio(P::Medium.weight(), mass); - if flip { - return Ok(queues.medium_priority.pop_front()); - } - mass -= P::Medium.weight(); - } - - if !queues.low_priority.is_empty() { - let flip = self.rand.random_ratio(P::Low.weight(), mass); - if flip { - return Ok(queues.low_priority.pop_front()); - } - } - - Ok(None) - } -} - -impl Drop for PriorityQueueReceiver { - fn drop(&mut self) { - self.state - .receiver_count - .fetch_sub(1, std::sync::atomic::Ordering::AcqRel); - } -} - -#[doc(hidden)] -pub struct Iter(PriorityQueueReceiver); -impl Iterator for Iter { - type Item = T; - - fn next(&mut self) -> Option { - self.0.pop().ok() - } -} -impl FusedIterator for Iter {} - -#[doc(hidden)] -pub struct TryIter { - receiver: PriorityQueueReceiver, - ended: bool, -} -impl Iterator for TryIter { - type Item = Result; - - fn next(&mut self) -> Option { - if self.ended { - return None; - } - - let res = self.receiver.try_pop(); - self.ended = res.is_err(); - - res.transpose() - } -} -impl FusedIterator for TryIter {} - -#[cfg(test)] -mod tests { - use collections::HashSet; - - use super::*; - - #[test] - fn all_tasks_get_yielded() { - let (tx, mut rx) = PriorityQueueReceiver::new(); - tx.send(Priority::Medium, 20).unwrap(); - tx.send(Priority::High, 30).unwrap(); - tx.send(Priority::Low, 10).unwrap(); - tx.send(Priority::Medium, 21).unwrap(); - tx.send(Priority::High, 31).unwrap(); - - drop(tx); - - assert_eq!( - rx.iter().collect::>(), - [30, 31, 20, 21, 10].into_iter().collect::>() - ) - } - - #[test] - fn new_high_prio_task_get_scheduled_quickly() { - let (tx, mut rx) = PriorityQueueReceiver::new(); - for _ in 0..100 { - tx.send(Priority::Low, 1).unwrap(); - } - - assert_eq!(rx.pop().unwrap(), 1); - tx.send(Priority::High, 3).unwrap(); - assert_eq!(rx.pop().unwrap(), 3); - assert_eq!(rx.pop().unwrap(), 1); - } -} diff --git a/crates/gpui_pre/src/scene.rs b/crates/gpui_pre/src/scene.rs deleted file mode 100644 index 46c1acf5..00000000 --- a/crates/gpui_pre/src/scene.rs +++ /dev/null @@ -1,1022 +0,0 @@ -// todo("windows"): remove -#![cfg_attr(windows, allow(dead_code))] - -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -use crate::{ - bounds_tree::BoundsTree, point, AtlasTextureId, AtlasTile, Background, Bounds, ContentMask, - Corners, Edges, Hsla, Pixels, Point, Radians, ScaledPixels, Size, -}; -use std::{ - fmt::Debug, - iter::Peekable, - ops::{Add, Range, Sub}, - slice, -}; - -#[allow(non_camel_case_types, unused)] -#[expect(missing_docs)] -pub type PathVertex_ScaledPixels = PathVertex; - -#[expect(missing_docs)] -pub type DrawOrder = u32; - -/// A boolean stored as a `u32` so that GPU-facing structs contain no -/// compiler-inserted padding bytes, which would be undefined behavior to -/// reinterpret as `&[u8]` when writing instance buffers. Guaranteed to be -/// `0` or `1` by construction; shaders read it as a `u32`/`uint`. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(transparent)] -pub struct PaddedBool32(u32); - -impl From for PaddedBool32 { - fn from(value: bool) -> Self { - PaddedBool32(value as u32) - } -} - -#[derive(Default)] -#[expect(missing_docs)] -pub struct Scene { - /// Immutable rounded clip nodes referenced by primitive masks. - pub rounded_clips: Vec>, - clip_indices: std::collections::HashMap<[u32; 13], u32>, - pub(crate) paint_operations: Vec, - primitive_bounds: BoundsTree, - layer_stack: Vec, - pub shadows: Vec, - pub quads: Vec, - pub paths: Vec>, - pub underlines: Vec, - pub monochrome_sprites: Vec, - pub subpixel_sprites: Vec, - pub polychrome_sprites: Vec, - pub surfaces: Vec, -} - -#[expect(missing_docs)] -impl Scene { - pub fn clear(&mut self) { - self.rounded_clips.clear(); - self.clip_indices.clear(); - self.paint_operations.clear(); - self.primitive_bounds.clear(); - self.layer_stack.clear(); - self.paths.clear(); - self.shadows.clear(); - self.quads.clear(); - self.underlines.clear(); - self.monochrome_sprites.clear(); - self.subpixel_sprites.clear(); - self.polychrome_sprites.clear(); - self.surfaces.clear(); - } - - pub fn len(&self) -> usize { - self.paint_operations.len() - } - - /// Intern a clip node for this frame. Identical ancestor chains share nodes. - pub fn insert_clip(&mut self, clip: crate::RoundedClip) -> u32 { - assert!( - clip.parent as usize <= self.rounded_clips.len(), - "clip parent must already exist in this scene" - ); - let key = [ - clip.bounds.origin.x.0.to_bits(), - clip.bounds.origin.y.0.to_bits(), - clip.bounds.size.width.0.to_bits(), - clip.bounds.size.height.0.to_bits(), - clip.radii_x.top_left.0.to_bits(), - clip.radii_x.top_right.0.to_bits(), - clip.radii_x.bottom_right.0.to_bits(), - clip.radii_x.bottom_left.0.to_bits(), - clip.radii_y.top_left.0.to_bits(), - clip.radii_y.top_right.0.to_bits(), - clip.radii_y.bottom_right.0.to_bits(), - clip.radii_y.bottom_left.0.to_bits(), - clip.parent, - ]; - if let Some(index) = self.clip_indices.get(&key) { - return *index; - } - let index = u32::try_from(self.rounded_clips.len()).expect("scene clip index overflow") + 1; - self.rounded_clips.push(clip); - self.clip_indices.insert(key, index); - index - } - - fn import_clip(&mut self, mut index: u32, previous: &Scene) -> u32 { - let mut chain = Vec::new(); - while index != 0 { - let clip = previous.rounded_clips[index as usize - 1]; - index = clip.parent; - chain.push(clip); - } - let mut parent = 0; - for mut clip in chain.into_iter().rev() { - clip.parent = parent; - parent = self.insert_clip(clip); - } - parent - } - - pub fn push_layer(&mut self, bounds: Bounds) { - let order = self.primitive_bounds.insert(bounds); - self.layer_stack.push(order); - self.paint_operations - .push(PaintOperation::StartLayer(bounds)); - } - - pub fn pop_layer(&mut self) { - self.layer_stack.pop(); - self.paint_operations.push(PaintOperation::EndLayer); - } - - pub fn insert_primitive(&mut self, primitive: impl Into) { - let mut primitive = primitive.into(); - let clipped_bounds = primitive - .bounds() - .intersect(&primitive.content_mask().bounds); - - if clipped_bounds.is_empty() { - return; - } - - let order = self - .layer_stack - .last() - .copied() - .unwrap_or_else(|| self.primitive_bounds.insert(clipped_bounds)); - match &mut primitive { - Primitive::Shadow(shadow) => { - shadow.order = order; - self.shadows.push(*shadow); - } - Primitive::Quad(quad) => { - quad.order = order; - self.quads.push(*quad); - } - Primitive::Path(path) => { - path.order = order; - path.id = PathId(self.paths.len()); - self.paths.push(path.clone()); - } - Primitive::Underline(underline) => { - underline.order = order; - self.underlines.push(*underline); - } - Primitive::MonochromeSprite(sprite) => { - sprite.order = order; - self.monochrome_sprites.push(*sprite); - } - Primitive::SubpixelSprite(sprite) => { - sprite.order = order; - self.subpixel_sprites.push(*sprite); - } - Primitive::PolychromeSprite(sprite) => { - sprite.order = order; - self.polychrome_sprites.push(*sprite); - } - Primitive::Surface(surface) => { - surface.order = order; - self.surfaces.push(surface.clone()); - } - } - self.paint_operations - .push(PaintOperation::Primitive(primitive)); - } - - pub fn replay(&mut self, range: Range, prev_scene: &Scene) { - let mut clip_remapping = std::collections::HashMap::new(); - for operation in &prev_scene.paint_operations[range] { - match operation { - PaintOperation::Primitive(primitive) => { - let mut primitive = primitive.clone(); - let mask = primitive.content_mask_mut(); - if mask.clip_index != 0 { - mask.clip_index = *clip_remapping - .entry(mask.clip_index) - .or_insert_with(|| self.import_clip(mask.clip_index, prev_scene)); - } - self.insert_primitive(primitive); - } - PaintOperation::StartLayer(bounds) => self.push_layer(*bounds), - PaintOperation::EndLayer => self.pop_layer(), - } - } - } - - pub fn finish(&mut self) { - self.shadows.sort_by_key(|shadow| shadow.order); - self.quads.sort_by_key(|quad| quad.order); - self.paths.sort_by_key(|path| path.order); - self.underlines.sort_by_key(|underline| underline.order); - self.monochrome_sprites - .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id)); - self.subpixel_sprites - .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id)); - self.polychrome_sprites - .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id)); - self.surfaces.sort_by_key(|surface| surface.order); - } - - #[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) - )] - pub fn batches(&self) -> impl Iterator + '_ { - BatchIterator { - shadows_start: 0, - shadows_iter: self.shadows.iter().peekable(), - quads_start: 0, - quads_iter: self.quads.iter().peekable(), - paths_start: 0, - paths_iter: self.paths.iter().peekable(), - underlines_start: 0, - underlines_iter: self.underlines.iter().peekable(), - monochrome_sprites_start: 0, - monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(), - subpixel_sprites_start: 0, - subpixel_sprites_iter: self.subpixel_sprites.iter().peekable(), - polychrome_sprites_start: 0, - polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(), - surfaces_start: 0, - surfaces_iter: self.surfaces.iter().peekable(), - } - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)] -#[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) -)] -pub(crate) enum PrimitiveKind { - Shadow, - #[default] - Quad, - Path, - Underline, - MonochromeSprite, - SubpixelSprite, - PolychromeSprite, - Surface, -} - -pub(crate) enum PaintOperation { - Primitive(Primitive), - StartLayer(Bounds), - EndLayer, -} - -#[derive(Clone)] -#[expect(missing_docs)] -pub enum Primitive { - Shadow(Shadow), - Quad(Quad), - Path(Path), - Underline(Underline), - MonochromeSprite(MonochromeSprite), - SubpixelSprite(SubpixelSprite), - PolychromeSprite(PolychromeSprite), - Surface(PaintSurface), -} - -#[expect(missing_docs)] -impl Primitive { - pub fn bounds(&self) -> &Bounds { - 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::SubpixelSprite(sprite) => &sprite.bounds, - Primitive::PolychromeSprite(sprite) => &sprite.bounds, - Primitive::Surface(surface) => &surface.bounds, - } - } - - pub fn content_mask(&self) -> &ContentMask { - 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::SubpixelSprite(sprite) => &sprite.content_mask, - Primitive::PolychromeSprite(sprite) => &sprite.content_mask, - Primitive::Surface(surface) => &surface.content_mask, - } - } - - pub fn content_mask_mut(&mut self) -> &mut ContentMask { - match self { - Primitive::Shadow(shadow) => &mut shadow.content_mask, - Primitive::Quad(quad) => &mut quad.content_mask, - Primitive::Path(path) => &mut path.content_mask, - Primitive::Underline(underline) => &mut underline.content_mask, - Primitive::MonochromeSprite(sprite) => &mut sprite.content_mask, - Primitive::SubpixelSprite(sprite) => &mut sprite.content_mask, - Primitive::PolychromeSprite(sprite) => &mut sprite.content_mask, - Primitive::Surface(surface) => &mut surface.content_mask, - } - } -} - -#[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) -)] -struct BatchIterator<'a> { - shadows_start: usize, - shadows_iter: Peekable>, - quads_start: usize, - quads_iter: Peekable>, - paths_start: usize, - paths_iter: Peekable>>, - underlines_start: usize, - underlines_iter: Peekable>, - monochrome_sprites_start: usize, - monochrome_sprites_iter: Peekable>, - subpixel_sprites_start: usize, - subpixel_sprites_iter: Peekable>, - polychrome_sprites_start: usize, - polychrome_sprites_iter: Peekable>, - surfaces_start: usize, - surfaces_iter: Peekable>, -} - -impl<'a> Iterator for BatchIterator<'a> { - type Item = PrimitiveBatch; - - fn next(&mut self) -> Option { - 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.subpixel_sprites_iter.peek().map(|s| s.order), - PrimitiveKind::SubpixelSprite, - ), - ( - 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_and_kind) = if first.0.is_some() { - (first.1, (second.0.unwrap_or(u32::MAX), second.1)) - } 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, batch_kind) < max_order_and_kind) - .is_some() - { - shadows_end += 1; - } - self.shadows_start = shadows_end; - Some(PrimitiveBatch::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, batch_kind) < max_order_and_kind) - .is_some() - { - quads_end += 1; - } - self.quads_start = quads_end; - Some(PrimitiveBatch::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, batch_kind) < max_order_and_kind) - .is_some() - { - paths_end += 1; - } - self.paths_start = paths_end; - Some(PrimitiveBatch::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, batch_kind) < max_order_and_kind) - .is_some() - { - underlines_end += 1; - } - self.underlines_start = underlines_end; - Some(PrimitiveBatch::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, batch_kind) < max_order_and_kind - && sprite.tile.texture_id == texture_id - }) - .is_some() - { - sprites_end += 1; - } - self.monochrome_sprites_start = sprites_end; - Some(PrimitiveBatch::MonochromeSprites { - texture_id, - range: sprites_start..sprites_end, - }) - } - PrimitiveKind::SubpixelSprite => { - let texture_id = self.subpixel_sprites_iter.peek().unwrap().tile.texture_id; - let sprites_start = self.subpixel_sprites_start; - let mut sprites_end = sprites_start + 1; - self.subpixel_sprites_iter.next(); - while self - .subpixel_sprites_iter - .next_if(|sprite| { - (sprite.order, batch_kind) < max_order_and_kind - && sprite.tile.texture_id == texture_id - }) - .is_some() - { - sprites_end += 1; - } - self.subpixel_sprites_start = sprites_end; - Some(PrimitiveBatch::SubpixelSprites { - texture_id, - range: 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 = sprites_start + 1; - self.polychrome_sprites_iter.next(); - while self - .polychrome_sprites_iter - .next_if(|sprite| { - (sprite.order, batch_kind) < max_order_and_kind - && sprite.tile.texture_id == texture_id - }) - .is_some() - { - sprites_end += 1; - } - self.polychrome_sprites_start = sprites_end; - Some(PrimitiveBatch::PolychromeSprites { - texture_id, - range: 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, batch_kind) < max_order_and_kind) - .is_some() - { - surfaces_end += 1; - } - self.surfaces_start = surfaces_end; - Some(PrimitiveBatch::Surfaces(surfaces_start..surfaces_end)) - } - } - } -} - -#[derive(Debug)] -#[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) -)] -#[allow(missing_docs)] -pub enum PrimitiveBatch { - Shadows(Range), - Quads(Range), - Paths(Range), - Underlines(Range), - MonochromeSprites { - texture_id: AtlasTextureId, - range: Range, - }, - #[cfg_attr(target_os = "macos", allow(dead_code))] - SubpixelSprites { - texture_id: AtlasTextureId, - range: Range, - }, - PolychromeSprites { - texture_id: AtlasTextureId, - range: Range, - }, - Surfaces(Range), -} - -impl PrimitiveBatch { - #[expect(missing_docs)] - pub fn label(&self) -> String { - match self { - Self::Shadows(range) => format!("shadows ({})", range.len()), - Self::Quads(range) => format!("quads ({})", range.len()), - Self::Paths(range) => format!("paths ({})", range.len()), - Self::Underlines(range) => format!("underlines ({})", range.len()), - Self::MonochromeSprites { texture_id, range } => { - format!( - "monochrome sprites ({}) on atlas {}", - range.len(), - texture_id.index - ) - } - Self::SubpixelSprites { texture_id, range } => { - format!( - "subpixel sprites ({}) on atlas {}", - range.len(), - texture_id.index - ) - } - Self::PolychromeSprites { texture_id, range } => { - format!( - "polychrome sprites ({}) on atlas {}", - range.len(), - texture_id.index - ) - } - Self::Surfaces(range) => format!("surfaces ({})", range.len()), - } - } -} - -#[derive(Default, Debug, Copy, Clone)] -#[repr(C)] -#[expect(missing_docs)] -pub struct Quad { - pub order: DrawOrder, - pub border_style: BorderStyle, - pub bounds: Bounds, - pub content_mask: ContentMask, - pub background: Background, - pub border_color: Hsla, - pub corner_radii: Corners, - pub border_widths: Edges, -} - -impl From for Primitive { - fn from(quad: Quad) -> Self { - Primitive::Quad(quad) - } -} - -#[derive(Debug, Copy, Clone)] -#[repr(C)] -#[expect(missing_docs)] -pub struct Underline { - pub order: DrawOrder, - pub pad: u32, // align to 8 bytes - pub bounds: Bounds, - pub content_mask: ContentMask, - pub color: Hsla, - pub thickness: ScaledPixels, - pub wavy: PaddedBool32, -} - -impl From for Primitive { - fn from(underline: Underline) -> Self { - Primitive::Underline(underline) - } -} - -#[derive(Debug, Copy, Clone)] -#[repr(C)] -#[expect(missing_docs)] -pub struct Shadow { - pub order: DrawOrder, - pub blur_radius: ScaledPixels, - pub bounds: Bounds, - pub corner_radii: Corners, - pub content_mask: ContentMask, - pub color: Hsla, - pub element_bounds: Bounds, - pub element_corner_radii: Corners, - /// 0 = drop shadow (rendered outside the element), 1 = inset shadow (rendered inside). - pub inset: u32, - pub pad: u32, // align to 8 bytes -} - -impl From for Primitive { - fn from(shadow: Shadow) -> Self { - Primitive::Shadow(shadow) - } -} - -/// The style of a border. -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub enum BorderStyle { - /// A solid border. - #[default] - Solid = 0, - /// A dashed border. - Dashed = 1, -} - -/// A data type representing a 2 dimensional transformation that can be applied to an element. -#[derive(Debug, Clone, Copy, PartialEq)] -#[repr(C)] -pub struct TransformationMatrix { - /// 2x2 matrix containing rotation and scale, - /// stored row-major - pub rotation_scale: [[f32; 2]; 2], - /// translation vector - pub translation: [f32; 2], -} - -impl Eq for TransformationMatrix {} - -impl TransformationMatrix { - /// The unit matrix, has no effect. - pub fn unit() -> Self { - Self { - rotation_scale: [[1.0, 0.0], [0.0, 1.0]], - translation: [0.0, 0.0], - } - } - - /// Move the origin by a given point - pub fn translate(mut self, point: Point) -> Self { - self.compose(Self { - rotation_scale: [[1.0, 0.0], [0.0, 1.0]], - translation: [point.x.0, point.y.0], - }) - } - - /// Clockwise rotation in radians around the origin - pub fn rotate(self, angle: Radians) -> Self { - self.compose(Self { - rotation_scale: [ - [angle.0.cos(), -angle.0.sin()], - [angle.0.sin(), angle.0.cos()], - ], - translation: [0.0, 0.0], - }) - } - - /// Scale around the origin - pub fn scale(self, size: Size) -> Self { - self.compose(Self { - rotation_scale: [[size.width, 0.0], [0.0, size.height]], - translation: [0.0, 0.0], - }) - } - - /// Perform matrix multiplication with another transformation - /// to produce a new transformation that is the result of - /// applying both transformations: first, `other`, then `self`. - #[inline] - pub fn compose(self, other: TransformationMatrix) -> TransformationMatrix { - if other == Self::unit() { - return self; - } - // Perform matrix multiplication - TransformationMatrix { - rotation_scale: [ - [ - self.rotation_scale[0][0] * other.rotation_scale[0][0] - + self.rotation_scale[0][1] * other.rotation_scale[1][0], - self.rotation_scale[0][0] * other.rotation_scale[0][1] - + self.rotation_scale[0][1] * other.rotation_scale[1][1], - ], - [ - self.rotation_scale[1][0] * other.rotation_scale[0][0] - + self.rotation_scale[1][1] * other.rotation_scale[1][0], - self.rotation_scale[1][0] * other.rotation_scale[0][1] - + self.rotation_scale[1][1] * other.rotation_scale[1][1], - ], - ], - translation: [ - self.translation[0] - + self.rotation_scale[0][0] * other.translation[0] - + self.rotation_scale[0][1] * other.translation[1], - self.translation[1] - + self.rotation_scale[1][0] * other.translation[0] - + self.rotation_scale[1][1] * other.translation[1], - ], - } - } - - /// Apply transformation to a point, mainly useful for debugging - pub fn apply(&self, point: Point) -> Point { - let input = [point.x.0, point.y.0]; - let mut output = self.translation; - for (i, output_cell) in output.iter_mut().enumerate() { - for (k, input_cell) in input.iter().enumerate() { - *output_cell += self.rotation_scale[i][k] * *input_cell; - } - } - Point::new(output[0].into(), output[1].into()) - } -} - -impl Default for TransformationMatrix { - fn default() -> Self { - Self::unit() - } -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -#[expect(missing_docs)] -pub struct MonochromeSprite { - pub order: DrawOrder, - pub pad: u32, - pub bounds: Bounds, - pub content_mask: ContentMask, - pub color: Hsla, - pub tile: AtlasTile, - pub transformation: TransformationMatrix, -} - -impl From for Primitive { - fn from(sprite: MonochromeSprite) -> Self { - Primitive::MonochromeSprite(sprite) - } -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -#[expect(missing_docs)] -pub struct SubpixelSprite { - pub order: DrawOrder, - pub pad: u32, // align to 8 bytes - pub bounds: Bounds, - pub content_mask: ContentMask, - pub color: Hsla, - pub tile: AtlasTile, - pub transformation: TransformationMatrix, -} - -impl From for Primitive { - fn from(sprite: SubpixelSprite) -> Self { - Primitive::SubpixelSprite(sprite) - } -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -#[expect(missing_docs)] -pub struct PolychromeSprite { - pub order: DrawOrder, - pub pad: u32, - pub grayscale: PaddedBool32, - pub opacity: f32, - pub bounds: Bounds, - pub content_mask: ContentMask, - pub corner_radii: Corners, - pub tile: AtlasTile, -} - -impl From for Primitive { - fn from(sprite: PolychromeSprite) -> Self { - Primitive::PolychromeSprite(sprite) - } -} - -#[derive(Clone, Debug)] -#[allow(missing_docs)] -pub struct PaintSurface { - pub order: DrawOrder, - pub bounds: Bounds, - pub content_mask: ContentMask, - #[cfg(target_os = "macos")] - pub image_buffer: core_video::pixel_buffer::CVPixelBuffer, -} - -impl From for Primitive { - fn from(surface: PaintSurface) -> Self { - Primitive::Surface(surface) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[expect(missing_docs)] -pub struct PathId(pub usize); - -/// A line made up of a series of vertices and control points. -#[derive(Clone, Debug)] -#[expect(missing_docs)] -pub struct Path { - pub id: PathId, - pub order: DrawOrder, - pub bounds: Bounds

, - pub content_mask: ContentMask

, - pub vertices: Vec>, - pub color: Background, - start: Point

, - current: Point

, - contour_count: usize, -} - -impl Path { - /// Create a new path with the given starting point. - pub fn new(start: Point) -> Self { - Self { - id: PathId(0), - order: DrawOrder::default(), - vertices: Vec::new(), - start, - current: start, - bounds: Bounds { - origin: start, - size: Default::default(), - }, - content_mask: Default::default(), - color: Default::default(), - contour_count: 0, - } - } - - /// Scale this path by the given factor. - pub fn scale(&self, factor: f32) -> Path { - 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, - } - } - - /// Move the start, current point to the given point. - pub fn move_to(&mut self, to: Point) { - self.contour_count += 1; - self.start = to; - self.current = to; - } - - /// Draw a straight line from the current point to the given point. - pub fn line_to(&mut self, to: Point) { - 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; - } - - /// Draw a curve from the current point to the given point, using the given control point. - pub fn curve_to(&mut self, to: Point, ctrl: Point) { - 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; - } - - /// Push a triangle to the Path. - pub fn push_triangle( - &mut self, - xy: (Point, Point, Point), - st: (Point, Point, Point), - ) { - 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 Path -where - T: Clone + Debug + Default + PartialEq + PartialOrd + Add + Sub, -{ - #[allow(unused)] - #[expect(missing_docs)] - pub fn clipped_bounds(&self) -> Bounds { - self.bounds.intersect(&self.content_mask.bounds) - } -} - -impl From> for Primitive { - fn from(path: Path) -> Self { - Primitive::Path(path) - } -} - -#[derive(Clone, Debug)] -#[repr(C)] -#[expect(missing_docs)] -pub struct PathVertex { - pub xy_position: Point

, - pub st_position: Point, - pub content_mask: ContentMask

, -} - -#[expect(missing_docs)] -impl PathVertex { - pub fn scale(&self, factor: f32) -> PathVertex { - PathVertex { - xy_position: self.xy_position.scale(factor), - st_position: self.st_position, - content_mask: self.content_mask.scale(factor), - } - } -} diff --git a/crates/gpui_pre/src/shared_uri.rs b/crates/gpui_pre/src/shared_uri.rs deleted file mode 100644 index e257aaf0..00000000 --- a/crates/gpui_pre/src/shared_uri.rs +++ /dev/null @@ -1,25 +0,0 @@ -use derive_more::{Deref, DerefMut}; - -use crate::SharedString; - -/// A [`SharedString`] containing a URI. -#[derive(Deref, DerefMut, Default, PartialEq, Eq, Hash, Clone)] -pub struct SharedUri(SharedString); - -impl std::fmt::Debug for SharedUri { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - -impl std::fmt::Display for SharedUri { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0.as_ref()) - } -} - -impl> From for SharedUri { - fn from(value: T) -> Self { - Self(value.into()) - } -} diff --git a/crates/gpui_pre/src/spring.rs b/crates/gpui_pre/src/spring.rs deleted file mode 100644 index efc87806..00000000 --- a/crates/gpui_pre/src/spring.rs +++ /dev/null @@ -1,830 +0,0 @@ -use std::{ops::RangeInclusive, time::Duration}; - -use crate::{Hsla, Pixels, Rems, Rgba}; - -const CRITICAL_DAMPING_TOLERANCE: f32 = 1e-4; -const DEFAULT_SPRING_EPSILON: f32 = 0.001; - -/// The physical parameters of a damped harmonic oscillator. -/// -/// `stiffness` and `mass` must be finite and positive. `damping` must be finite -/// and non-negative. -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct SpringConfig { - /// The spring stiffness, conventionally written as $k$. - pub stiffness: f32, - /// The viscous damping coefficient, conventionally written as $c$. - pub damping: f32, - /// The moving mass, conventionally written as $m$. - pub mass: f32, -} - -impl SpringConfig { - /// Creates a spring from its physical parameters. - pub const fn new(stiffness: f32, damping: f32, mass: f32) -> Self { - Self { - stiffness, - damping, - mass, - } - } - - /// Returns the natural angular frequency and damping ratio $(\omega_0, \zeta)$. - pub fn canonical(&self) -> (f32, f32) { - let natural_frequency = (self.stiffness / self.mass).sqrt(); - let damping_ratio = self.damping / (2.0 * (self.stiffness * self.mass).sqrt()); - (natural_frequency, damping_ratio) - } - - /// Advances a spring toward a target that remains fixed for `delta_time`. - /// - /// This analytic step is independent of frame rate and preserves velocity, - /// allowing an interrupted spring to be retargeted without restarting it. - pub fn step(&self, state: SpringState, target: f32, delta_time: f32) -> SpringState { - let propagator = self.propagator(delta_time); - let displacement = state.position - target; - - SpringState { - position: target + propagator[0][0] * displacement + propagator[0][1] * state.velocity, - velocity: propagator[1][0] * displacement + propagator[1][1] * state.velocity, - } - } - - /// Advances a spring toward a target moving at a constant velocity. - /// - /// A first-order hold avoids the frame-rate-dependent lag introduced by - /// treating a dragged target as stationary between frames. - pub fn step_ramp( - &self, - state: SpringState, - target: f32, - target_velocity: f32, - delta_time: f32, - ) -> SpringState { - let (natural_frequency, damping_ratio) = self.canonical(); - let steady_state_lag = -2.0 * damping_ratio * target_velocity / natural_frequency; - let displacement = state.position - target - steady_state_lag; - let velocity = state.velocity - target_velocity; - let propagator = self.propagator(delta_time); - let target = target + target_velocity * delta_time; - - SpringState { - position: target - + steady_state_lag - + propagator[0][0] * displacement - + propagator[0][1] * velocity, - velocity: target_velocity - + propagator[1][0] * displacement - + propagator[1][1] * velocity, - } - } - - /// Returns the exact state-transition matrix for a constant target. - /// - /// Materializing this matrix is useful when many springs share the same - /// configuration and frame delta. A matrix must not be reused when the - /// frame delta changes. - pub fn propagator(&self, delta_time: f32) -> [[f32; 2]; 2] { - let (natural_frequency, damping_ratio) = self.canonical(); - - if damping_ratio < 1.0 - CRITICAL_DAMPING_TOLERANCE { - let decay = damping_ratio * natural_frequency; - let damped_frequency = natural_frequency * (1.0 - damping_ratio * damping_ratio).sqrt(); - let exponential = (-decay * delta_time).exp(); - let (sine, cosine) = (damped_frequency * delta_time).sin_cos(); - let sine_over_frequency = sine / damped_frequency; - - [ - [ - exponential * (cosine + decay * sine_over_frequency), - exponential * sine_over_frequency, - ], - [ - -exponential * natural_frequency * natural_frequency * sine_over_frequency, - exponential * (cosine - decay * sine_over_frequency), - ], - ] - } else if damping_ratio > 1.0 + CRITICAL_DAMPING_TOLERANCE { - let root = (damping_ratio * damping_ratio - 1.0).sqrt(); - let root_sum = damping_ratio + root; - let slow_root = -natural_frequency / root_sum; - let fast_root = -natural_frequency * root_sum; - let denominator = slow_root - fast_root; - let slow_exponential = (slow_root * delta_time).exp(); - let fast_exponential = (fast_root * delta_time).exp(); - - [ - [ - (-fast_root * slow_exponential + slow_root * fast_exponential) / denominator, - (slow_exponential - fast_exponential) / denominator, - ], - [ - slow_root * fast_root * (fast_exponential - slow_exponential) / denominator, - (slow_root * slow_exponential - fast_root * fast_exponential) / denominator, - ], - ] - } else { - let exponential = (-natural_frequency * delta_time).exp(); - - [ - [ - exponential * (1.0 + natural_frequency * delta_time), - exponential * delta_time, - ], - [ - -exponential * natural_frequency * natural_frequency * delta_time, - exponential * (1.0 - natural_frequency * delta_time), - ], - ] - } - } - - /// Tests both displacement and velocity against a positional tolerance. - /// - /// Velocity is compared with `epsilon * natural_frequency`, giving it the - /// corresponding animated-units-per-second scale. - pub fn is_settled(&self, state: SpringState, target: f32, epsilon: f32) -> bool { - let (natural_frequency, _) = self.canonical(); - epsilon.is_finite() - && epsilon >= 0.0 - && (state.position - target).abs() <= epsilon - && state.velocity.abs() <= epsilon * natural_frequency - } - - /// Returns a conservative time after which the spring remains settled. - /// - /// An undamped spring has no finite settling time and returns - /// [`Duration::MAX`]. - pub fn settle_time(&self, state: SpringState, target: f32, epsilon: f32) -> Duration { - let displacement = state.position - target; - if displacement == 0.0 && state.velocity == 0.0 { - return Duration::ZERO; - } - - let (natural_frequency, damping_ratio) = self.canonical(); - if !natural_frequency.is_finite() - || natural_frequency <= 0.0 - || !damping_ratio.is_finite() - || damping_ratio <= 0.0 - || !epsilon.is_finite() - || epsilon <= 0.0 - { - return Duration::MAX; - } - - let velocity_threshold = epsilon * natural_frequency; - - if damping_ratio < 1.0 - CRITICAL_DAMPING_TOLERANCE { - let decay = damping_ratio * natural_frequency; - let damped_frequency = natural_frequency * (1.0 - damping_ratio * damping_ratio).sqrt(); - let sine_coefficient = (state.velocity + decay * displacement) / damped_frequency; - let position_envelope = displacement.hypot(sine_coefficient); - let velocity_cosine = damped_frequency * sine_coefficient - decay * displacement; - let velocity_sine = -damped_frequency * displacement - decay * sine_coefficient; - let velocity_envelope = velocity_cosine.hypot(velocity_sine); - - find_settle_time( - epsilon, - velocity_threshold, - 0.0, - natural_frequency, - move |time| { - let exponential = (-decay * time).exp(); - ( - position_envelope * exponential, - velocity_envelope * exponential, - ) - }, - ) - } else if damping_ratio > 1.0 + CRITICAL_DAMPING_TOLERANCE { - let root = (damping_ratio * damping_ratio - 1.0).sqrt(); - let root_sum = damping_ratio + root; - let slow_root = -natural_frequency / root_sum; - let fast_root = -natural_frequency * root_sum; - let denominator = slow_root - fast_root; - let slow_coefficient = (state.velocity - fast_root * displacement) / denominator; - let fast_coefficient = (slow_root * displacement - state.velocity) / denominator; - - find_settle_time( - epsilon, - velocity_threshold, - 0.0, - natural_frequency, - move |time| { - let slow_term = slow_coefficient.abs() * (slow_root * time).exp(); - let fast_term = fast_coefficient.abs() * (fast_root * time).exp(); - ( - slow_term + fast_term, - slow_root.abs() * slow_term + fast_root.abs() * fast_term, - ) - }, - ) - } else { - let linear_coefficient = state.velocity + natural_frequency * displacement; - let position_constant = displacement.abs(); - let position_linear = linear_coefficient.abs(); - let velocity_constant = (linear_coefficient - natural_frequency * displacement).abs(); - let velocity_linear = natural_frequency * linear_coefficient.abs(); - let position_decay_start = - envelope_decay_start(position_constant, position_linear, natural_frequency); - let velocity_decay_start = - envelope_decay_start(velocity_constant, velocity_linear, natural_frequency); - - find_settle_time( - epsilon, - velocity_threshold, - position_decay_start.max(velocity_decay_start), - natural_frequency, - move |time| { - let exponential = (-natural_frequency * time).exp(); - ( - (position_constant + position_linear * time) * exponential, - (velocity_constant + velocity_linear * time) * exponential, - ) - }, - ) - } - } -} - -/// The instantaneous position and velocity of a spring. -#[derive(Clone, Copy, Debug, Default, PartialEq)] -pub struct SpringState { - /// The current value in the animated unit. - pub position: f32, - /// The current value's change per second. - pub velocity: f32, -} - -/// A value that can be targeted by a one-dimensional spring. -/// -/// Implementations may project the spring coordinate into a richer output, -/// allowing a discrete state or a path through a multidimensional value to be -/// driven by one spring. -pub trait SpringTarget: 'static { - /// The value supplied to the spring animator. - type Output; - - /// Returns the target in the spring's coordinate space. - fn target(&self) -> f32; - - /// Projects a spring coordinate into the animated output. - fn resolve(&self, value: f32) -> Self::Output; -} - -impl SpringTarget for f32 { - type Output = f32; - - fn target(&self) -> f32 { - *self - } - - fn resolve(&self, value: f32) -> Self::Output { - value - } -} - -impl SpringTarget for Pixels { - type Output = Pixels; - - fn target(&self) -> f32 { - self.as_f32() - } - - fn resolve(&self, value: f32) -> Self::Output { - Pixels::from(value) - } -} - -impl SpringTarget for Rems { - type Output = Rems; - - fn target(&self) -> f32 { - self.0 - } - - fn resolve(&self, value: f32) -> Self::Output { - Rems(value) - } -} - -impl SpringTarget for bool { - type Output = AnimationPhase; - - fn target(&self) -> f32 { - if *self { 1.0 } else { 0.0 } - } - - fn resolve(&self, value: f32) -> Self::Output { - AnimationPhase(value) - } -} - -/// A potentially overshooting coordinate within an animation. -/// -/// Phases are not restricted to 0..1. Multi-stage animations can assign each -/// stage its own coordinate and interpolate over ranges such as 1..=2. -#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] -pub struct AnimationPhase( - /// The unbounded phase coordinate. - pub f32, -); - -impl AnimationPhase { - /// Restricts this phase to the bounds of a range. - pub fn clamp(self, range: RangeInclusive) -> Self { - let (first, second) = range.into_inner(); - Self(self.0.clamp(first.min(second), first.max(second))) - } - - /// Interpolates between values using 0 and 1 as their phase coordinates. - pub fn interpolate(self, from: T, to: T) -> T { - T::interpolate(from, to, self.0) - } - - /// Interpolates between values without extrapolating beyond 0 and 1. - pub fn interpolate_clamped(self, from: T, to: T) -> T { - T::interpolate(from, to, self.0.clamp(0.0, 1.0)) - } - - /// Interpolates between values assigned to arbitrary phase coordinates. - pub fn interpolate_between( - self, - range: RangeInclusive, - from: T, - to: T, - ) -> T { - let (start, end) = range.into_inner(); - let phase = if start == end { - if self.0 < start { 0.0 } else { 1.0 } - } else { - (self.0 - start) / (end - start) - }; - T::interpolate(from, to, phase) - } - - /// Interpolates over arbitrary phase coordinates without extrapolating. - pub fn interpolate_between_clamped( - self, - range: RangeInclusive, - from: T, - to: T, - ) -> T { - let (start, end) = range.into_inner(); - let phase = if start == end { - if self.0 < start { 0.0 } else { 1.0 } - } else { - ((self.0 - start) / (end - start)).clamp(0.0, 1.0) - }; - T::interpolate(from, to, phase) - } -} - -impl From for AnimationPhase { - fn from(value: f32) -> Self { - Self(value) - } -} - -impl From for AnimationPhase { - fn from(value: bool) -> Self { - Self(if value { 1.0 } else { 0.0 }) - } -} - -impl SpringTarget for AnimationPhase { - type Output = AnimationPhase; - - fn target(&self) -> f32 { - self.0 - } - - fn resolve(&self, value: f32) -> Self::Output { - Self(value) - } -} - -/// A value that supports linear interpolation and extrapolation. -pub trait Interpolate: Sized { - /// Resolves the value at `phase`, where 0 is `from` and 1 is `to`. - fn interpolate(from: Self, to: Self, phase: f32) -> Self; -} - -impl Interpolate for f32 { - fn interpolate(from: Self, to: Self, phase: f32) -> Self { - from + (to - from) * phase - } -} - -impl Interpolate for Pixels { - fn interpolate(from: Self, to: Self, phase: f32) -> Self { - from + (to - from) * phase - } -} - -impl Interpolate for Rems { - fn interpolate(from: Self, to: Self, phase: f32) -> Self { - from + (to - from) * phase - } -} - -impl Interpolate for Rgba { - fn interpolate(from: Self, to: Self, phase: f32) -> Self { - Self { - r: f32::interpolate(from.r, to.r, phase), - g: f32::interpolate(from.g, to.g, phase), - b: f32::interpolate(from.b, to.b, phase), - a: f32::interpolate(from.a, to.a, phase), - } - } -} - -impl Interpolate for Hsla { - fn interpolate(from: Self, to: Self, phase: f32) -> Self { - let hue_delta = (to.h - from.h + 0.5).rem_euclid(1.0) - 0.5; - Self { - h: (from.h + hue_delta * phase).rem_euclid(1.0), - s: f32::interpolate(from.s, to.s, phase), - l: f32::interpolate(from.l, to.l, phase), - a: f32::interpolate(from.a, to.a, phase), - } - } -} - -/// Controls how a spring advances and resolves its presentation value. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum SpringPlayback { - /// Advances toward the latest target, preserving velocity across retargets. - #[default] - Running, - /// Holds the current position and velocity until playback resumes. - Paused, - /// Holds the current position and discards velocity. - Stopped, - /// Snaps to the latest target and discards velocity. - Completed, - /// Returns to the initial value and discards velocity. - Cancelled, -} - -/// A stateful spring animation targeting a value or projected path. -#[derive(Clone, Debug)] -pub struct SpringAnimation { - pub(crate) config: SpringConfig, - pub(crate) target: T, - pub(crate) epsilon: f32, - pub(crate) initial: Option, - pub(crate) playback: SpringPlayback, -} - -impl SpringAnimation<()> { - /// Creates a spring animation builder. - pub fn new(config: SpringConfig) -> Self { - Self { - config, - target: (), - epsilon: DEFAULT_SPRING_EPSILON, - initial: None, - playback: SpringPlayback::Running, - } - } - - /// Sets the value or path targeted by this spring. - pub fn to(self, target: T) -> SpringAnimation { - let SpringAnimation { - config, - target: (), - epsilon, - initial, - playback, - } = self; - SpringAnimation { - config, - target, - epsilon, - initial, - playback, - } - } -} - -impl SpringAnimation { - /// Sets the settling tolerance in the target's scalar coordinate space. - pub fn with_epsilon(mut self, epsilon: f32) -> Self { - self.epsilon = epsilon; - self - } - - /// Sets how the spring advances or resolves its current value. - pub fn playback(mut self, playback: SpringPlayback) -> Self { - self.playback = playback; - self - } -} - -impl SpringAnimation { - /// Sets the coordinate used when this element has no prior spring state. - pub fn from(mut self, initial: T) -> Self { - self.initial = Some(initial.target()); - self - } -} - -/// Adapts a spring starting at zero with no velocity to GPUI's duration-based easing API. -/// -/// The returned easing can overshoot the normalized 0..1 output range. Retargeting -/// this duration-based form restarts the spring; use [`SpringConfig::step`] when -/// preserving velocity matters. -pub fn sampled_easing(config: SpringConfig, epsilon: f32) -> (Duration, impl Fn(f32) -> f32) { - let initial_state = SpringState { - position: 0.0, - velocity: 0.0, - }; - let duration = config.settle_time(initial_state, 1.0, epsilon); - let duration_seconds = duration.as_secs_f32(); - - (duration, move |progress| { - if progress <= 0.0 { - 0.0 - } else if progress >= 1.0 { - 1.0 - } else { - config - .step(initial_state, 1.0, progress * duration_seconds) - .position - } - }) -} - -fn envelope_decay_start(constant: f32, linear: f32, decay: f32) -> f32 { - if linear == 0.0 { - 0.0 - } else { - (1.0 / decay - constant / linear).max(0.0) - } -} - -fn find_settle_time( - position_threshold: f32, - velocity_threshold: f32, - decay_start: f32, - natural_frequency: f32, - envelope: impl Fn(f32) -> (f32, f32), -) -> Duration { - let is_below_threshold = |time| { - let (position, velocity) = envelope(time); - position <= position_threshold && velocity <= velocity_threshold - }; - - if is_below_threshold(decay_start) { - return duration_from_secs(decay_start); - } - - let mut lower_bound = decay_start; - let mut upper_bound = decay_start.max(natural_frequency.recip()); - while !is_below_threshold(upper_bound) { - lower_bound = upper_bound; - upper_bound *= 2.0; - if !upper_bound.is_finite() { - return Duration::MAX; - } - } - - for _ in 0..32 { - let midpoint = (lower_bound + upper_bound) / 2.0; - if is_below_threshold(midpoint) { - upper_bound = midpoint; - } else { - lower_bound = midpoint; - } - } - - duration_from_secs(upper_bound) -} - -fn duration_from_secs(seconds: f32) -> Duration { - if !seconds.is_finite() || seconds >= Duration::MAX.as_secs_f32() { - Duration::MAX - } else if seconds <= 0.0 { - Duration::ZERO - } else { - Duration::from_secs_f32(seconds) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const EPSILON: f32 = 1e-4; - - #[test] - fn spring_targets_resolve_typed_outputs() { - assert_eq!(12.0_f32.target(), 12.0); - assert_eq!(12.0_f32.resolve(14.0), 14.0); - assert_eq!(Pixels::from(12.0).target(), 12.0); - assert_eq!(Pixels::from(12.0).resolve(14.0), Pixels::from(14.0)); - assert_eq!(false.target(), 0.0); - assert_eq!(true.target(), 1.0); - assert_eq!(true.resolve(1.25), AnimationPhase(1.25)); - } - - #[test] - fn animation_phases_interpolate_over_arbitrary_ranges() { - let phase = AnimationPhase(1.5); - assert_eq!(phase.interpolate_between(1.0..=2.0, 10.0, 20.0), 15.0); - assert_eq!( - phase.interpolate_between_clamped(2.0..=3.0, 10.0, 20.0), - 10.0 - ); - assert_eq!( - AnimationPhase(3.5).interpolate_between(2.0..=3.0, 10.0, 20.0), - 25.0 - ); - } - - #[test] - fn hsla_interpolation_takes_the_shortest_hue_path() { - let from = Hsla { - h: 0.9, - s: 0.5, - l: 0.5, - a: 1.0, - }; - let to = Hsla { - h: 0.1, - s: 1.0, - l: 0.75, - a: 0.5, - }; - let result = AnimationPhase(0.5).interpolate(from, to); - - assert!(result.h < EPSILON || (1.0 - result.h) < EPSILON); - assert!((result.s - 0.75).abs() < EPSILON); - assert!((result.l - 0.625).abs() < EPSILON); - assert!((result.a - 0.75).abs() < EPSILON); - } - - #[test] - fn propagators_compose_and_have_expected_determinant() { - for damping_ratio in [0.4, 1.0, 1.5] { - let natural_frequency = 12.0; - let config = SpringConfig::new( - natural_frequency * natural_frequency, - 2.0 * damping_ratio * natural_frequency, - 1.0, - ); - let first = config.propagator(0.013); - let second = config.propagator(0.021); - let combined = multiply(second, first); - let direct = config.propagator(0.034); - - for row in 0..2 { - for column in 0..2 { - assert!( - (combined[row][column] - direct[row][column]).abs() < 2e-4, - "{damping_ratio}: {combined:?} != {direct:?}" - ); - } - } - - let determinant = direct[0][0] * direct[1][1] - direct[0][1] * direct[1][0]; - let expected = (-2.0 * damping_ratio * natural_frequency * 0.034).exp(); - assert!((determinant - expected).abs() < 2e-4); - } - } - - #[test] - fn step_preserves_semigroup_for_every_damping_regime() { - let state = SpringState { - position: -3.0, - velocity: 5.0, - }; - for damping in [4.0, 20.0, 40.0] { - let config = SpringConfig::new(100.0, damping, 1.0); - let stepped = config.step(config.step(state, 7.0, 0.013), 7.0, 0.021); - let direct = config.step(state, 7.0, 0.034); - - assert!((stepped.position - direct.position).abs() < 2e-4); - assert!((stepped.velocity - direct.velocity).abs() < 2e-4); - } - } - - #[test] - fn ramp_tracks_steady_state_lag() { - let natural_frequency = 10.0; - let damping_ratio = 0.8; - let target_velocity = 3.0; - let config = SpringConfig::new( - natural_frequency * natural_frequency, - 2.0 * damping_ratio * natural_frequency, - 1.0, - ); - let lag = -2.0 * damping_ratio * target_velocity / natural_frequency; - let state = SpringState { - position: lag, - velocity: target_velocity, - }; - let next = config.step_ramp(state, 0.0, target_velocity, 0.25); - - assert!((next.position - (target_velocity * 0.25 + lag)).abs() < EPSILON); - assert!((next.velocity - target_velocity).abs() < EPSILON); - } - - #[test] - fn settling_requires_low_velocity() { - let config = SpringConfig::new(100.0, 10.0, 1.0); - assert!(!config.is_settled( - SpringState { - position: 1.0, - velocity: 1.0, - }, - 1.0, - 0.01, - )); - assert!(config.is_settled( - SpringState { - position: 1.005, - velocity: 0.05, - }, - 1.0, - 0.01, - )); - } - - #[test] - fn settle_time_is_conservative_for_every_damping_regime() { - let initial_state = SpringState { - position: -2.0, - velocity: 4.0, - }; - for damping in [4.0, 20.0, 40.0] { - let config = SpringConfig::new(100.0, damping, 1.0); - let duration = config.settle_time(initial_state, 3.0, 0.001); - assert_ne!(duration, Duration::MAX); - - for additional_time in [0.0, 0.1, 1.0] { - let state = - config.step(initial_state, 3.0, duration.as_secs_f32() + additional_time); - assert!( - config.is_settled(state, 3.0, 0.001), - "{damping}: {duration:?} produced {state:?}" - ); - } - } - } - - #[test] - fn settle_time_accounts_for_motion_outside_an_instantaneous_tolerance() { - let config = SpringConfig::new(100.0, 2.0, 1.0); - let state = SpringState { - position: 1.125, - velocity: 1.25, - }; - assert!(config.is_settled(state, 1.0, 0.125)); - - let duration = config.settle_time(state, 1.0, 0.125); - assert!(duration > Duration::ZERO); - assert!(config.is_settled(config.step(state, 1.0, duration.as_secs_f32()), 1.0, 0.125,)); - } - - #[test] - fn undamped_spring_never_settles() { - let config = SpringConfig::new(100.0, 0.0, 1.0); - assert_eq!( - config.settle_time( - SpringState { - position: 0.0, - velocity: 0.0, - }, - 1.0, - 0.001, - ), - Duration::MAX - ); - } - - #[test] - fn sampled_easing_has_exact_endpoints_and_can_overshoot() { - let config = SpringConfig::new(100.0, 6.0, 1.0); - let (duration, easing) = sampled_easing(config, 0.001); - - assert_ne!(duration, Duration::MAX); - assert_eq!(easing(0.0), 0.0); - assert_eq!(easing(1.0), 1.0); - assert!((1..100).any(|step| easing(step as f32 / 100.0) > 1.0)); - } - - fn multiply(left: [[f32; 2]; 2], right: [[f32; 2]; 2]) -> [[f32; 2]; 2] { - [ - [ - left[0][0] * right[0][0] + left[0][1] * right[1][0], - left[0][0] * right[0][1] + left[0][1] * right[1][1], - ], - [ - left[1][0] * right[0][0] + left[1][1] * right[1][0], - left[1][0] * right[0][1] + left[1][1] * right[1][1], - ], - ] - } -} diff --git a/crates/gpui_pre/src/style.rs b/crates/gpui_pre/src/style.rs deleted file mode 100644 index 49e3c184..00000000 --- a/crates/gpui_pre/src/style.rs +++ /dev/null @@ -1,1583 +0,0 @@ -use std::{ - hash::{Hash, Hasher}, - iter, mem, - ops::Range, -}; - -use crate::{ - black, phi, point, px, quad, rems, size, AbsoluteLength, App, Background, BackgroundTag, - BorderStyle, Bounds, Corners, CornersRefinement, CursorStyle, DefiniteLength, DevicePixels, - Edges, EdgesRefinement, Font, FontFallbacks, FontFeatures, FontStyle, FontWeight, GridLocation, - Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size, SizeRefinement, Styled, - TextRun, Window, -}; -use collections::HashSet; -use refineable::Refineable; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -/// Use this struct for interfacing with the 'debug_below' styling from your own elements. -/// If a parent element has this style set on it, then this struct will be set as a global in -/// GPUI. -#[cfg(debug_assertions)] -pub struct DebugBelow; - -#[cfg(debug_assertions)] -impl crate::Global for DebugBelow {} - -/// How to fit the image into the bounds of the element. -pub enum ObjectFit { - /// The image will be stretched to fill the bounds of the element. - Fill, - /// The image will be scaled to fit within the bounds of the element. - Contain, - /// The image will be scaled to cover the bounds of the element. - Cover, - /// The image will be scaled down to fit within the bounds of the element. - ScaleDown, - /// The image will maintain its original size. - None, -} - -impl ObjectFit { - /// Get the bounds of the image within the given bounds. - pub fn get_bounds( - &self, - bounds: Bounds, - image_size: Size, - ) -> Bounds { - let image_size = image_size.map(|dimension| Pixels::from(u32::from(dimension))); - let image_ratio = image_size.width / image_size.height; - let bounds_ratio = bounds.size.width / bounds.size.height; - - match self { - ObjectFit::Fill => bounds, - ObjectFit::Contain => { - let new_size = if bounds_ratio > image_ratio { - size( - image_size.width * (bounds.size.height / image_size.height), - bounds.size.height, - ) - } else { - size( - bounds.size.width, - image_size.height * (bounds.size.width / image_size.width), - ) - }; - - Bounds { - origin: point( - bounds.origin.x + (bounds.size.width - new_size.width) / 2.0, - bounds.origin.y + (bounds.size.height - new_size.height) / 2.0, - ), - size: new_size, - } - } - ObjectFit::ScaleDown => { - // Check if the image is larger than the bounds in either dimension. - if image_size.width > bounds.size.width || image_size.height > bounds.size.height { - // If the image is larger, use the same logic as Contain to scale it down. - let new_size = if bounds_ratio > image_ratio { - size( - image_size.width * (bounds.size.height / image_size.height), - bounds.size.height, - ) - } else { - size( - bounds.size.width, - image_size.height * (bounds.size.width / image_size.width), - ) - }; - - Bounds { - origin: point( - bounds.origin.x + (bounds.size.width - new_size.width) / 2.0, - bounds.origin.y + (bounds.size.height - new_size.height) / 2.0, - ), - size: new_size, - } - } else { - // If the image is smaller than or equal to the container, display it at its original size, - // centered within the container. - let original_size = size(image_size.width, image_size.height); - Bounds { - origin: point( - bounds.origin.x + (bounds.size.width - original_size.width) / 2.0, - bounds.origin.y + (bounds.size.height - original_size.height) / 2.0, - ), - size: original_size, - } - } - } - ObjectFit::Cover => { - let new_size = if bounds_ratio > image_ratio { - size( - bounds.size.width, - image_size.height * (bounds.size.width / image_size.width), - ) - } else { - size( - image_size.width * (bounds.size.height / image_size.height), - bounds.size.height, - ) - }; - - Bounds { - origin: point( - bounds.origin.x + (bounds.size.width - new_size.width) / 2.0, - bounds.origin.y + (bounds.size.height - new_size.height) / 2.0, - ), - size: new_size, - } - } - ObjectFit::None => Bounds { - origin: bounds.origin, - size: image_size, - }, - } - } -} - -/// The minimum size of a column or row in a grid layout -#[derive( - Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, JsonSchema, Serialize, Deserialize, -)] -pub enum GridTemplateMinSize { - /// The column or row size may be 0 - #[default] - Zero, - /// The column or row size can be determined by the min content - MinContent, - /// The column or row size can be determined by the max content - MaxContent, -} - -/// A simplified representation of the grid-template-* value -#[derive( - Copy, - Clone, - Refineable, - PartialEq, - Eq, - PartialOrd, - Ord, - Debug, - Default, - JsonSchema, - Serialize, - Deserialize, -)] -pub struct GridTemplate { - /// How this template directive should be repeated - pub repeat: u16, - /// The minimum size in the repeat(<>, minmax(_, 1fr)) equation - pub min_size: GridTemplateMinSize, -} - -/// The CSS styling that can be applied to an element via the `Styled` trait -#[derive(Clone, Refineable, Debug)] -#[refineable(Debug, PartialEq, Serialize, Deserialize, JsonSchema)] -pub struct Style { - /// What layout strategy should be used? - pub display: Display, - - /// Should the element be painted on screen? - pub visibility: Visibility, - - // Overflow properties - /// How children overflowing their container should affect layout - #[refineable] - pub overflow: Point, - /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes. - pub scrollbar_width: AbsoluteLength, - /// Whether both x and y axis should be scrollable at the same time. - pub allow_concurrent_scroll: bool, - /// Whether scrolling should be restricted to the input gesture's axis. - /// - /// Pixel-based scroll gestures are locked to their initially dominant axis. The lock may be - /// released when the gesture changes direction strongly. Touch phases delimit gestures when - /// available, with a timeout fallback for platforms that only emit moved events. - /// - /// This also prevents input from being remapped to another axis. For example, horizontal input - /// will not scroll a container that only has vertical overflow enabled. Mouse wheel platforms - /// typically report ordinary wheel input on the Y axis and Shift-modified input on the X axis. - /// - /// ## Motivation - /// - /// On the web when scrolling with the mouse wheel, scrolling up and down will always scroll the Y axis, even when - /// the mouse is over a horizontally-scrollable element. - /// - /// The only way to scroll horizontally is to hold down `Shift` while scrolling, which then changes the scroll axis - /// to the X axis. - /// - /// Currently, GPUI operates differently from the web in that it will scroll an element in either the X or Y axis - /// when scrolling with just the mouse wheel. This causes problems when scrolling in a vertical list that contains - /// horizontally-scrollable elements, as when you get to the horizontally-scrollable elements the scroll will be - /// hijacked. - /// - /// Ideally we would match the web's behavior and not have a need for this, but right now we're adding this opt-in - /// style property to limit the potential blast radius. - pub restrict_scroll_to_axis: bool, - - // Position properties - /// What should the `position` value of this struct use as a base offset? - pub position: Position, - /// How should the position of this element be tweaked relative to the layout defined? - #[refineable] - pub inset: Edges, - - // Size properties - /// Sets the initial size of the item - #[refineable] - pub size: Size, - /// Controls the minimum size of the item - #[refineable] - pub min_size: Size, - /// Controls the maximum size of the item - #[refineable] - pub max_size: Size, - /// Sets the preferred aspect ratio for the item. The ratio is calculated as width divided by height. - pub aspect_ratio: Option, - - // Spacing Properties - /// How large should the margin be on each side? - #[refineable] - pub margin: Edges, - /// How large should the padding be on each side? - #[refineable] - pub padding: Edges, - /// How large should the border be on each side? - #[refineable] - pub border_widths: Edges, - - // Alignment properties - /// How this node's children aligned in the cross/block axis? - pub align_items: Option, - /// How this node should be aligned in the cross/block axis. Falls back to the parents [`AlignItems`] if not set - pub align_self: Option, - /// How should content contained within this item be aligned in the cross/block axis - pub align_content: Option, - /// How should contained within this item be aligned in the main/inline axis - pub justify_content: Option, - /// How large should the gaps between items in a flex container be? - #[refineable] - pub gap: Size, - - // Flexbox properties - /// Which direction does the main axis flow in? - pub flex_direction: FlexDirection, - /// Should elements wrap, or stay in a single line? - pub flex_wrap: FlexWrap, - /// Sets the initial main axis size of the item - pub flex_basis: Length, - /// The relative rate at which this item grows when it is expanding to fill space, 0.0 is the default value, and this value must be positive. - pub flex_grow: f32, - /// The relative rate at which this item shrinks when it is contracting to fit into space, 1.0 is the default value, and this value must be positive. - pub flex_shrink: f32, - - /// The fill color of this element - pub background: Option, - - /// The border color of this element - pub border_color: Option, - - /// The border style of this element - pub border_style: BorderStyle, - - /// The radius of the corners of this element - #[refineable] - pub corner_radii: Corners, - - /// Box shadow of the element - pub box_shadow: Vec, - - /// The text style of this element - #[refineable] - pub text: TextStyleRefinement, - - /// The mouse cursor style shown when the mouse pointer is over an element. - pub mouse_cursor: Option, - - /// The opacity of this element - pub opacity: Option, - - /// The grid columns of this element - /// Roughly equivalent to the Tailwind `grid-cols-` - pub grid_cols: Option, - - /// The row span of this element - /// Equivalent to the Tailwind `grid-rows-` - pub grid_rows: Option, - - /// The grid location of this element - pub grid_location: Option, - - /// Whether to draw a red debugging outline around this element - #[cfg(debug_assertions)] - pub debug: bool, - - /// Whether to draw a red debugging outline around this element and all of its conforming children - #[cfg(debug_assertions)] - pub debug_below: bool, -} - -impl Styled for StyleRefinement { - fn style(&mut self) -> &mut StyleRefinement { - self - } -} - -impl StyleRefinement { - /// The grid location of this element - pub fn grid_location_mut(&mut self) -> &mut GridLocation { - self.grid_location.get_or_insert_default() - } -} - -/// The value of the visibility property, similar to the CSS property `visibility` -#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] -pub enum Visibility { - /// The element should be drawn as normal. - #[default] - Visible, - /// The element should not be drawn, but should still take up space in the layout. - Hidden, -} - -/// The possible values of the box-shadow property -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] -pub struct BoxShadow { - /// What color should the shadow have? - pub color: Hsla, - /// How should it be offset from its element? - pub offset: Point, - /// How much should the shadow be blurred? - pub blur_radius: Pixels, - /// How much should the shadow spread? - pub spread_radius: Pixels, - /// Whether this is an inset shadow (drawn inside the element's bounds). - pub inset: bool, -} - -impl BoxShadow { - /// Creates a new [`BoxShadow`] with the given offset and color, matching the order - /// of the CSS `box-shadow` property. Use the builder methods to set blur radius, - /// spread radius, and inset. - pub fn new(offset_x: Pixels, offset_y: Pixels, color: Hsla) -> Self { - Self { - color, - offset: point(offset_x, offset_y), - blur_radius: px(0.), - spread_radius: px(0.), - inset: false, - } - } - - /// Sets the shadow blur radius. - pub fn blur_radius(mut self, blur_radius: Pixels) -> Self { - self.blur_radius = blur_radius; - self - } - - /// Sets the shadow spread radius. - pub fn spread_radius(mut self, spread_radius: Pixels) -> Self { - self.spread_radius = spread_radius; - self - } - - /// Marks the shadow as inset (drawn inside the element's bounds). - pub fn inset(mut self) -> Self { - self.inset = true; - self - } -} - -/// How to handle whitespace in text -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -pub enum WhiteSpace { - /// Normal line wrapping when text overflows the width of the element - #[default] - Normal, - /// No line wrapping, text will overflow the width of the element - Nowrap, -} - -/// How to truncate text that overflows the width of the element -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -pub enum TextOverflow { - /// Truncate the text at the end when it doesn't fit, and represent this truncation by - /// displaying the provided string (e.g., "very long te…"). - Truncate(SharedString), - /// Truncate the text at the start when it doesn't fit, and represent this truncation by - /// displaying the provided string at the beginning (e.g., "…ong text here"). - /// Typically more adequate for file paths where the end is more important than the beginning. - TruncateStart(SharedString), - /// Truncate the text in the middle when it doesn't fit, preserving both the start and end - /// of the string (e.g., "long fi…name.rs"). Useful for filenames where both the prefix - /// and the extension are important context. - TruncateMiddle(SharedString), -} - -/// How to align text within the element -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -pub enum TextAlign { - /// Align the text to the left of the element - #[default] - Left, - - /// Center the text within the element - Center, - - /// Align the text to the right of the element - Right, -} - -/// The properties that can be used to style text in GPUI -#[derive(Refineable, Clone, Debug, PartialEq)] -#[refineable(Debug, PartialEq, Serialize, Deserialize, JsonSchema)] -pub struct TextStyle { - /// The color of the text - pub color: Hsla, - - /// The font family to use - pub font_family: SharedString, - - /// The font features to use - pub font_features: FontFeatures, - - /// The fallback fonts to use - pub font_fallbacks: Option, - - /// The font size to use, in pixels or rems. - pub font_size: AbsoluteLength, - - /// The line height to use, in pixels or fractions - pub line_height: DefiniteLength, - - /// The font weight, e.g. bold - pub font_weight: FontWeight, - - /// The font style, e.g. italic - pub font_style: FontStyle, - - /// The background color of the text - pub background_color: Option, - - /// The underline style of the text - pub underline: Option, - - /// The strikethrough style of the text - pub strikethrough: Option, - - /// How to handle whitespace in the text - pub white_space: WhiteSpace, - - /// The text should be truncated if it overflows the width of the element - pub text_overflow: Option, - - /// How the text should be aligned within the element - pub text_align: TextAlign, - - /// The number of lines to display before truncating the text - pub line_clamp: Option, -} - -impl Default for TextStyle { - fn default() -> Self { - TextStyle { - color: black(), - // todo(linux) make this configurable or choose better default - font_family: ".SystemUIFont".into(), - font_features: FontFeatures::default(), - font_fallbacks: None, - font_size: rems(1.).into(), - line_height: phi(), - font_weight: FontWeight::default(), - font_style: FontStyle::default(), - background_color: None, - underline: None, - strikethrough: None, - white_space: WhiteSpace::Normal, - text_overflow: None, - text_align: TextAlign::default(), - line_clamp: None, - } - } -} - -impl TextStyle { - /// Create a new text style with the given highlighting applied. - pub fn highlight(mut self, style: impl Into) -> Self { - let style = style.into(); - if let Some(weight) = style.font_weight { - self.font_weight = weight; - } - if let Some(style) = style.font_style { - self.font_style = style; - } - - if let Some(color) = style.color { - self.color = self.color.blend(color); - } - - if let Some(factor) = style.fade_out { - self.color.fade_out(factor); - } - - if let Some(background_color) = style.background_color { - self.background_color = Some(background_color); - } - - if let Some(underline) = style.underline { - self.underline = Some(underline); - } - - if let Some(strikethrough) = style.strikethrough { - self.strikethrough = Some(strikethrough); - } - - self - } - - /// Get the font configured for this text style. - pub fn font(&self) -> Font { - Font { - family: self.font_family.clone(), - features: self.font_features.clone(), - fallbacks: self.font_fallbacks.clone(), - weight: self.font_weight, - style: self.font_style, - } - } - - /// Returns the rounded line height in pixels. - pub fn line_height_in_pixels(&self, rem_size: Pixels) -> Pixels { - self.line_height.to_pixels(self.font_size, rem_size).round() - } - - /// Convert this text style into a [`TextRun`], for the given length of the text. - pub fn to_run(&self, len: usize) -> TextRun { - TextRun { - len, - font: Font { - family: self.font_family.clone(), - features: self.font_features.clone(), - fallbacks: self.font_fallbacks.clone(), - weight: self.font_weight, - style: self.font_style, - }, - color: self.color, - background_color: self.background_color, - underline: self.underline, - strikethrough: self.strikethrough, - } - } -} - -/// A highlight style to apply, similar to a `TextStyle` except -/// for a single font, uniformly sized and spaced text. -#[derive(Copy, Clone, Debug, Default, PartialEq)] -pub struct HighlightStyle { - /// The color of the text - pub color: Option, - - /// The font weight, e.g. bold - pub font_weight: Option, - - /// The font style, e.g. italic - pub font_style: Option, - - /// The background color of the text - pub background_color: Option, - - /// The underline style of the text - pub underline: Option, - - /// The underline style of the text - pub strikethrough: Option, - - /// Similar to the CSS `opacity` property, this will cause the text to be less vibrant. - pub fade_out: Option, -} - -impl Eq for HighlightStyle {} - -impl Hash for HighlightStyle { - fn hash(&self, state: &mut H) { - self.color.hash(state); - self.font_weight.hash(state); - self.font_style.hash(state); - self.background_color.hash(state); - self.underline.hash(state); - self.strikethrough.hash(state); - state.write_u32(u32::from_be_bytes( - self.fade_out.map(|f| f.to_be_bytes()).unwrap_or_default(), - )); - } -} - -impl Style { - /// Returns true if the style is visible and the background is opaque. - pub fn has_opaque_background(&self) -> bool { - self.background - .as_ref() - .is_some_and(|fill| fill.color().is_some_and(|color| !color.is_transparent())) - } - - /// Get the text style in this element style. - pub fn text_style(&self) -> Option<&TextStyleRefinement> { - if self.text.is_some() { - Some(&self.text) - } else { - None - } - } - - /// Get the content mask for this element style, based on the given bounds. - /// If the element does not hide its overflow, this will return `None`. - pub fn overflow_mask( - &self, - bounds: Bounds, - rem_size: Pixels, - scale_factor: f32, - ) -> Option { - match self.overflow { - Point { - x: Overflow::Visible, - y: Overflow::Visible, - } => None, - _ => { - let (bounds, radii, border_widths) = - self.paint_geometry(bounds, rem_size, scale_factor); - let has_visible_border = self - .border_color - .is_some_and(|color| !color.is_transparent()); - let border = if has_visible_border { - border_widths - } else { - Edges::default() - }; - let mut min = bounds.origin; - let mut max = bounds.bottom_right(); - - min.x += border.left; - max.x -= border.right; - min.y += border.top; - max.y -= border.bottom; - - // A scroll container clips its rounded padding box even when - // only one scroll axis is enabled (for example, menu panels). - let scrolling = - self.overflow.x == Overflow::Scroll || self.overflow.y == Overflow::Scroll; - - let bounds = match ( - !scrolling && self.overflow.x == Overflow::Visible, - !scrolling && self.overflow.y == Overflow::Visible, - ) { - // x and y both visible - (true, true) => return None, - // x visible, y hidden - (true, false) => Bounds::from_corners( - point(min.x, bounds.origin.y), - point(max.x, bounds.bottom_right().y), - ), - // x hidden, y visible - (false, true) => Bounds::from_corners( - point(bounds.origin.x, min.y), - point(bounds.bottom_right().x, max.y), - ), - // both hidden - (false, false) => Bounds::from_corners(min, max), - }; - - let (radii_x, radii_y) = if scrolling - || (self.overflow.x != Overflow::Visible - && self.overflow.y != Overflow::Visible) - { - ( - Corners { - top_left: (radii.top_left - border.left).max(px(0.)), - top_right: (radii.top_right - border.right).max(px(0.)), - bottom_right: (radii.bottom_right - border.right).max(px(0.)), - bottom_left: (radii.bottom_left - border.left).max(px(0.)), - }, - Corners { - top_left: (radii.top_left - border.top).max(px(0.)), - top_right: (radii.top_right - border.top).max(px(0.)), - bottom_right: (radii.bottom_right - border.bottom).max(px(0.)), - bottom_left: (radii.bottom_left - border.bottom).max(px(0.)), - }, - ) - } else { - (Corners::default(), Corners::default()) - }; - Some(crate::ClipRegion::rounded(bounds, radii_x, radii_y)) - } - } - } - - // Background, border, and overflow must derive from the same device-pixel - // geometry. In particular, a fractional border can paint a whole pixel. - fn paint_geometry( - &self, - bounds: Bounds, - rem_size: Pixels, - scale_factor: f32, - ) -> (Bounds, Corners, Edges) { - let radii = self - .corner_radii - .to_pixels(rem_size) - .clamp_radii_for_quad_size(bounds.size); - let snap = |value: Pixels| { - px(crate::util::round_to_device_pixel(value.0, scale_factor) / scale_factor) - }; - let origin = bounds.origin.map(snap); - let far = bounds.bottom_right().map(snap); - let bounds = Bounds::from_corners(origin, point(far.x.max(origin.x), far.y.max(origin.y))); - let radii = radii.clamp_radii_for_quad_size(bounds.size); - let borders = self.border_widths.to_pixels(rem_size).map(|value| { - px(crate::util::round_stroke_to_device_pixel(value.0, scale_factor) / scale_factor) - }); - (bounds, radii, borders) - } - - /// Paints the background of an element styled with this style. - pub fn paint( - &self, - bounds: Bounds, - window: &mut Window, - cx: &mut App, - continuation: impl FnOnce(&mut Window, &mut App), - ) { - #[cfg(debug_assertions)] - if self.debug_below { - cx.set_global(DebugBelow) - } - - #[cfg(debug_assertions)] - if self.debug || cx.has_global::() { - window.paint_quad(crate::outline(bounds, crate::red(), BorderStyle::default())); - } - - let rem_size = window.rem_size(); - let (bounds, corner_radii, border_widths) = - self.paint_geometry(bounds, rem_size, window.scale_factor()); - - window.paint_drop_shadows(bounds, corner_radii, &self.box_shadow); - - let background_color = self.background.as_ref().and_then(Fill::color); - if background_color.is_some_and(|color| !color.is_transparent()) { - let mut border_color = match background_color { - Some(color) => match color.tag { - BackgroundTag::Solid - | BackgroundTag::PatternSlash - | BackgroundTag::Checkerboard => color.solid, - - BackgroundTag::LinearGradient => color - .colors - .first() - .map(|stop| stop.color) - .unwrap_or_default(), - }, - None => Hsla::default(), - }; - border_color.a = 0.; - window.paint_quad(quad( - bounds, - corner_radii, - background_color.unwrap_or_default(), - Edges::default(), - border_color, - self.border_style, - )); - } - - window.paint_inset_shadows(bounds, corner_radii, &self.box_shadow); - - continuation(window, cx); - - if self.is_border_visible() { - let mut background = self.border_color.unwrap_or_default(); - background.a = 0.; - window.paint_quad(quad( - bounds, - corner_radii, - background, - border_widths, - self.border_color.unwrap_or_default(), - self.border_style, - )); - } - - #[cfg(debug_assertions)] - if self.debug_below { - cx.remove_global::(); - } - } - - fn is_border_visible(&self) -> bool { - self.border_color - .is_some_and(|color| !color.is_transparent()) - && self.border_widths.any(|length| !length.is_zero()) - } -} - -impl Default for Style { - fn default() -> Self { - Style { - display: Display::Block, - visibility: Visibility::Visible, - overflow: Point { - x: Overflow::Visible, - y: Overflow::Visible, - }, - allow_concurrent_scroll: false, - restrict_scroll_to_axis: false, - scrollbar_width: AbsoluteLength::default(), - position: Position::Relative, - inset: Edges::auto(), - margin: Edges::::zero(), - padding: Edges::::zero(), - border_widths: Edges::::zero(), - size: Size::auto(), - min_size: Size::auto(), - max_size: Size::auto(), - aspect_ratio: None, - gap: Size::default(), - // Alignment - align_items: None, - align_self: None, - align_content: None, - justify_content: None, - // Flexbox - flex_direction: FlexDirection::Row, - flex_wrap: FlexWrap::NoWrap, - flex_grow: 0.0, - flex_shrink: 1.0, - flex_basis: Length::Auto, - background: None, - border_color: None, - border_style: BorderStyle::default(), - corner_radii: Corners::default(), - box_shadow: Default::default(), - text: TextStyleRefinement::default(), - mouse_cursor: None, - opacity: None, - grid_rows: None, - grid_cols: None, - grid_location: None, - - #[cfg(debug_assertions)] - debug: false, - #[cfg(debug_assertions)] - debug_below: false, - } - } -} - -/// The properties that can be applied to an underline. -#[derive( - Refineable, Copy, Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, -)] -pub struct UnderlineStyle { - /// The thickness of the underline. - pub thickness: Pixels, - - /// The color of the underline. - pub color: Option, - - /// Whether the underline should be wavy, like in a spell checker. - pub wavy: bool, -} - -/// The properties that can be applied to a strikethrough. -#[derive( - Refineable, Copy, Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, -)] -pub struct StrikethroughStyle { - /// The thickness of the strikethrough. - pub thickness: Pixels, - - /// The color of the strikethrough. - pub color: Option, -} - -/// The kinds of fill that can be applied to a shape. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] -pub enum Fill { - /// A solid color fill. - Color(Background), -} - -impl Fill { - /// Unwrap this fill into a solid color, if it is one. - /// - /// If the fill is not a solid color, this method returns `None`. - pub fn color(&self) -> Option { - match self { - Fill::Color(color) => Some(*color), - } - } -} - -impl Default for Fill { - fn default() -> Self { - Self::Color(Background::default()) - } -} - -impl From for Fill { - fn from(color: Hsla) -> Self { - Self::Color(color.into()) - } -} - -impl From for Fill { - fn from(color: Rgba) -> Self { - Self::Color(color.into()) - } -} - -impl From for Fill { - fn from(background: Background) -> Self { - Self::Color(background) - } -} - -impl From 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), - font_weight: Some(other.font_weight), - font_style: Some(other.font_style), - background_color: other.background_color, - underline: other.underline, - strikethrough: other.strikethrough, - fade_out: None, - } - } -} - -impl HighlightStyle { - /// Create a highlight style with just a color - pub fn color(color: Hsla) -> Self { - Self { - color: Some(color), - ..Default::default() - } - } - /// Blend this highlight style with another. - /// Non-continuous properties, like font_weight and font_style, are overwritten. - #[must_use] - pub fn highlight(self, other: HighlightStyle) -> Self { - Self { - color: other - .color - .map(|other_color| { - if let Some(color) = self.color { - color.blend(other_color) - } else { - other_color - } - }) - .or(self.color), - font_weight: other.font_weight.or(self.font_weight), - font_style: other.font_style.or(self.font_style), - background_color: other.background_color.or(self.background_color), - underline: other.underline.or(self.underline), - strikethrough: other.strikethrough.or(self.strikethrough), - fade_out: other - .fade_out - .map(|source_fade| { - self.fade_out - .map(|dest_fade| (dest_fade * (1. + source_fade)).clamp(0., 1.)) - .unwrap_or(source_fade) - }) - .or(self.fade_out), - } - } -} - -impl From for HighlightStyle { - fn from(color: Hsla) -> Self { - Self { - color: Some(color), - ..Default::default() - } - } -} - -impl From for HighlightStyle { - fn from(font_weight: FontWeight) -> Self { - Self { - font_weight: Some(font_weight), - ..Default::default() - } - } -} - -impl From for HighlightStyle { - fn from(font_style: FontStyle) -> Self { - Self { - font_style: Some(font_style), - ..Default::default() - } - } -} - -impl From for HighlightStyle { - fn from(color: Rgba) -> Self { - Self { - color: Some(color.into()), - ..Default::default() - } - } -} - -/// Combine and merge the highlights and ranges in the two iterators. -pub fn combine_highlights( - a: impl IntoIterator, HighlightStyle)>, - b: impl IntoIterator, HighlightStyle)>, -) -> impl Iterator, HighlightStyle)> { - let mut endpoints = Vec::new(); - let mut highlights = Vec::new(); - for (range, highlight) in a.into_iter().chain(b) { - if !range.is_empty() { - let highlight_id = highlights.len(); - endpoints.push((range.start, highlight_id, true)); - endpoints.push((range.end, highlight_id, false)); - highlights.push(highlight); - } - } - endpoints.sort_unstable_by_key(|(position, _, _)| *position); - let mut endpoints = endpoints.into_iter().peekable(); - - let mut active_styles = HashSet::default(); - let mut ix = 0; - iter::from_fn(move || { - while let Some((endpoint_ix, highlight_id, is_start)) = endpoints.peek() { - let prev_index = mem::replace(&mut ix, *endpoint_ix); - if ix > prev_index && !active_styles.is_empty() { - let current_style = active_styles - .iter() - .fold(HighlightStyle::default(), |acc, highlight_id| { - acc.highlight(highlights[*highlight_id]) - }); - return Some((prev_index..ix, current_style)); - } - - if *is_start { - active_styles.insert(*highlight_id); - } else { - active_styles.remove(highlight_id); - } - endpoints.next(); - } - None - }) -} - -/// Used to control how child nodes are aligned. -/// For Flexbox it controls alignment in the cross axis -/// For Grid it controls alignment in the block axis -/// -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/align-items) -#[derive(Copy, Clone, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema)] -// Copy of taffy::style type of the same name, to derive JsonSchema. -pub enum AlignItems { - /// Items are packed toward the start of the axis - Start, - /// Items are packed toward the end of the axis - End, - /// Items are packed towards the flex-relative start of the axis. - /// - /// For flex containers with flex_direction RowReverse or ColumnReverse this is equivalent - /// to End. In all other cases it is equivalent to Start. - FlexStart, - /// Items are packed towards the flex-relative end of the axis. - /// - /// For flex containers with flex_direction RowReverse or ColumnReverse this is equivalent - /// to Start. In all other cases it is equivalent to End. - FlexEnd, - /// Items are packed along the center of the cross axis - Center, - /// Items are aligned such as their baselines align - Baseline, - /// Stretch to fill the container - Stretch, -} -/// Used to control how child nodes are aligned. -/// Does not apply to Flexbox, and will be ignored if specified on a flex container -/// For Grid it controls alignment in the inline axis -/// -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-items) -pub type JustifyItems = AlignItems; -/// Used to control how the specified nodes is aligned. -/// Overrides the parent Node's `AlignItems` property. -/// For Flexbox it controls alignment in the cross axis -/// For Grid it controls alignment in the block axis -/// -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/align-self) -pub type AlignSelf = AlignItems; -/// Used to control how the specified nodes is aligned. -/// Overrides the parent Node's `JustifyItems` property. -/// Does not apply to Flexbox, and will be ignored if specified on a flex child -/// For Grid it controls alignment in the inline axis -/// -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-self) -pub type JustifySelf = AlignItems; - -/// Sets the distribution of space between and around content items -/// For Flexbox it controls alignment in the cross axis -/// For Grid it controls alignment in the block axis -/// -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/align-content) -#[derive(Copy, Clone, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema)] -// Copy of taffy::style type of the same name, to derive JsonSchema. -pub enum AlignContent { - /// Items are packed toward the start of the axis - Start, - /// Items are packed toward the end of the axis - End, - /// Items are packed towards the flex-relative start of the axis. - /// - /// For flex containers with flex_direction RowReverse or ColumnReverse this is equivalent - /// to End. In all other cases it is equivalent to Start. - FlexStart, - /// Items are packed towards the flex-relative end of the axis. - /// - /// For flex containers with flex_direction RowReverse or ColumnReverse this is equivalent - /// to Start. In all other cases it is equivalent to End. - FlexEnd, - /// Items are centered around the middle of the axis - Center, - /// Items are stretched to fill the container - Stretch, - /// The first and last items are aligned flush with the edges of the container (no gap) - /// The gap between items is distributed evenly. - SpaceBetween, - /// The gap between the first and last items is exactly THE SAME as the gap between items. - /// The gaps are distributed evenly - SpaceEvenly, - /// The gap between the first and last items is exactly HALF the gap between items. - /// The gaps are distributed evenly in proportion to these ratios. - SpaceAround, -} - -/// Sets the distribution of space between and around content items -/// For Flexbox it controls alignment in the main axis -/// For Grid it controls alignment in the inline axis -/// -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content) -pub type JustifyContent = AlignContent; - -/// Sets the layout used for the children of this node -/// -/// The default values depends on on which feature flags are enabled. The order of precedence is: Flex, Grid, Block, None. -#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, JsonSchema)] -// Copy of taffy::style type of the same name, to derive JsonSchema. -pub enum Display { - /// The children will follow the block layout algorithm - Block, - /// The children will follow the flexbox layout algorithm - #[default] - Flex, - /// The children will follow the CSS Grid layout algorithm - Grid, - /// The children will not be laid out, and will follow absolute positioning - None, -} - -/// Controls whether flex items are forced onto one line or can wrap onto multiple lines. -/// -/// Defaults to [`FlexWrap::NoWrap`] -/// -/// [Specification](https://www.w3.org/TR/css-flexbox-1/#flex-wrap-property) -#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, JsonSchema)] -// Copy of taffy::style type of the same name, to derive JsonSchema. -pub enum FlexWrap { - /// Items will not wrap and stay on a single line - #[default] - NoWrap, - /// Items will wrap according to this item's [`FlexDirection`] - Wrap, - /// Items will wrap in the opposite direction to this item's [`FlexDirection`] - WrapReverse, -} - -/// The direction of the flexbox layout main axis. -/// -/// There are always two perpendicular layout axes: main (or primary) and cross (or secondary). -/// Adding items will cause them to be positioned adjacent to each other along the main axis. -/// By varying this value throughout your tree, you can create complex axis-aligned layouts. -/// -/// Items are always aligned relative to the cross axis, and justified relative to the main axis. -/// -/// The default behavior is [`FlexDirection::Row`]. -/// -/// [Specification](https://www.w3.org/TR/css-flexbox-1/#flex-direction-property) -#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, JsonSchema)] -// Copy of taffy::style type of the same name, to derive JsonSchema. -pub enum FlexDirection { - /// Defines +x as the main axis - /// - /// Items will be added from left to right in a row. - #[default] - Row, - /// Defines +y as the main axis - /// - /// Items will be added from top to bottom in a column. - Column, - /// Defines -x as the main axis - /// - /// Items will be added from right to left in a row. - RowReverse, - /// Defines -y as the main axis - /// - /// Items will be added from bottom to top in a column. - ColumnReverse, -} - -/// How children overflowing their container should affect layout -/// -/// In CSS the primary effect of this property is to control whether contents of a parent container that overflow that container should -/// be displayed anyway, be clipped, or trigger the container to become a scroll container. However it also has secondary effects on layout, -/// the main ones being: -/// -/// - The automatic minimum size Flexbox/CSS Grid items with non-`Visible` overflow is `0` rather than being content based -/// - `Overflow::Scroll` nodes have space in the layout reserved for a scrollbar (width controlled by the `scrollbar_width` property) -/// -/// In Taffy, we only implement the layout related secondary effects as we are not concerned with drawing/painting. The amount of space reserved for -/// a scrollbar is controlled by the `scrollbar_width` property. If this is `0` then `Scroll` behaves identically to `Hidden`. -/// -/// -#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, JsonSchema)] -// Copy of taffy::style type of the same name, to derive JsonSchema. -pub enum Overflow { - /// The automatic minimum size of this node as a flexbox/grid item should be based on the size of its content. - /// Content that overflows this node *should* contribute to the scroll region of its parent. - #[default] - Visible, - /// The automatic minimum size of this node as a flexbox/grid item should be based on the size of its content. - /// Content that overflows this node should *not* contribute to the scroll region of its parent. - Clip, - /// The automatic minimum size of this node as a flexbox/grid item should be `0`. - /// Content that overflows this node should *not* contribute to the scroll region of its parent. - Hidden, - /// The automatic minimum size of this node as a flexbox/grid item should be `0`. Additionally, space should be reserved - /// for a scrollbar. The amount of space reserved is controlled by the `scrollbar_width` property. - /// Content that overflows this node should *not* contribute to the scroll region of its parent. - Scroll, -} - -/// The positioning strategy for this item. -/// -/// This controls both how the origin is determined for the [`Style::position`] field, -/// and whether or not the item will be controlled by flexbox's layout algorithm. -/// -/// WARNING: this enum follows the behavior of [CSS's `position` property](https://developer.mozilla.org/en-US/docs/Web/CSS/position), -/// which can be unintuitive. -/// -/// [`Position::Relative`] is the default value, in contrast to the default behavior in CSS. -#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, JsonSchema)] -// Copy of taffy::style type of the same name, to derive JsonSchema. -pub enum Position { - /// The offset is computed relative to the final position given by the layout algorithm. - /// Offsets do not affect the position of any other items; they are effectively a correction factor applied at the end. - #[default] - Relative, - /// The offset is computed relative to this item's closest positioned ancestor, if any. - /// Otherwise, it is placed relative to the origin. - /// No space is created for the item in the page layout, and its size will not be altered. - /// - /// WARNING: to opt-out of layouting entirely, you must use [`Display::None`] instead on your [`Style`] object. - Absolute, -} - -impl From for taffy::style::AlignItems { - fn from(value: AlignItems) -> Self { - match value { - AlignItems::Start => Self::START, - AlignItems::End => Self::END, - AlignItems::FlexStart => Self::FLEX_START, - AlignItems::FlexEnd => Self::FLEX_END, - AlignItems::Center => Self::CENTER, - AlignItems::Baseline => Self::BASELINE, - AlignItems::Stretch => Self::STRETCH, - } - } -} - -impl From for taffy::style::AlignContent { - fn from(value: AlignContent) -> Self { - match value { - AlignContent::Start => Self::START, - AlignContent::End => Self::END, - AlignContent::FlexStart => Self::FLEX_START, - AlignContent::FlexEnd => Self::FLEX_END, - AlignContent::Center => Self::CENTER, - AlignContent::Stretch => Self::STRETCH, - AlignContent::SpaceBetween => Self::SPACE_BETWEEN, - AlignContent::SpaceEvenly => Self::SPACE_EVENLY, - AlignContent::SpaceAround => Self::SPACE_AROUND, - } - } -} - -impl From for taffy::style::Display { - fn from(value: Display) -> Self { - match value { - Display::Block => Self::Block, - Display::Flex => Self::Flex, - Display::Grid => Self::Grid, - Display::None => Self::None, - } - } -} - -impl From for taffy::style::FlexWrap { - fn from(value: FlexWrap) -> Self { - match value { - FlexWrap::NoWrap => Self::NoWrap, - FlexWrap::Wrap => Self::Wrap, - FlexWrap::WrapReverse => Self::WrapReverse, - } - } -} - -impl From for taffy::style::FlexDirection { - fn from(value: FlexDirection) -> Self { - match value { - FlexDirection::Row => Self::Row, - FlexDirection::Column => Self::Column, - FlexDirection::RowReverse => Self::RowReverse, - FlexDirection::ColumnReverse => Self::ColumnReverse, - } - } -} - -impl From for taffy::style::Overflow { - fn from(value: Overflow) -> Self { - match value { - Overflow::Visible => Self::Visible, - Overflow::Clip => Self::Clip, - Overflow::Hidden => Self::Hidden, - Overflow::Scroll => Self::Scroll, - } - } -} - -impl From for taffy::style::Position { - fn from(value: Position) -> Self { - match value { - Position::Relative => Self::Relative, - Position::Absolute => Self::Absolute, - } - } -} - -#[cfg(test)] -mod tests { - use crate::{blue, green, px, red, yellow}; - - use super::*; - - use util_macros::perf; - - #[perf] - fn test_basic_highlight_style_combination() { - let style_a = HighlightStyle::default(); - let style_b = HighlightStyle::default(); - let style_a = style_a.highlight(style_b); - assert_eq!( - style_a, - HighlightStyle::default(), - "Combining empty styles should not produce a non-empty style." - ); - - let mut style_b = HighlightStyle { - color: Some(red()), - strikethrough: Some(StrikethroughStyle { - thickness: px(2.), - color: Some(blue()), - }), - fade_out: Some(0.), - font_style: Some(FontStyle::Italic), - font_weight: Some(FontWeight(300.)), - background_color: Some(yellow()), - underline: Some(UnderlineStyle { - thickness: px(2.), - color: Some(red()), - wavy: true, - }), - }; - let expected_style = style_b; - - let style_a = style_a.highlight(style_b); - assert_eq!( - style_a, expected_style, - "Blending an empty style with another style should return the other style" - ); - - let style_b = style_b.highlight(Default::default()); - assert_eq!( - style_b, expected_style, - "Blending a style with an empty style should not change the style." - ); - - let mut style_c = expected_style; - - let style_d = HighlightStyle { - color: Some(blue().alpha(0.7)), - strikethrough: Some(StrikethroughStyle { - thickness: px(4.), - color: Some(crate::red()), - }), - fade_out: Some(0.), - font_style: Some(FontStyle::Oblique), - font_weight: Some(FontWeight(800.)), - background_color: Some(green()), - underline: Some(UnderlineStyle { - thickness: px(4.), - color: None, - wavy: false, - }), - }; - - let expected_style = HighlightStyle { - color: Some(red().blend(blue().alpha(0.7))), - strikethrough: Some(StrikethroughStyle { - thickness: px(4.), - color: Some(red()), - }), - // TODO this does not seem right - fade_out: Some(0.), - font_style: Some(FontStyle::Oblique), - font_weight: Some(FontWeight(800.)), - background_color: Some(green()), - underline: Some(UnderlineStyle { - thickness: px(4.), - color: None, - wavy: false, - }), - }; - - let style_c = style_c.highlight(style_d); - assert_eq!( - style_c, expected_style, - "Blending styles should blend properties where possible and override all others" - ); - } - - #[perf] - fn test_combine_highlights() { - assert_eq!( - combine_highlights( - [ - (0..5, green().into()), - (4..10, FontWeight::BOLD.into()), - (15..20, yellow().into()), - ], - [ - (2..6, FontStyle::Italic.into()), - (1..3, blue().into()), - (21..23, red().into()), - ] - ) - .collect::>(), - [ - ( - 0..1, - HighlightStyle { - color: Some(green()), - ..Default::default() - } - ), - ( - 1..2, - HighlightStyle { - color: Some(blue()), - ..Default::default() - } - ), - ( - 2..3, - HighlightStyle { - color: Some(blue()), - font_style: Some(FontStyle::Italic), - ..Default::default() - } - ), - ( - 3..4, - HighlightStyle { - color: Some(green()), - font_style: Some(FontStyle::Italic), - ..Default::default() - } - ), - ( - 4..5, - HighlightStyle { - color: Some(green()), - font_weight: Some(FontWeight::BOLD), - font_style: Some(FontStyle::Italic), - ..Default::default() - } - ), - ( - 5..6, - HighlightStyle { - font_weight: Some(FontWeight::BOLD), - font_style: Some(FontStyle::Italic), - ..Default::default() - } - ), - ( - 6..10, - HighlightStyle { - font_weight: Some(FontWeight::BOLD), - ..Default::default() - } - ), - ( - 15..20, - HighlightStyle { - color: Some(yellow()), - ..Default::default() - } - ), - ( - 21..23, - HighlightStyle { - color: Some(red()), - ..Default::default() - } - ) - ] - ); - } - - #[perf] - fn test_text_style_refinement() { - let mut style = Style::default(); - style.refine(&StyleRefinement::default().text_size(px(20.0))); - style.refine(&StyleRefinement::default().font_weight(FontWeight::SEMIBOLD)); - - assert_eq!( - Some(AbsoluteLength::from(px(20.0))), - style.text_style().unwrap().font_size - ); - - assert_eq!( - Some(FontWeight::SEMIBOLD), - style.text_style().unwrap().font_weight - ); - } -} diff --git a/crates/gpui_pre/src/styled.rs b/crates/gpui_pre/src/styled.rs deleted file mode 100644 index a24982a6..00000000 --- a/crates/gpui_pre/src/styled.rs +++ /dev/null @@ -1,904 +0,0 @@ -use crate::{ - self as gpui, AbsoluteLength, AlignContent, AlignItems, AlignSelf, BorderStyle, CursorStyle, - DefiniteLength, Display, Fill, FlexDirection, FlexWrap, Font, FontFeatures, FontStyle, - FontWeight, GridPlacement, GridTemplate, GridTemplateMinSize, Hsla, JustifyContent, Length, - SharedString, StrikethroughStyle, StyleRefinement, TextAlign, TextOverflow, - TextStyleRefinement, UnderlineStyle, WhiteSpace, px, relative, rems, -}; -pub use gpui_macros::{ - border_style_methods, box_shadow_style_methods, cursor_style_methods, margin_style_methods, - overflow_style_methods, padding_style_methods, position_style_methods, - visibility_style_methods, -}; -const ELLIPSIS: SharedString = SharedString::new_static("…"); - -/// A trait for elements that can be styled. -/// Use this to opt-in to a utility CSS-like styling API. -// gate on rust-analyzer so rust-analyzer never needs to expand this macro, it takes up to 10 seconds to expand due to inefficiencies in rust-analyzers proc-macro srv -#[cfg_attr( - all(any(feature = "inspector", debug_assertions), not(rust_analyzer)), - gpui_macros::derive_inspector_reflection -)] -pub trait Styled: Sized { - /// Returns a reference to the style memory of this element. - fn style(&mut self) -> &mut StyleRefinement; - - gpui_macros::style_helpers!(); - gpui_macros::visibility_style_methods!(); - gpui_macros::margin_style_methods!(); - gpui_macros::padding_style_methods!(); - gpui_macros::position_style_methods!(); - gpui_macros::overflow_style_methods!(); - gpui_macros::cursor_style_methods!(); - gpui_macros::border_style_methods!(); - gpui_macros::box_shadow_style_methods!(); - - /// Sets the display type of the element to `block`. - /// [Docs](https://tailwindcss.com/docs/display) - fn block(mut self) -> Self { - self.style().display = Some(Display::Block); - self - } - - /// Sets the display type of the element to `flex`. - /// [Docs](https://tailwindcss.com/docs/display) - fn flex(mut self) -> Self { - self.style().display = Some(Display::Flex); - self - } - - /// Sets the display type of the element to `grid`. - /// [Docs](https://tailwindcss.com/docs/display) - fn grid(mut self) -> Self { - self.style().display = Some(Display::Grid); - self - } - - /// Sets the display type of the element to `none`. - /// [Docs](https://tailwindcss.com/docs/display) - fn hidden(mut self) -> Self { - self.style().display = Some(Display::None); - self - } - - /// Set the space to be reserved for rendering the scrollbar. - /// - /// This will only affect the layout of the element when overflow for this element is set to - /// `Overflow::Scroll`. - fn scrollbar_width(mut self, width: impl Into) -> Self { - self.style().scrollbar_width = Some(width.into()); - self - } - - /// Sets the whitespace of the element to `normal`. - /// [Docs](https://tailwindcss.com/docs/whitespace#normal) - fn whitespace_normal(mut self) -> Self { - self.text_style().white_space = Some(WhiteSpace::Normal); - self - } - - /// Sets the whitespace of the element to `nowrap`. - /// [Docs](https://tailwindcss.com/docs/whitespace#nowrap) - fn whitespace_nowrap(mut self) -> Self { - self.text_style().white_space = Some(WhiteSpace::Nowrap); - self - } - - /// Sets the truncate overflowing text with an ellipsis (…) at the end if needed. - /// [Docs](https://tailwindcss.com/docs/text-overflow#ellipsis) - fn text_ellipsis(mut self) -> Self { - self.text_style().text_overflow = Some(TextOverflow::Truncate(ELLIPSIS)); - self - } - - /// Sets the truncate overflowing text with an ellipsis (…) at the start if needed. - /// Typically more adequate for file paths where the end is more important than the beginning. - /// Note: This doesn't exist in Tailwind CSS. - fn text_ellipsis_start(mut self) -> Self { - self.text_style().text_overflow = Some(TextOverflow::TruncateStart(ELLIPSIS)); - self - } - - /// Sets the truncate overflowing text with an ellipsis (…) in the middle if needed. - /// Preserves the beginning and end of the text. Useful for filenames. - /// Note: This doesn't exist in Tailwind CSS. - fn text_ellipsis_middle(mut self) -> Self { - self.text_style().text_overflow = Some(TextOverflow::TruncateMiddle(ELLIPSIS)); - self - } - - /// Sets the text overflow behavior of the element. - fn text_overflow(mut self, overflow: TextOverflow) -> Self { - self.text_style().text_overflow = Some(overflow); - self - } - - /// Set the text alignment of the element. - fn text_align(mut self, align: TextAlign) -> Self { - self.text_style().text_align = Some(align); - self - } - - /// Sets the text alignment to left - fn text_left(mut self) -> Self { - self.text_align(TextAlign::Left) - } - - /// Sets the text alignment to center - fn text_center(mut self) -> Self { - self.text_align(TextAlign::Center) - } - - /// Sets the text alignment to right - fn text_right(mut self) -> Self { - self.text_align(TextAlign::Right) - } - - /// Sets the truncate to prevent text from wrapping and truncate overflowing text with an ellipsis (…) if needed. - /// [Docs](https://tailwindcss.com/docs/text-overflow#truncate) - fn truncate(mut self) -> Self { - self.overflow_hidden().whitespace_nowrap().text_ellipsis() - } - - /// Sets number of lines to show before truncating the text. - /// [Docs](https://tailwindcss.com/docs/line-clamp) - fn line_clamp(mut self, lines: usize) -> Self { - let mut text_style = self.text_style(); - text_style.line_clamp = Some(lines); - self.overflow_hidden() - } - - /// Sets the flex direction of the element to `column`. - /// [Docs](https://tailwindcss.com/docs/flex-direction#column) - fn flex_col(mut self) -> Self { - self.style().flex_direction = Some(FlexDirection::Column); - self - } - - /// Sets the flex direction of the element to `column-reverse`. - /// [Docs](https://tailwindcss.com/docs/flex-direction#column-reverse) - fn flex_col_reverse(mut self) -> Self { - self.style().flex_direction = Some(FlexDirection::ColumnReverse); - self - } - - /// Sets the flex direction of the element to `row`. - /// [Docs](https://tailwindcss.com/docs/flex-direction#row) - fn flex_row(mut self) -> Self { - self.style().flex_direction = Some(FlexDirection::Row); - self - } - - /// Sets the flex direction of the element to `row-reverse`. - /// [Docs](https://tailwindcss.com/docs/flex-direction#row-reverse) - fn flex_row_reverse(mut self) -> Self { - self.style().flex_direction = Some(FlexDirection::RowReverse); - self - } - - /// Sets the element to allow a flex item to grow and shrink as needed, ignoring its initial size. - /// [Docs](https://tailwindcss.com/docs/flex#flex-1) - fn flex_1(mut self) -> Self { - self.style().flex_grow = Some(1.); - self.style().flex_shrink = Some(1.); - self.style().flex_basis = Some(relative(0.).into()); - self - } - - /// Sets the element to allow a flex item to grow and shrink, taking into account its initial size. - /// [Docs](https://tailwindcss.com/docs/flex#auto) - fn flex_auto(mut self) -> Self { - self.style().flex_grow = Some(1.); - self.style().flex_shrink = Some(1.); - self.style().flex_basis = Some(Length::Auto); - self - } - - /// Sets the element to allow a flex item to shrink but not grow, taking into account its initial size. - /// [Docs](https://tailwindcss.com/docs/flex#initial) - fn flex_initial(mut self) -> Self { - self.style().flex_grow = Some(0.); - self.style().flex_shrink = Some(1.); - self.style().flex_basis = Some(Length::Auto); - self - } - - /// Sets the element to prevent a flex item from growing or shrinking. - /// [Docs](https://tailwindcss.com/docs/flex#none) - fn flex_none(mut self) -> Self { - self.style().flex_grow = Some(0.); - self.style().flex_shrink = Some(0.); - self.style().flex_basis = Some(Length::Auto); - self - } - - /// Sets the initial size of flex items for this element. - /// [Docs](https://tailwindcss.com/docs/flex-basis) - fn flex_basis(mut self, basis: impl Into) -> Self { - self.style().flex_basis = Some(basis.into()); - self - } - - /// Sets the flex item's grow factor. - /// [Docs](https://tailwindcss.com/docs/flex-grow) - fn flex_grow(mut self, grow: f32) -> Self { - self.style().flex_grow = Some(grow); - self - } - - /// Disables flex item growth (flex-grow: 0). - /// [Docs](https://tailwindcss.com/docs/flex-grow#dont-grow) - fn flex_grow_0(mut self) -> Self { - self.style().flex_grow = Some(0.); - self - } - - /// Enables flex item growth (flex-grow: 1). - /// [Docs](https://tailwindcss.com/docs/flex-grow#grow-1) - fn flex_grow_1(mut self) -> Self { - self.style().flex_grow = Some(1.); - self - } - - /// Sets the flex item's shrink factor. - /// [Docs](https://tailwindcss.com/docs/flex-shrink) - fn flex_shrink(mut self, shrink: f32) -> Self { - self.style().flex_shrink = Some(shrink); - self - } - - /// Disables flex item shrinking (flex-shrink: 0). - /// [Docs](https://tailwindcss.com/docs/flex-shrink#dont-shrink) - fn flex_shrink_0(mut self) -> Self { - self.style().flex_shrink = Some(0.); - self - } - - /// Enables flex item shrinking (flex-shrink: 1). - /// [Docs](https://tailwindcss.com/docs/flex-shrink#shrink-1) - fn flex_shrink_1(mut self) -> Self { - self.style().flex_shrink = Some(1.); - self - } - - /// Sets the element to allow flex items to wrap. - /// [Docs](https://tailwindcss.com/docs/flex-wrap#wrap-normally) - fn flex_wrap(mut self) -> Self { - self.style().flex_wrap = Some(FlexWrap::Wrap); - self - } - - /// Sets the element wrap flex items in the reverse direction. - /// [Docs](https://tailwindcss.com/docs/flex-wrap#wrap-reversed) - fn flex_wrap_reverse(mut self) -> Self { - self.style().flex_wrap = Some(FlexWrap::WrapReverse); - self - } - - /// Sets the element to prevent flex items from wrapping, causing inflexible items to overflow the container if necessary. - /// [Docs](https://tailwindcss.com/docs/flex-wrap#dont-wrap) - fn flex_nowrap(mut self) -> Self { - self.style().flex_wrap = Some(FlexWrap::NoWrap); - self - } - - /// Sets the element to align flex items to the start of the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-items#start) - fn items_start(mut self) -> Self { - self.style().align_items = Some(AlignItems::FlexStart); - self - } - - /// Sets the element to align flex items to the end of the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-items#end) - fn items_end(mut self) -> Self { - self.style().align_items = Some(AlignItems::FlexEnd); - self - } - - /// Sets the element to align flex items along the center of the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-items#center) - fn items_center(mut self) -> Self { - self.style().align_items = Some(AlignItems::Center); - self - } - - /// Sets the element to align flex items along the baseline of the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-items#baseline) - fn items_baseline(mut self) -> Self { - self.style().align_items = Some(AlignItems::Baseline); - self - } - - /// Sets the element to stretch flex items to fill the available space along the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-items#stretch) - fn items_stretch(mut self) -> Self { - self.style().align_items = Some(AlignItems::Stretch); - self - } - - /// Sets how this specific element is aligned along the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-self#start) - fn self_start(mut self) -> Self { - self.style().align_self = Some(AlignSelf::Start); - self - } - - /// Sets this element to align against the end of the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-self#end) - fn self_end(mut self) -> Self { - self.style().align_self = Some(AlignSelf::End); - self - } - - /// Sets this element to align against the start of the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-self#start) - fn self_flex_start(mut self) -> Self { - self.style().align_self = Some(AlignSelf::FlexStart); - self - } - - /// Sets this element to align against the end of the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-self#end) - fn self_flex_end(mut self) -> Self { - self.style().align_self = Some(AlignSelf::FlexEnd); - self - } - - /// Sets this element to align along the center of the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-self#center) - fn self_center(mut self) -> Self { - self.style().align_self = Some(AlignSelf::Center); - self - } - - /// Sets this element to align along the baseline of the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-self#baseline) - fn self_baseline(mut self) -> Self { - self.style().align_self = Some(AlignSelf::Baseline); - self - } - - /// Sets this element to stretch to fill the available space along the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-self#stretch) - fn self_stretch(mut self) -> Self { - self.style().align_self = Some(AlignSelf::Stretch); - self - } - - /// Sets the element to justify flex items against the start of the container's main axis. - /// [Docs](https://tailwindcss.com/docs/justify-content#start) - fn justify_start(mut self) -> Self { - self.style().justify_content = Some(JustifyContent::Start); - self - } - - /// Sets the element to justify flex items against the end of the container's main axis. - /// [Docs](https://tailwindcss.com/docs/justify-content#end) - fn justify_end(mut self) -> Self { - self.style().justify_content = Some(JustifyContent::End); - self - } - - /// Sets the element to justify flex items along the center of the container's main axis. - /// [Docs](https://tailwindcss.com/docs/justify-content#center) - fn justify_center(mut self) -> Self { - self.style().justify_content = Some(JustifyContent::Center); - self - } - - /// Sets the element to justify flex items along the container's main axis - /// such that there is an equal amount of space between each item. - /// [Docs](https://tailwindcss.com/docs/justify-content#space-between) - fn justify_between(mut self) -> Self { - self.style().justify_content = Some(JustifyContent::SpaceBetween); - self - } - - /// Sets the element to justify items along the container's main axis such - /// that there is an equal amount of space on each side of each item. - /// [Docs](https://tailwindcss.com/docs/justify-content#space-around) - fn justify_around(mut self) -> Self { - self.style().justify_content = Some(JustifyContent::SpaceAround); - self - } - - /// Sets the element to justify items along the container's main axis such - /// that there is an equal amount of space around each item, but also - /// accounting for the doubling of space you would normally see between - /// each item when using justify-around. - /// [Docs](https://tailwindcss.com/docs/justify-content#space-evenly) - fn justify_evenly(mut self) -> Self { - self.style().justify_content = Some(JustifyContent::SpaceEvenly); - self - } - - /// Sets the element to pack content items in their default position as if no align-content value was set. - /// [Docs](https://tailwindcss.com/docs/align-content#normal) - fn content_normal(mut self) -> Self { - self.style().align_content = None; - self - } - - /// Sets the element to pack content items in the center of the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-content#center) - fn content_center(mut self) -> Self { - self.style().align_content = Some(AlignContent::Center); - self - } - - /// Sets the element to pack content items against the start of the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-content#start) - fn content_start(mut self) -> Self { - self.style().align_content = Some(AlignContent::FlexStart); - self - } - - /// Sets the element to pack content items against the end of the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-content#end) - fn content_end(mut self) -> Self { - self.style().align_content = Some(AlignContent::FlexEnd); - self - } - - /// Sets the element to pack content items along the container's cross axis - /// such that there is an equal amount of space between each item. - /// [Docs](https://tailwindcss.com/docs/align-content#space-between) - fn content_between(mut self) -> Self { - self.style().align_content = Some(AlignContent::SpaceBetween); - self - } - - /// Sets the element to pack content items along the container's cross axis - /// such that there is an equal amount of space on each side of each item. - /// [Docs](https://tailwindcss.com/docs/align-content#space-around) - fn content_around(mut self) -> Self { - self.style().align_content = Some(AlignContent::SpaceAround); - self - } - - /// Sets the element to pack content items along the container's cross axis - /// such that there is an equal amount of space between each item. - /// [Docs](https://tailwindcss.com/docs/align-content#space-evenly) - fn content_evenly(mut self) -> Self { - self.style().align_content = Some(AlignContent::SpaceEvenly); - self - } - - /// Sets the element to allow content items to fill the available space along the container's cross axis. - /// [Docs](https://tailwindcss.com/docs/align-content#stretch) - fn content_stretch(mut self) -> Self { - self.style().align_content = Some(AlignContent::Stretch); - self - } - - /// Sets the aspect ratio of the element. - /// [Docs](https://tailwindcss.com/docs/aspect-ratio) - fn aspect_ratio(mut self, ratio: f32) -> Self { - self.style().aspect_ratio = Some(ratio); - self - } - - /// Sets the aspect ratio of the element to 1/1 – equal width and height. - /// [Docs](https://tailwindcss.com/docs/aspect-ratio) - fn aspect_square(mut self) -> Self { - self.style().aspect_ratio = Some(1.0); - self - } - - /// Sets the background color of the element. - fn bg(mut self, fill: F) -> Self - where - F: Into, - Self: Sized, - { - self.style().background = Some(fill.into()); - self - } - - /// Sets the border style of the element. - fn border_dashed(mut self) -> Self { - self.style().border_style = Some(BorderStyle::Dashed); - self - } - - /// Returns a mutable reference to the text style that has been configured on this element. - fn text_style(&mut self) -> &mut TextStyleRefinement { - let style: &mut StyleRefinement = self.style(); - &mut style.text - } - - /// Sets the text color of this element. - /// - /// This value cascades to its child elements. - fn text_color(mut self, color: impl Into) -> Self { - self.text_style().color = Some(color.into()); - self - } - - /// Sets the font weight of this element - /// - /// This value cascades to its child elements. - fn font_weight(mut self, weight: FontWeight) -> Self { - self.text_style().font_weight = Some(weight); - self - } - - /// Sets the background color of this element. - /// - /// This value cascades to its child elements. - fn text_bg(mut self, bg: impl Into) -> Self { - self.text_style().background_color = Some(bg.into()); - self - } - - /// Sets the text size of this element. - /// - /// This value cascades to its child elements. - fn text_size(mut self, size: impl Into) -> Self { - self.text_style().font_size = Some(size.into()); - self - } - - /// Sets the text size to 'extra small'. - /// [Docs](https://tailwindcss.com/docs/font-size#setting-the-font-size) - fn text_xs(mut self) -> Self { - self.text_style().font_size = Some(rems(0.75).into()); - self - } - - /// Sets the text size to 'small'. - /// [Docs](https://tailwindcss.com/docs/font-size#setting-the-font-size) - fn text_sm(mut self) -> Self { - self.text_style().font_size = Some(rems(0.875).into()); - self - } - - /// Sets the text size to 'base'. - /// [Docs](https://tailwindcss.com/docs/font-size#setting-the-font-size) - fn text_base(mut self) -> Self { - self.text_style().font_size = Some(rems(1.0).into()); - self - } - - /// Sets the text size to 'large'. - /// [Docs](https://tailwindcss.com/docs/font-size#setting-the-font-size) - fn text_lg(mut self) -> Self { - self.text_style().font_size = Some(rems(1.125).into()); - self - } - - /// Sets the text size to 'extra large'. - /// [Docs](https://tailwindcss.com/docs/font-size#setting-the-font-size) - fn text_xl(mut self) -> Self { - self.text_style().font_size = Some(rems(1.25).into()); - self - } - - /// Sets the text size to 'extra extra large'. - /// [Docs](https://tailwindcss.com/docs/font-size#setting-the-font-size) - fn text_2xl(mut self) -> Self { - self.text_style().font_size = Some(rems(1.5).into()); - self - } - - /// Sets the text size to 'extra extra extra large'. - /// [Docs](https://tailwindcss.com/docs/font-size#setting-the-font-size) - fn text_3xl(mut self) -> Self { - self.text_style().font_size = Some(rems(1.875).into()); - self - } - - /// Sets the font style of the element to italic. - /// [Docs](https://tailwindcss.com/docs/font-style#italicizing-text) - fn italic(mut self) -> Self { - self.text_style().font_style = Some(FontStyle::Italic); - self - } - - /// Sets the font style of the element to normal (not italic). - /// [Docs](https://tailwindcss.com/docs/font-style#displaying-text-normally) - fn not_italic(mut self) -> Self { - self.text_style().font_style = Some(FontStyle::Normal); - self - } - - /// Sets the text decoration to underline. - /// [Docs](https://tailwindcss.com/docs/text-decoration-line#underling-text) - fn underline(mut self) -> Self { - let style = self.text_style(); - style.underline = Some(UnderlineStyle { - thickness: px(1.), - ..Default::default() - }); - self - } - - /// Sets the decoration of the text to have a line through it. - /// [Docs](https://tailwindcss.com/docs/text-decoration-line#adding-a-line-through-text) - fn line_through(mut self) -> Self { - let style = self.text_style(); - style.strikethrough = Some(StrikethroughStyle { - thickness: px(1.), - ..Default::default() - }); - self - } - - /// Removes the text decoration on this element. - /// - /// This value cascades to its child elements. - fn text_decoration_none(mut self) -> Self { - self.text_style().underline = None; - self - } - - /// Sets the color for the underline on this element - fn text_decoration_color(mut self, color: impl Into) -> Self { - let style = self.text_style(); - let underline = style.underline.get_or_insert_with(Default::default); - underline.color = Some(color.into()); - self - } - - /// Sets the text decoration style to a solid line. - /// [Docs](https://tailwindcss.com/docs/text-decoration-style) - fn text_decoration_solid(mut self) -> Self { - let style = self.text_style(); - let underline = style.underline.get_or_insert_with(Default::default); - underline.wavy = false; - self - } - - /// Sets the text decoration style to a wavy line. - /// [Docs](https://tailwindcss.com/docs/text-decoration-style) - fn text_decoration_wavy(mut self) -> Self { - let style = self.text_style(); - let underline = style.underline.get_or_insert_with(Default::default); - underline.wavy = true; - self - } - - /// Sets the text decoration to be 0px thick. - /// [Docs](https://tailwindcss.com/docs/text-decoration-thickness) - fn text_decoration_0(mut self) -> Self { - let style = self.text_style(); - let underline = style.underline.get_or_insert_with(Default::default); - underline.thickness = px(0.); - self - } - - /// Sets the text decoration to be 1px thick. - /// [Docs](https://tailwindcss.com/docs/text-decoration-thickness) - fn text_decoration_1(mut self) -> Self { - let style = self.text_style(); - let underline = style.underline.get_or_insert_with(Default::default); - underline.thickness = px(1.); - self - } - - /// Sets the text decoration to be 2px thick. - /// [Docs](https://tailwindcss.com/docs/text-decoration-thickness) - fn text_decoration_2(mut self) -> Self { - let style = self.text_style(); - let underline = style.underline.get_or_insert_with(Default::default); - underline.thickness = px(2.); - self - } - - /// Sets the text decoration to be 4px thick. - /// [Docs](https://tailwindcss.com/docs/text-decoration-thickness) - fn text_decoration_4(mut self) -> Self { - let style = self.text_style(); - let underline = style.underline.get_or_insert_with(Default::default); - underline.thickness = px(4.); - self - } - - /// Sets the text decoration to be 8px thick. - /// [Docs](https://tailwindcss.com/docs/text-decoration-thickness) - fn text_decoration_8(mut self) -> Self { - let style = self.text_style(); - let underline = style.underline.get_or_insert_with(Default::default); - underline.thickness = px(8.); - self - } - - /// Sets the font family of this element and its children. - fn font_family(mut self, family_name: impl Into) -> Self { - self.text_style().font_family = Some(family_name.into()); - self - } - - /// Sets the font features of this element and its children. - fn font_features(mut self, features: FontFeatures) -> Self { - self.text_style().font_features = Some(features); - self - } - - /// Sets the font of this element and its children. - fn font(mut self, font: Font) -> Self { - let Font { - family, - features, - fallbacks, - weight, - style, - } = font; - - let text_style = self.text_style(); - text_style.font_family = Some(family); - text_style.font_features = Some(features); - text_style.font_weight = Some(weight); - text_style.font_style = Some(style); - text_style.font_fallbacks = fallbacks; - - self - } - - /// Sets the line height of this element and its children. - fn line_height(mut self, line_height: impl Into) -> Self { - self.text_style().line_height = Some(line_height.into()); - self - } - - /// Sets the opacity of this element and its children. - fn opacity(mut self, opacity: f32) -> Self { - self.style().opacity = Some(opacity); - self - } - - /// Sets the grid columns of this element. - fn grid_cols(mut self, cols: u16) -> Self { - self.style().grid_cols = Some(GridTemplate { - repeat: cols, - min_size: GridTemplateMinSize::Zero, - }); - self - } - - /// Sets the grid columns with min-content minimum sizing. - /// Unlike grid_cols, it won't shrink to width 0 in AvailableSpace::MinContent constraints. - fn grid_cols_min_content(mut self, cols: u16) -> Self { - self.style().grid_cols = Some(GridTemplate { - repeat: cols, - min_size: GridTemplateMinSize::MinContent, - }); - self - } - - /// Sets the grid columns with max-content maximum sizing for content-based column widths. - fn grid_cols_max_content(mut self, cols: u16) -> Self { - self.style().grid_cols = Some(GridTemplate { - repeat: cols, - min_size: GridTemplateMinSize::MaxContent, - }); - self - } - - /// Sets the grid rows of this element. - fn grid_rows(mut self, rows: u16) -> Self { - self.style().grid_rows = Some(GridTemplate { - repeat: rows, - min_size: GridTemplateMinSize::Zero, - }); - self - } - - /// Sets the grid rows with min-content minimum sizing. - /// Unlike grid_rows, it won't shrink to height 0 in AvailableSpace::MinContent constraints. - fn grid_rows_min_content(mut self, rows: u16) -> Self { - self.style().grid_rows = Some(GridTemplate { - repeat: rows, - min_size: GridTemplateMinSize::MinContent, - }); - self - } - - /// Sets the grid rows with max-content maximum sizing for content-based row heights. - fn grid_rows_max_content(mut self, rows: u16) -> Self { - self.style().grid_rows = Some(GridTemplate { - repeat: rows, - min_size: GridTemplateMinSize::MaxContent, - }); - self - } - - /// Sets the column start of this element. - fn col_start(mut self, start: i16) -> Self { - let grid_location = self.style().grid_location_mut(); - grid_location.column.start = GridPlacement::Line(start); - self - } - - /// Sets the column start of this element to auto. - fn col_start_auto(mut self) -> Self { - let grid_location = self.style().grid_location_mut(); - grid_location.column.start = GridPlacement::Auto; - self - } - - /// Sets the column end of this element. - fn col_end(mut self, end: i16) -> Self { - let grid_location = self.style().grid_location_mut(); - grid_location.column.end = GridPlacement::Line(end); - self - } - - /// Sets the column end of this element to auto. - fn col_end_auto(mut self) -> Self { - let grid_location = self.style().grid_location_mut(); - grid_location.column.end = GridPlacement::Auto; - self - } - - /// Sets the column span of this element. - fn col_span(mut self, span: u16) -> Self { - let grid_location = self.style().grid_location_mut(); - grid_location.column = GridPlacement::Span(span)..GridPlacement::Span(span); - self - } - - /// Sets the row span of this element. - fn col_span_full(mut self) -> Self { - let grid_location = self.style().grid_location_mut(); - grid_location.column = GridPlacement::Line(1)..GridPlacement::Line(-1); - self - } - - /// Sets the row start of this element. - fn row_start(mut self, start: i16) -> Self { - let grid_location = self.style().grid_location_mut(); - grid_location.row.start = GridPlacement::Line(start); - self - } - - /// Sets the row start of this element to "auto" - fn row_start_auto(mut self) -> Self { - let grid_location = self.style().grid_location_mut(); - grid_location.row.start = GridPlacement::Auto; - self - } - - /// Sets the row end of this element. - fn row_end(mut self, end: i16) -> Self { - let grid_location = self.style().grid_location_mut(); - grid_location.row.end = GridPlacement::Line(end); - self - } - - /// Sets the row end of this element to "auto" - fn row_end_auto(mut self) -> Self { - let grid_location = self.style().grid_location_mut(); - grid_location.row.end = GridPlacement::Auto; - self - } - - /// Sets the row span of this element. - fn row_span(mut self, span: u16) -> Self { - let grid_location = self.style().grid_location_mut(); - grid_location.row = GridPlacement::Span(span)..GridPlacement::Span(span); - self - } - - /// Sets the row span of this element. - fn row_span_full(mut self) -> Self { - let grid_location = self.style().grid_location_mut(); - grid_location.row = GridPlacement::Line(1)..GridPlacement::Line(-1); - self - } - - /// Draws a debug border around this element. - #[cfg(debug_assertions)] - fn debug(mut self) -> Self { - self.style().debug = Some(true); - self - } - - /// Draws a debug border on all conforming elements below this element. - #[cfg(debug_assertions)] - fn debug_below(mut self) -> Self { - self.style().debug_below = Some(true); - self - } -} diff --git a/crates/gpui_pre/src/subscription.rs b/crates/gpui_pre/src/subscription.rs deleted file mode 100644 index b0c55a39..00000000 --- a/crates/gpui_pre/src/subscription.rs +++ /dev/null @@ -1,351 +0,0 @@ -use collections::BTreeMap; -use gpui_util::post_inc; -use std::{ - cell::{Cell, RefCell}, - fmt::Debug, - rc::Rc, -}; - -pub(crate) struct SubscriberSet( - Rc>>, -); - -impl Clone for SubscriberSet { - fn clone(&self) -> Self { - SubscriberSet(self.0.clone()) - } -} - -struct SubscriberSetState { - subscribers: BTreeMap>>>, - next_subscriber_id: usize, -} - -struct Subscriber { - active: Rc>, - dropped: Rc>, - callback: Callback, -} - -impl SubscriberSet -where - EmitterKey: 'static + Ord + Clone + Debug, - Callback: 'static, -{ - pub fn new() -> Self { - Self(Rc::new(RefCell::new(SubscriberSetState { - subscribers: Default::default(), - next_subscriber_id: 0, - }))) - } - - /// Inserts a new [`Subscription`] for the given `emitter_key`. By default, subscriptions - /// are inert, meaning that they won't be listed when calling `[SubscriberSet::remove]` or `[SubscriberSet::retain]`. - /// This method returns a tuple of a [`Subscription`] and an `impl FnOnce`, and you can use the latter - /// to activate the [`Subscription`]. - pub fn insert( - &self, - emitter_key: EmitterKey, - callback: Callback, - ) -> (Subscription, impl FnOnce() + use) { - let active = Rc::new(Cell::new(false)); - let dropped = Rc::new(Cell::new(false)); - let mut lock = self.0.borrow_mut(); - let subscriber_id = post_inc(&mut lock.next_subscriber_id); - lock.subscribers - .entry(emitter_key.clone()) - .or_default() - .get_or_insert_with(Default::default) - .insert( - subscriber_id, - Subscriber { - active: active.clone(), - dropped: dropped.clone(), - callback, - }, - ); - let this = self.0.clone(); - - let subscription = Subscription { - unsubscribe: Some(Box::new(move || { - dropped.set(true); - - let mut lock = this.borrow_mut(); - let Some(subscribers) = lock.subscribers.get_mut(&emitter_key) else { - return; - }; - - if let Some(subscribers) = subscribers { - subscribers.remove(&subscriber_id); - if subscribers.is_empty() { - lock.subscribers.remove(&emitter_key); - } - } - })), - }; - (subscription, move || active.set(true)) - } - - pub fn remove( - &self, - emitter: &EmitterKey, - ) -> impl IntoIterator + use { - let subscribers = self.0.borrow_mut().subscribers.remove(emitter); - subscribers - .unwrap_or_default() - .map(|s| s.into_values()) - .into_iter() - .flatten() - .filter_map(|subscriber| { - if subscriber.active.get() { - Some(subscriber.callback) - } else { - None - } - }) - } - - /// Call the given callback for each subscriber to the given emitter. - /// If the callback returns false, the subscriber is removed. - pub fn retain(&self, emitter: &EmitterKey, mut f: F) - where - F: FnMut(&mut Callback) -> bool, - { - let Some(mut subscribers) = self - .0 - .borrow_mut() - .subscribers - .get_mut(emitter) - .and_then(|s| s.take()) - else { - return; - }; - - subscribers.retain(|_, subscriber| { - if !subscriber.active.get() { - return true; - } - if subscriber.dropped.get() { - return false; - } - let keep = f(&mut subscriber.callback); - keep && !subscriber.dropped.get() - }); - let mut lock = self.0.borrow_mut(); - - // Add any new subscribers that were added while invoking the callback. - if let Some(Some(new_subscribers)) = lock.subscribers.remove(emitter) { - subscribers.extend(new_subscribers); - } - - if !subscribers.is_empty() { - lock.subscribers.insert(emitter.clone(), Some(subscribers)); - } - } -} - -/// A handle to a subscription created by GPUI. When dropped, the subscription -/// is cancelled and the callback will no longer be invoked. -#[must_use] -pub struct Subscription { - unsubscribe: Option>, -} - -impl Subscription { - /// Creates a new subscription with a callback that gets invoked when - /// this subscription is dropped. - pub fn new(unsubscribe: impl 'static + FnOnce()) -> Self { - Self { - unsubscribe: Some(Box::new(unsubscribe)), - } - } - - /// Detaches the subscription from this handle. The callback will - /// continue to be invoked until the entities it has been - /// subscribed to are dropped - pub fn detach(mut self) { - self.unsubscribe.take(); - } - - /// Joins two subscriptions into a single subscription. Detach will - /// detach both interior subscriptions. - pub fn join(mut subscription_a: Self, mut subscription_b: Self) -> Self { - let a_unsubscribe = subscription_a.unsubscribe.take(); - let b_unsubscribe = subscription_b.unsubscribe.take(); - Self { - unsubscribe: Some(Box::new(move || { - if let Some(self_unsubscribe) = a_unsubscribe { - self_unsubscribe(); - } - if let Some(other_unsubscribe) = b_unsubscribe { - other_unsubscribe(); - } - })), - } - } -} - -impl Drop for Subscription { - fn drop(&mut self) { - if let Some(unsubscribe) = self.unsubscribe.take() { - unsubscribe(); - } - } -} - -impl std::fmt::Debug for Subscription { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Subscription").finish() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{Global, TestApp}; - - #[test] - fn test_unsubscribe_during_callback_with_insert() { - struct TestGlobal; - impl Global for TestGlobal {} - - let mut app = TestApp::new(); - app.set_global(TestGlobal); - - let observer_a_count = Rc::new(Cell::new(0usize)); - let observer_b_count = Rc::new(Cell::new(0usize)); - - let sub_a: Rc>> = Default::default(); - let sub_b: Rc>> = Default::default(); - - // Observer A fires first (lower subscriber_id). It drops itself and - // inserts a new observer for the same global. - *sub_a.borrow_mut() = Some(app.update({ - let count = observer_a_count.clone(); - let sub_a = sub_a.clone(); - move |cx| { - cx.observe_global::(move |cx| { - count.set(count.get() + 1); - sub_a.borrow_mut().take(); - cx.observe_global::(|_| {}).detach(); - }) - } - })); - - // Observer B fires second. It just drops itself. - *sub_b.borrow_mut() = Some(app.update({ - let count = observer_b_count.clone(); - let sub_b = sub_b.clone(); - move |cx| { - cx.observe_global::(move |_cx| { - count.set(count.get() + 1); - sub_b.borrow_mut().take(); - }) - } - })); - - // Both fire once. - app.update(|cx| cx.set_global(TestGlobal)); - assert_eq!(observer_a_count.get(), 1); - assert_eq!(observer_b_count.get(), 1); - - // Neither should fire again — both dropped their subscriptions. - app.update(|cx| cx.set_global(TestGlobal)); - assert_eq!(observer_a_count.get(), 1); - assert_eq!(observer_b_count.get(), 1, "orphaned subscriber fired again"); - } - - #[test] - fn test_callback_dropped_by_earlier_callback_does_not_fire() { - struct TestGlobal; - impl Global for TestGlobal {} - - let mut app = TestApp::new(); - app.set_global(TestGlobal); - - let observer_b_count = Rc::new(Cell::new(0usize)); - let sub_b: Rc>> = Default::default(); - - // Observer A fires first and drops B's subscription. - app.update({ - let sub_b = sub_b.clone(); - move |cx| { - cx.observe_global::(move |_cx| { - sub_b.borrow_mut().take(); - }) - .detach(); - } - }); - - // Observer B fires second — but A already dropped it. - *sub_b.borrow_mut() = Some(app.update({ - let count = observer_b_count.clone(); - move |cx| { - cx.observe_global::(move |_cx| { - count.set(count.get() + 1); - }) - } - })); - - app.update(|cx| cx.set_global(TestGlobal)); - assert_eq!( - observer_b_count.get(), - 0, - "B should not fire — A dropped its subscription" - ); - } - - #[test] - fn test_self_drop_during_callback() { - struct TestGlobal; - impl Global for TestGlobal {} - - let mut app = TestApp::new(); - app.set_global(TestGlobal); - - let count = Rc::new(Cell::new(0usize)); - let sub: Rc>> = Default::default(); - - *sub.borrow_mut() = Some(app.update({ - let count = count.clone(); - let sub = sub.clone(); - move |cx| { - cx.observe_global::(move |_cx| { - count.set(count.get() + 1); - sub.borrow_mut().take(); - }) - } - })); - - app.update(|cx| cx.set_global(TestGlobal)); - assert_eq!(count.get(), 1); - - app.update(|cx| cx.set_global(TestGlobal)); - assert_eq!(count.get(), 1, "should not fire after self-drop"); - } - - #[test] - fn test_subscription_drop() { - struct TestGlobal; - impl Global for TestGlobal {} - - let mut app = TestApp::new(); - app.set_global(TestGlobal); - - let count = Rc::new(Cell::new(0usize)); - - let subscription = app.update({ - let count = count.clone(); - move |cx| { - cx.observe_global::(move |_cx| { - count.set(count.get() + 1); - }) - } - }); - - drop(subscription); - - app.update(|cx| cx.set_global(TestGlobal)); - assert_eq!(count.get(), 0, "should not fire after drop"); - } -} diff --git a/crates/gpui_pre/src/svg_renderer.rs b/crates/gpui_pre/src/svg_renderer.rs deleted file mode 100644 index d06a4b0a..00000000 --- a/crates/gpui_pre/src/svg_renderer.rs +++ /dev/null @@ -1,527 +0,0 @@ -use crate::{ - AssetSource, DevicePixels, IsZero, RenderImage, Result, SharedString, Size, - swap_rgba_pa_to_bgra, -}; -use image::Frame; -use resvg::tiny_skia::Pixmap; -use smallvec::SmallVec; -use std::{ - hash::Hash, - sync::{Arc, LazyLock, OnceLock}, -}; - -#[cfg(target_os = "macos")] -const EMOJI_FONT_FAMILIES: &[&str] = &["Apple Color Emoji", ".AppleColorEmojiUI"]; - -#[cfg(target_os = "windows")] -const EMOJI_FONT_FAMILIES: &[&str] = &["Segoe UI Emoji", "Segoe UI Symbol"]; - -#[cfg(any(target_os = "linux", target_os = "freebsd"))] -const EMOJI_FONT_FAMILIES: &[&str] = &[ - "Noto Color Emoji", - "Emoji One", - "Twitter Color Emoji", - "JoyPixels", -]; - -#[cfg(not(any( - target_os = "macos", - target_os = "windows", - target_os = "linux", - target_os = "freebsd", -)))] -const EMOJI_FONT_FAMILIES: &[&str] = &[]; - -fn is_emoji_presentation(c: char) -> bool { - static EMOJI_PRESENTATION_REGEX: LazyLock = - LazyLock::new(|| regex::Regex::new("\\p{Emoji_Presentation}").unwrap()); - let mut buf = [0u8; 4]; - EMOJI_PRESENTATION_REGEX.is_match(c.encode_utf8(&mut buf)) -} - -fn font_has_char(db: &usvg::fontdb::Database, id: usvg::fontdb::ID, ch: char) -> bool { - db.with_face_data(id, |font_data, face_index| { - ttf_parser::Face::parse(font_data, face_index) - .ok() - .and_then(|face| face.glyph_index(ch)) - .is_some() - }) - .unwrap_or(false) -} - -fn select_emoji_font( - ch: char, - fonts: &[usvg::fontdb::ID], - db: &usvg::fontdb::Database, - families: &[&str], -) -> Option { - for family_name in families { - let query = usvg::fontdb::Query { - families: &[usvg::fontdb::Family::Name(family_name)], - weight: usvg::fontdb::Weight(400), - stretch: usvg::fontdb::Stretch::Normal, - style: usvg::fontdb::Style::Normal, - }; - - let Some(id) = db.query(&query) else { - continue; - }; - - if fonts.contains(&id) || !font_has_char(db, id, ch) { - continue; - } - - return Some(id); - } - - None -} - -/// When rendering SVGs, we render them at twice the size to get a higher-quality result. -pub const SMOOTH_SVG_SCALE_FACTOR: f32 = 2.; - -#[derive(Clone, PartialEq, Hash, Eq)] -#[expect(missing_docs)] -pub struct RenderSvgParams { - pub path: SharedString, - pub size: Size, -} - -#[derive(Clone)] -/// A struct holding everything necessary to render SVGs. -pub struct SvgRenderer { - asset_source: Arc, - usvg_options: Arc>, -} - -/// A parsed SVG document that can be rasterized at any scale. -/// -/// Produced by [`SvgRenderer::parse_svg`] and rasterized by -/// [`SvgRenderer::render_parsed`]. Parsing resolves fonts and converts text -/// to paths, so callers that need to rasterize the same SVG at multiple -/// scales should retain this value to avoid re-paying the parse cost. -pub struct ParsedSvg(usvg::Tree); - -/// The size in which to rasterize the SVG. -#[derive(Clone, Copy)] -pub enum SvgSize { - /// A width in device pixels. The SVG retains its aspect ratio. - Size(Size), - /// An exact width and height in device pixels. - ExactSize(Size), - /// A logical scaling factor to apply to the size provided by the SVG. - ScaleFactor(f32), -} - -impl From for SvgSize { - fn from(scale_factor: f32) -> Self { - Self::ScaleFactor(scale_factor) - } -} - -impl SvgRenderer { - /// Creates a new SVG renderer with the provided asset source. - pub fn new(asset_source: Arc) -> Self { - static SYSTEM_FONT_DB: LazyLock> = LazyLock::new(|| { - let mut db = usvg::fontdb::Database::new(); - db.load_system_fonts(); - Arc::new(db) - }); - - // Build the enriched font DB lazily on first SVG render rather than - // eagerly at construction time. This avoids the expensive deep-clone - // of the system font database for code paths that never render SVGs - // (e.g. tests). - let enriched_fontdb: Arc>> = Arc::new(OnceLock::new()); - - let default_font_resolver = usvg::FontResolver::default_font_selector(); - let font_resolver = Box::new({ - let asset_source = asset_source.clone(); - move |font: &usvg::Font, db: &mut Arc| { - if db.is_empty() { - let fontdb = enriched_fontdb.get_or_init(|| { - let mut db = (**SYSTEM_FONT_DB).clone(); - load_bundled_fonts(&*asset_source, &mut db); - fix_generic_font_families(&mut db); - Arc::new(db) - }); - *db = fontdb.clone(); - } - if let Some(id) = default_font_resolver(font, db) { - return Some(id); - } - // fontdb doesn't recognize CSS system font keywords like "system-ui" - // or "ui-sans-serif", so fall back to sans-serif before any face. - let sans_query = usvg::fontdb::Query { - families: &[usvg::fontdb::Family::SansSerif], - ..Default::default() - }; - db.query(&sans_query) - .or_else(|| db.faces().next().map(|f| f.id)) - } - }); - let default_fallback_selection = usvg::FontResolver::default_fallback_selector(); - let fallback_selection = Box::new( - move |ch: char, fonts: &[usvg::fontdb::ID], db: &mut Arc| { - if is_emoji_presentation(ch) { - if let Some(id) = select_emoji_font(ch, fonts, db.as_ref(), EMOJI_FONT_FAMILIES) - { - return Some(id); - } - } - - default_fallback_selection(ch, fonts, db) - }, - ); - let options = usvg::Options { - font_resolver: usvg::FontResolver { - select_font: font_resolver, - select_fallback: fallback_selection, - }, - ..Default::default() - }; - Self { - asset_source, - usvg_options: Arc::new(options), - } - } - - /// Parses SVG data into a [`ParsedSvg`] that can be rasterized at any scale. - #[ztracing::instrument(skip_all)] - pub fn parse_svg(&self, bytes: &[u8]) -> Result { - usvg::Tree::from_data(bytes, &self.usvg_options).map(ParsedSvg) - } - - /// Rasterizes a previously parsed SVG into an image buffer. - #[ztracing::instrument(skip_all)] - pub fn render_parsed( - &self, - svg: &ParsedSvg, - size: impl Into, - ) -> Result, usvg::Error> { - let (size, image_scale_factor) = match size.into() { - SvgSize::Size(size) => (SvgSize::Size(size), 1.0), - SvgSize::ExactSize(size) => (SvgSize::ExactSize(size), 1.0), - SvgSize::ScaleFactor(scale_factor) => ( - SvgSize::ScaleFactor(scale_factor * SMOOTH_SVG_SCALE_FACTOR), - SMOOTH_SVG_SCALE_FACTOR, - ), - }; - let pixmap = rasterize_tree(&svg.0, size)?; - let mut buffer = - image::ImageBuffer::from_raw(pixmap.width(), pixmap.height(), pixmap.take()).unwrap(); - - for pixel in buffer.chunks_exact_mut(4) { - swap_rgba_pa_to_bgra(pixel); - } - - let mut image = RenderImage::new(SmallVec::from_const([Frame::new(buffer)])); - image.scale_factor = image_scale_factor; - Ok(Arc::new(image)) - } - - /// Renders the given bytes into an image buffer. - pub fn render_single_frame( - &self, - bytes: &[u8], - scale_factor: f32, - ) -> Result, usvg::Error> { - let svg = self.parse_svg(bytes)?; - self.render_parsed(&svg, scale_factor) - } - - pub(crate) fn render_alpha_mask( - &self, - params: &RenderSvgParams, - bytes: Option<&[u8]>, - ) -> Result, Vec)>> { - anyhow::ensure!(!params.size.is_zero(), "can't render at a zero size"); - - let render_pixmap = |bytes| { - let pixmap = self.render_pixmap(bytes, SvgSize::Size(params.size))?; - - // Convert the pixmap's pixels into an alpha mask. - let size = Size::new( - DevicePixels(pixmap.width() as i32), - DevicePixels(pixmap.height() as i32), - ); - let alpha_mask = pixmap - .pixels() - .iter() - .map(|p| p.alpha()) - .collect::>(); - - Ok(Some((size, alpha_mask))) - }; - - if let Some(bytes) = bytes { - render_pixmap(bytes) - } else if let Some(bytes) = self.asset_source.load(¶ms.path)? { - render_pixmap(&bytes) - } else { - Ok(None) - } - } - - fn render_pixmap(&self, bytes: &[u8], size: SvgSize) -> Result { - let tree = usvg::Tree::from_data(bytes, &self.usvg_options)?; - rasterize_tree(&tree, size) - } -} - -fn rasterize_tree(tree: &usvg::Tree, size: SvgSize) -> Result { - // Cap the size of the rendered pixmap to avoid texture allocation panics - // Related issue: #56466 - const MAX_SIZE: f32 = 8192.0; - - let svg_size = tree.size(); - let (mut width, mut height) = match size { - SvgSize::Size(size) => { - let scale = i32::from(size.width) as f32 / svg_size.width(); - (svg_size.width() * scale, svg_size.height() * scale) - } - SvgSize::ExactSize(size) => (i32::from(size.width) as f32, i32::from(size.height) as f32), - SvgSize::ScaleFactor(scale) => (svg_size.width() * scale, svg_size.height() * scale), - }; - - if width > MAX_SIZE { - log::warn!("Attempted to render pixmap where width ({width}) > MAX_SIZE ({MAX_SIZE})"); - } - if height > MAX_SIZE { - log::warn!("Attempted to render pixmap where height ({height}) > MAX_SIZE ({MAX_SIZE})"); - } - let scale = (MAX_SIZE / width).min(MAX_SIZE / height).min(1.0); - width *= scale; - height *= scale; - - // Render the SVG to a pixmap with the specified width and height. - let mut pixmap = resvg::tiny_skia::Pixmap::new(width as u32, height as u32) - .ok_or(usvg::Error::InvalidSize)?; - - let transform = resvg::tiny_skia::Transform::from_scale( - width / svg_size.width(), - height / svg_size.height(), - ); - - resvg::render(tree, transform, &mut pixmap.as_mut()); - - Ok(pixmap) -} - -fn load_bundled_fonts(asset_source: &dyn AssetSource, db: &mut usvg::fontdb::Database) { - let font_paths = [ - "fonts/ibm-plex-sans/IBMPlexSans-Regular.ttf", - "fonts/lilex/Lilex-Regular.ttf", - ]; - for path in font_paths { - match asset_source.load(path) { - Ok(Some(data)) => db.load_font_data(data.into_owned()), - Ok(None) => log::warn!("Bundled font not found: {path}"), - Err(error) => log::warn!("Failed to load bundled font {path}: {error}"), - } - } -} - -// fontdb defaults generic families to Microsoft fonts ("Arial", "Times New Roman") -// which aren't installed on most Linux systems. fontconfig normally overrides these, -// but when it fails the defaults remain and all generic family queries return None. -fn fix_generic_font_families(db: &mut usvg::fontdb::Database) { - use usvg::fontdb::{Family, Query}; - - let families_and_fallbacks: &[(Family<'_>, &str)] = &[ - (Family::SansSerif, "IBM Plex Sans"), - // No serif font bundled; use sans-serif as best available fallback. - (Family::Serif, "IBM Plex Sans"), - (Family::Monospace, "Lilex"), - (Family::Cursive, "IBM Plex Sans"), - (Family::Fantasy, "IBM Plex Sans"), - ]; - - for (family, fallback_name) in families_and_fallbacks { - let query = Query { - families: &[*family], - ..Default::default() - }; - if db.query(&query).is_none() { - match family { - Family::SansSerif => db.set_sans_serif_family(*fallback_name), - Family::Serif => db.set_serif_family(*fallback_name), - Family::Monospace => db.set_monospace_family(*fallback_name), - Family::Cursive => db.set_cursive_family(*fallback_name), - Family::Fantasy => db.set_fantasy_family(*fallback_name), - _ => {} - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use usvg::fontdb::{Database, Family, Query}; - - const IBM_PLEX_REGULAR: &[u8] = - include_bytes!("../../../assets/fonts/ibm-plex-sans/IBMPlexSans-Regular.ttf"); - const LILEX_REGULAR: &[u8] = include_bytes!("../../../assets/fonts/lilex/Lilex-Regular.ttf"); - - #[test] - fn renders_parsed_svg_at_requested_size() -> Result<()> { - let renderer = SvgRenderer::new(Arc::new(())); - let svg = renderer.parse_svg( - br#""#, - )?; - let requested_size = Size::new(DevicePixels(24), DevicePixels(12)); - let image = renderer.render_parsed(&svg, SvgSize::ExactSize(requested_size))?; - - assert_eq!(image.size(0), requested_size); - Ok(()) - } - - #[test] - fn preserves_aspect_ratio_for_width_constrained_size() -> Result<()> { - let renderer = SvgRenderer::new(Arc::new(())); - let svg = renderer.parse_svg( - br#""#, - )?; - let image = renderer.render_parsed( - &svg, - SvgSize::Size(Size::new(DevicePixels(24), DevicePixels(24))), - )?; - - assert_eq!(image.size(0), Size::new(DevicePixels(24), DevicePixels(12))); - Ok(()) - } - - fn db_with_bundled_fonts() -> Database { - let mut db = Database::new(); - db.load_font_data(IBM_PLEX_REGULAR.to_vec()); - db.load_font_data(LILEX_REGULAR.to_vec()); - db - } - - #[test] - fn text_with_split_glyph_clusters_in_mixed_fonts_does_not_panic() { - let mut db = Database::new(); - db.load_font_data(IBM_PLEX_REGULAR.to_vec()); - db.load_font_data(LILEX_REGULAR.to_vec()); - let options = usvg::Options { - fontdb: std::sync::Arc::new(db), - ..Default::default() - }; - - // A base letter followed by a stack of combining marks. Under HarfBuzz's - // default cluster merging every mark glyph shares the base's byte index, - // which is the "glyph splitting" condition that triggered the panic. The - // chunk must use two different fonts so the buggy merge path runs. - let zalgo = "e\u{0301}\u{0302}\u{0303}\u{0304}\u{0306}\u{0307}\u{0308}\u{030a}"; - let svg = format!( - r#"{zalgo}{zalgo}"# - ); - - // Before the fix this aborts via panic with a message like - // "removal index (is 5) should be < len (is 5)". - usvg::Tree::from_data(svg.as_bytes(), &options) - .expect("SVG with mixed-font text should parse"); - } - - #[test] - fn test_is_emoji_presentation() { - let cases = [ - ("a", false), - ("Z", false), - ("1", false), - ("#", false), - ("*", false), - ("漢", false), - ("中", false), - ("カ", false), - ("©", false), - ("♥", false), - ("😀", true), - ("✅", true), - ("🇺🇸", true), - // SVG fallback is not cluster-aware yet - ("©️", false), - ("♥️", false), - ("1️⃣", false), - ]; - for (s, expected) in cases { - assert_eq!( - is_emoji_presentation(s.chars().next().unwrap()), - expected, - "for char {:?}", - s - ); - } - } - - #[test] - fn fix_generic_font_families_sets_all_families() { - let mut db = db_with_bundled_fonts(); - fix_generic_font_families(&mut db); - - let families = [ - Family::SansSerif, - Family::Serif, - Family::Monospace, - Family::Cursive, - Family::Fantasy, - ]; - - for family in families { - let query = Query { - families: &[family], - ..Default::default() - }; - assert!( - db.query(&query).is_some(), - "Expected generic family {family:?} to resolve after fix_generic_font_families" - ); - } - } - - #[test] - fn test_select_emoji_font_skips_family_without_glyph() { - let mut db = db_with_bundled_fonts(); - - let ibm_plex_sans = db - .query(&usvg::fontdb::Query { - families: &[usvg::fontdb::Family::Name("IBM Plex Sans")], - weight: usvg::fontdb::Weight(400), - stretch: usvg::fontdb::Stretch::Normal, - style: usvg::fontdb::Style::Normal, - }) - .unwrap(); - let lilex = db - .query(&usvg::fontdb::Query { - families: &[usvg::fontdb::Family::Name("Lilex")], - weight: usvg::fontdb::Weight(400), - stretch: usvg::fontdb::Stretch::Normal, - style: usvg::fontdb::Style::Normal, - }) - .unwrap(); - let selected = select_emoji_font('│', &[], &db, &["IBM Plex Sans", "Lilex"]).unwrap(); - - assert_eq!(selected, lilex); - assert!(!font_has_char(&db, ibm_plex_sans, '│')); - assert!(font_has_char(&db, selected, '│')); - } - - #[test] - fn fix_generic_font_families_monospace_resolves_to_lilex() { - let mut db = db_with_bundled_fonts(); - fix_generic_font_families(&mut db); - - let query = Query { - families: &[Family::Monospace], - ..Default::default() - }; - let id = db.query(&query).expect("Monospace should resolve"); - let face = db.face(id).expect("Face should exist"); - assert!( - face.families.iter().any(|(name, _)| name.contains("Lilex")), - "Monospace should map to Lilex, got {:?}", - face.families - ); - } -} diff --git a/crates/gpui_pre/src/tab_stop.rs b/crates/gpui_pre/src/tab_stop.rs deleted file mode 100644 index bde651ae..00000000 --- a/crates/gpui_pre/src/tab_stop.rs +++ /dev/null @@ -1,615 +0,0 @@ -use std::fmt::Debug; - -use ::sum_tree::SumTree; -use collections::FxHashMap; -use sum_tree::Bias; - -use crate::{FocusHandle, FocusId}; - -/// Represents a collection of focus handles using the tab-index APIs. -#[derive(Debug)] -pub(crate) struct TabStopMap { - current_path: TabStopPath, - pub(crate) insertion_history: Vec, - by_id: FxHashMap, - order: SumTree, -} - -#[derive(Debug, Clone)] -pub enum TabStopOperation { - Insert(FocusHandle), - Group(TabIndex), - GroupEnd, -} - -impl TabStopOperation { - fn focus_handle(&self) -> Option<&FocusHandle> { - match self { - TabStopOperation::Insert(focus_handle) => Some(focus_handle), - _ => None, - } - } -} - -type TabIndex = isize; - -#[derive(Debug, Default, PartialEq, Eq, Clone, Ord, PartialOrd)] -struct TabStopPath(smallvec::SmallVec<[TabIndex; 6]>); - -#[derive(Clone, Debug, Default, Eq, PartialEq)] -struct TabStopNode { - /// Path to access the node in the tree - /// The final node in the list is a leaf node corresponding to an actual focus handle, - /// all other nodes are group nodes - path: TabStopPath, - /// index into the backing array of nodes. Corresponds to insertion order - node_insertion_index: usize, - - /// Whether this node is a tab stop - tab_stop: bool, -} - -impl Ord for TabStopNode { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.path - .cmp(&other.path) - .then(self.node_insertion_index.cmp(&other.node_insertion_index)) - } -} - -impl PartialOrd for TabStopNode { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(&other)) - } -} - -impl Default for TabStopMap { - fn default() -> Self { - Self { - current_path: TabStopPath::default(), - insertion_history: Vec::new(), - by_id: FxHashMap::default(), - order: SumTree::new(()), - } - } -} - -impl TabStopMap { - pub fn insert(&mut self, focus_handle: &FocusHandle) { - self.insertion_history - .push(TabStopOperation::Insert(focus_handle.clone())); - let mut path = self.current_path.clone(); - path.0.push(focus_handle.tab_index); - let order = TabStopNode { - node_insertion_index: self.insertion_history.len() - 1, - tab_stop: focus_handle.tab_stop, - path, - }; - self.by_id.insert(focus_handle.id, order.clone()); - self.order.insert_or_replace(order, ()); - } - - pub fn begin_group(&mut self, tab_index: isize) { - self.insertion_history - .push(TabStopOperation::Group(tab_index)); - self.current_path.0.push(tab_index); - } - - pub fn end_group(&mut self) { - self.insertion_history.push(TabStopOperation::GroupEnd); - self.current_path.0.pop(); - } - - pub fn clear(&mut self) { - *self = Self::default(); - self.current_path.0.clear(); - self.insertion_history.clear(); - self.by_id.clear(); - self.order = SumTree::new(()); - } - - pub fn next(&self, focused_id: Option<&FocusId>) -> Option { - let Some(focused_id) = focused_id else { - let first = self.order.first()?; - if first.tab_stop { - return self.focus_handle_for_order(first); - } else { - return self - .next_inner(first) - .and_then(|order| self.focus_handle_for_order(order)); - } - }; - - let Some(node) = self.tab_node_for_focus_id(focused_id) else { - return self.next(None); - }; - let item = self.next_inner(node); - - if let Some(item) = item { - self.focus_handle_for_order(&item) - } else { - self.next(None) - } - } - - fn next_inner(&self, node: &TabStopNode) -> Option<&TabStopNode> { - let mut cursor = self.order.cursor::(()); - cursor.seek(&node, Bias::Left); - cursor.next(); - while let Some(item) = cursor.item() - && !item.tab_stop - { - cursor.next(); - } - - cursor.item() - } - - pub fn prev(&self, focused_id: Option<&FocusId>) -> Option { - let Some(focused_id) = focused_id else { - let last = self.order.last()?; - if last.tab_stop { - return self.focus_handle_for_order(last); - } else { - return self - .prev_inner(last) - .and_then(|order| self.focus_handle_for_order(order)); - } - }; - - let Some(node) = self.tab_node_for_focus_id(focused_id) else { - return self.prev(None); - }; - let item = self.prev_inner(node); - - if let Some(item) = item { - self.focus_handle_for_order(&item) - } else { - self.prev(None) - } - } - - fn prev_inner(&self, node: &TabStopNode) -> Option<&TabStopNode> { - let mut cursor = self.order.cursor::(()); - cursor.seek(&node, Bias::Left); - cursor.prev(); - while let Some(item) = cursor.item() - && !item.tab_stop - { - cursor.prev(); - } - - cursor.item() - } - - pub fn replay(&mut self, nodes: &[TabStopOperation]) { - for node in nodes { - match node { - TabStopOperation::Insert(focus_handle) => self.insert(focus_handle), - TabStopOperation::Group(tab_index) => self.begin_group(*tab_index), - TabStopOperation::GroupEnd => self.end_group(), - } - } - } - - pub fn paint_index(&self) -> usize { - self.insertion_history.len() - } - - pub(crate) fn tab_stop_count(&self) -> usize { - self.by_id.values().filter(|node| node.tab_stop).count() - } - - fn focus_handle_for_order(&self, order: &TabStopNode) -> Option { - let handle = self.insertion_history[order.node_insertion_index].focus_handle(); - debug_assert!( - handle.is_some(), - "The order node did not correspond to an element, this is a GPUI bug" - ); - handle.cloned() - } - - fn tab_node_for_focus_id(&self, focused_id: &FocusId) -> Option<&TabStopNode> { - let Some(order) = self.by_id.get(focused_id) else { - return None; - }; - Some(order) - } -} - -mod sum_tree_impl { - use sum_tree::SeekTarget; - - use crate::tab_stop::{TabStopNode, TabStopPath}; - - #[derive(Clone, Debug)] - pub struct TabStopOrderNodeSummary { - max_index: usize, - max_path: TabStopPath, - pub tab_stops: usize, - } - - pub type TabStopCount = usize; - - impl sum_tree::ContextLessSummary for TabStopOrderNodeSummary { - fn zero() -> Self { - TabStopOrderNodeSummary { - max_index: 0, - max_path: TabStopPath::default(), - tab_stops: 0, - } - } - - fn add_summary(&mut self, summary: &Self) { - self.max_index = summary.max_index; - self.max_path = summary.max_path.clone(); - self.tab_stops += summary.tab_stops; - } - } - - impl sum_tree::KeyedItem for TabStopNode { - type Key = Self; - - fn key(&self) -> Self::Key { - self.clone() - } - } - - impl sum_tree::Item for TabStopNode { - type Summary = TabStopOrderNodeSummary; - - fn summary(&self, _cx: ::Context<'_>) -> Self::Summary { - TabStopOrderNodeSummary { - max_index: self.node_insertion_index, - max_path: self.path.clone(), - tab_stops: if self.tab_stop { 1 } else { 0 }, - } - } - } - - impl<'a> sum_tree::Dimension<'a, TabStopOrderNodeSummary> for TabStopCount { - fn zero(_: ::Context<'_>) -> Self { - 0 - } - - fn add_summary( - &mut self, - summary: &'a TabStopOrderNodeSummary, - _: ::Context<'_>, - ) { - *self += summary.tab_stops; - } - } - - impl<'a> sum_tree::Dimension<'a, TabStopOrderNodeSummary> for TabStopNode { - fn zero(_: ::Context<'_>) -> Self { - TabStopNode::default() - } - - fn add_summary( - &mut self, - summary: &'a TabStopOrderNodeSummary, - _: ::Context<'_>, - ) { - self.node_insertion_index = summary.max_index; - self.path = summary.max_path.clone(); - } - } - - impl<'a, 'b> SeekTarget<'a, TabStopOrderNodeSummary, TabStopNode> for &'b TabStopNode { - fn cmp( - &self, - cursor_location: &TabStopNode, - _: ::Context<'_>, - ) -> std::cmp::Ordering { - Iterator::cmp(self.path.0.iter(), cursor_location.path.0.iter()).then( - ::cmp( - &self.node_insertion_index, - &cursor_location.node_insertion_index, - ), - ) - } - } -} - -#[cfg(test)] -mod tests { - use itertools::Itertools as _; - - use crate::{FocusHandle, FocusId, FocusMap, TabStopMap}; - use std::sync::Arc; - - #[test] - fn test_tab_handles() { - let focus_map = Arc::new(FocusMap::default()); - let mut tab_index_map = TabStopMap::default(); - - let focus_handles = [ - FocusHandle::new(&focus_map).tab_stop(true).tab_index(0), - FocusHandle::new(&focus_map).tab_stop(true).tab_index(1), - FocusHandle::new(&focus_map).tab_stop(true).tab_index(1), - FocusHandle::new(&focus_map), - FocusHandle::new(&focus_map).tab_index(2), - FocusHandle::new(&focus_map).tab_stop(true).tab_index(0), - FocusHandle::new(&focus_map).tab_stop(true).tab_index(2), - ]; - - for handle in focus_handles.iter() { - tab_index_map.insert(handle); - } - let expected = [ - focus_handles[0].clone(), - focus_handles[5].clone(), - focus_handles[1].clone(), - focus_handles[2].clone(), - focus_handles[6].clone(), - ]; - - let mut prev = None; - let mut found = vec![]; - for _ in 0..expected.len() { - let handle = tab_index_map.next(prev.as_ref()).unwrap(); - prev = Some(handle.id); - found.push(handle.id); - } - - assert_eq!( - found, - expected.iter().map(|handle| handle.id).collect::>() - ); - - // Select first tab index if no handle is currently focused. - assert_eq!(tab_index_map.next(None), Some(expected[0].clone())); - // Select last tab index if no handle is currently focused. - assert_eq!(tab_index_map.prev(None), expected.last().cloned(),); - - assert_eq!( - tab_index_map.next(Some(&expected[0].id)), - Some(expected[1].clone()) - ); - assert_eq!( - tab_index_map.next(Some(&expected[1].id)), - Some(expected[2].clone()) - ); - assert_eq!( - tab_index_map.next(Some(&expected[2].id)), - Some(expected[3].clone()) - ); - assert_eq!( - tab_index_map.next(Some(&expected[3].id)), - Some(expected[4].clone()) - ); - assert_eq!( - tab_index_map.next(Some(&expected[4].id)), - Some(expected[0].clone()) - ); - - // prev - assert_eq!(tab_index_map.prev(None), Some(expected[4].clone())); - assert_eq!( - tab_index_map.prev(Some(&expected[0].id)), - Some(expected[4].clone()) - ); - assert_eq!( - tab_index_map.prev(Some(&expected[1].id)), - Some(expected[0].clone()) - ); - assert_eq!( - tab_index_map.prev(Some(&expected[2].id)), - Some(expected[1].clone()) - ); - assert_eq!( - tab_index_map.prev(Some(&expected[3].id)), - Some(expected[2].clone()) - ); - assert_eq!( - tab_index_map.prev(Some(&expected[4].id)), - Some(expected[3].clone()) - ); - } - - #[test] - fn test_tab_non_stop_filtering() { - let focus_map = Arc::new(FocusMap::default()); - let mut tab_index_map = TabStopMap::default(); - - // Check that we can query next from a non-stop tab - let tab_non_stop_1 = FocusHandle::new(&focus_map).tab_stop(false).tab_index(1); - let tab_stop_2 = FocusHandle::new(&focus_map).tab_stop(true).tab_index(2); - tab_index_map.insert(&tab_non_stop_1); - tab_index_map.insert(&tab_stop_2); - let result = tab_index_map.next(Some(&tab_non_stop_1.id)).unwrap(); - assert_eq!(result.id, tab_stop_2.id); - - // Check that we skip over non-stop tabs - let tab_stop_0 = FocusHandle::new(&focus_map).tab_stop(true).tab_index(0); - let tab_non_stop_0 = FocusHandle::new(&focus_map).tab_stop(false).tab_index(0); - tab_index_map.insert(&tab_stop_0); - tab_index_map.insert(&tab_non_stop_0); - let result = tab_index_map.next(Some(&tab_stop_0.id)).unwrap(); - assert_eq!(result.id, tab_stop_2.id); - } - - #[must_use] - struct TabStopMapTest { - tab_map: TabStopMap, - focus_map: Arc, - expected: Vec<(usize, FocusId)>, - } - - impl TabStopMapTest { - #[must_use] - fn new() -> Self { - Self { - tab_map: TabStopMap::default(), - focus_map: Arc::new(FocusMap::default()), - expected: Vec::default(), - } - } - - #[must_use] - fn tab_non_stop(mut self, index: isize) -> Self { - let handle = FocusHandle::new(&self.focus_map) - .tab_stop(false) - .tab_index(index); - self.tab_map.insert(&handle); - self - } - - #[must_use] - fn tab_stop(mut self, index: isize, expected: usize) -> Self { - let handle = FocusHandle::new(&self.focus_map) - .tab_stop(true) - .tab_index(index); - self.tab_map.insert(&handle); - self.expected.push((expected, handle.id)); - self.expected.sort_by_key(|(expected, _)| *expected); - self - } - - #[must_use] - fn tab_group(mut self, tab_index: isize, children: impl FnOnce(Self) -> Self) -> Self { - self.tab_map.begin_group(tab_index); - self = children(self); - self.tab_map.end_group(); - self - } - - fn traverse_tab_map( - &self, - traverse: impl Fn(&TabStopMap, Option<&FocusId>) -> Option, - ) -> Vec { - let mut last_focus_id = None; - let mut found = vec![]; - for _ in 0..self.expected.len() { - let handle = traverse(&self.tab_map, last_focus_id.as_ref()).unwrap(); - last_focus_id = Some(handle.id); - found.push(handle.id); - } - found - } - - fn assert(self) { - let mut expected = self.expected.iter().map(|(_, id)| *id).collect_vec(); - - // Check next order - let forward_found = self.traverse_tab_map(|tab_map, prev| tab_map.next(prev)); - assert_eq!(forward_found, expected); - - // Test overflow. Last to first - assert_eq!( - self.tab_map - .next(forward_found.last()) - .map(|handle| handle.id), - expected.first().cloned() - ); - - // Check previous order - let reversed_found = self.traverse_tab_map(|tab_map, prev| tab_map.prev(prev)); - expected.reverse(); - assert_eq!(reversed_found, expected); - - // Test overflow. First to last - assert_eq!( - self.tab_map - .prev(reversed_found.last()) - .map(|handle| handle.id), - expected.first().cloned(), - ); - } - } - - #[test] - fn test_with_disabled_tab_stop() { - TabStopMapTest::new() - .tab_stop(0, 0) - .tab_non_stop(1) - .tab_stop(2, 1) - .tab_stop(3, 2) - .assert(); - } - - #[test] - fn test_with_multiple_disabled_tab_stops() { - TabStopMapTest::new() - .tab_non_stop(0) - .tab_stop(1, 0) - .tab_non_stop(3) - .tab_stop(3, 1) - .tab_non_stop(4) - .assert(); - } - - #[test] - fn test_tab_group_functionality() { - TabStopMapTest::new() - .tab_stop(0, 0) - .tab_stop(0, 1) - .tab_group(2, |t| t.tab_stop(0, 2).tab_stop(1, 3)) - .tab_stop(3, 4) - .tab_stop(4, 5) - .assert() - } - - #[test] - fn test_sibling_groups() { - TabStopMapTest::new() - .tab_stop(0, 0) - .tab_stop(1, 1) - .tab_group(2, |test| test.tab_stop(0, 2).tab_stop(1, 3)) - .tab_stop(3, 4) - .tab_stop(4, 5) - .tab_group(6, |test| test.tab_stop(0, 6).tab_stop(1, 7)) - .tab_stop(7, 8) - .tab_stop(8, 9) - .assert(); - } - - #[test] - fn test_nested_group() { - TabStopMapTest::new() - .tab_stop(0, 0) - .tab_stop(1, 1) - .tab_group(2, |t| { - t.tab_group(0, |t| t.tab_stop(0, 2).tab_stop(1, 3)) - .tab_stop(1, 4) - }) - .tab_stop(3, 5) - .tab_stop(4, 6) - .assert(); - } - - #[test] - fn test_sibling_nested_groups() { - TabStopMapTest::new() - .tab_stop(0, 0) - .tab_stop(1, 1) - .tab_group(2, |builder| { - builder - .tab_stop(0, 2) - .tab_stop(2, 5) - .tab_group(1, |builder| builder.tab_stop(0, 3).tab_stop(1, 4)) - .tab_group(3, |builder| builder.tab_stop(0, 6).tab_stop(1, 7)) - }) - .tab_stop(3, 8) - .tab_stop(4, 9) - .assert(); - } - - #[test] - fn test_sibling_nested_groups_out_of_order() { - TabStopMapTest::new() - .tab_stop(9, 9) - .tab_stop(8, 8) - .tab_group(7, |builder| { - builder - .tab_stop(0, 2) - .tab_stop(2, 5) - .tab_group(3, |builder| builder.tab_stop(1, 7).tab_stop(0, 6)) - .tab_group(1, |builder| builder.tab_stop(0, 3).tab_stop(1, 4)) - }) - .tab_stop(3, 0) - .tab_stop(4, 1) - .assert(); - } -} diff --git a/crates/gpui_pre/src/taffy.rs b/crates/gpui_pre/src/taffy.rs deleted file mode 100644 index b9e583a8..00000000 --- a/crates/gpui_pre/src/taffy.rs +++ /dev/null @@ -1,776 +0,0 @@ -use crate::{ - AbsoluteLength, App, Bounds, DefiniteLength, Edges, GridTemplate, Length, Pixels, Point, Size, - Style, Window, size, - util::{ - ceil_to_device_pixel, round_half_toward_zero, round_stroke_to_device_pixel, - round_to_device_pixel, - }, -}; -use collections::{FxHashMap, FxHashSet}; -use std::{fmt::Debug, ops::Range}; -use taffy::{ - TaffyTree, TraversePartialTree as _, - geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize}, - prelude::{max_content, min_content}, - style::AvailableSpace as TaffyAvailableSpace, - tree::NodeId, -}; - -#[cfg(feature = "stacker")] -type StackSafe = stacksafe::StackSafe; -#[cfg(not(feature = "stacker"))] -type StackSafe = T; - -type MeasureFn = - dyn FnMut(Size>, Size, &mut Window, &mut App) -> Size; -type NodeMeasureFn = StackSafe>; - -struct NodeContext { - measure: NodeMeasureFn, -} -pub struct TaffyLayoutEngine { - taffy: TaffyTree, - absolute_layout_bounds: FxHashMap>, - /// Unrounded absolute border-box top-left per-node coordinate in device pixels. - absolute_outer_origins: FxHashMap>, - computed_layouts: FxHashSet, - layout_bounds_scratch_space: Vec, -} - -const EXPECT_MESSAGE: &str = "we should avoid taffy layout errors by construction if possible"; - -impl TaffyLayoutEngine { - pub fn new() -> Self { - let mut taffy = TaffyTree::new(); - taffy.disable_rounding(); - TaffyLayoutEngine { - taffy, - absolute_layout_bounds: FxHashMap::default(), - absolute_outer_origins: FxHashMap::default(), - computed_layouts: FxHashSet::default(), - layout_bounds_scratch_space: Vec::new(), - } - } - - pub fn clear(&mut self) { - self.taffy.clear(); - self.absolute_layout_bounds.clear(); - self.absolute_outer_origins.clear(); - self.computed_layouts.clear(); - } - - pub fn request_layout( - &mut self, - style: Style, - rem_size: Pixels, - scale_factor: f32, - children: &[LayoutId], - ) -> LayoutId { - let taffy_style = style.to_taffy(rem_size, scale_factor); - - if children.is_empty() { - self.taffy - .new_leaf(taffy_style) - .expect(EXPECT_MESSAGE) - .into() - } else { - self.taffy - // This is safe because LayoutId is repr(transparent) to taffy::tree::NodeId. - .new_with_children(taffy_style, LayoutId::to_taffy_slice(children)) - .expect(EXPECT_MESSAGE) - .into() - } - } - - pub fn request_measured_layout( - &mut self, - style: Style, - rem_size: Pixels, - scale_factor: f32, - measure: impl FnMut( - Size>, - Size, - &mut Window, - &mut App, - ) -> Size - + 'static, - ) -> LayoutId { - let taffy_style = style.to_taffy(rem_size, scale_factor); - let measure = Box::new(measure) as Box; - #[cfg(feature = "stacker")] - let measure = StackSafe::new(measure); - - self.taffy - .new_leaf_with_context(taffy_style, NodeContext { measure }) - .expect(EXPECT_MESSAGE) - .into() - } - - /// Treats any `auto` dimension of the given node's style as filling `size`. - /// - /// This is applied to window roots before layout so they behave like the - /// root element on the web, which stretches to fill the initial containing - /// block (the viewport) unless given an explicit size. Explicitly styled - /// dimensions are preserved. - pub fn stretch_auto_size_to_fill( - &mut self, - id: LayoutId, - size: Size, - scale_factor: f32, - ) { - let style = self.taffy.style(id.0).expect(EXPECT_MESSAGE); - let stretch_width = style.size.width.is_auto(); - let stretch_height = style.size.height.is_auto(); - if !stretch_width && !stretch_height { - return; - } - let mut style = style.clone(); - if stretch_width { - style.size.width = - taffy::style::Dimension::length(round_to_device_pixel(size.width.0, scale_factor)); - } - if stretch_height { - style.size.height = - taffy::style::Dimension::length(round_to_device_pixel(size.height.0, scale_factor)); - } - self.taffy.set_style(id.0, style).expect(EXPECT_MESSAGE); - } - - // Used to understand performance - #[allow(dead_code)] - fn count_all_children(&self, parent: LayoutId) -> anyhow::Result { - let mut count = 0; - - for child in self.taffy.children(parent.0)? { - // Count this child. - count += 1; - - // Count all of this child's children. - count += self.count_all_children(LayoutId(child))? - } - - Ok(count) - } - - // Used to understand performance - #[allow(dead_code)] - fn max_depth(&self, depth: u32, parent: LayoutId) -> anyhow::Result { - println!( - "{parent:?} at depth {depth} has {} children", - self.taffy.child_count(parent.0) - ); - - let mut max_child_depth = 0; - - for child in self.taffy.children(parent.0)? { - max_child_depth = std::cmp::max(max_child_depth, self.max_depth(0, LayoutId(child))?); - } - - Ok(depth + 1 + max_child_depth) - } - - // Used to understand performance - #[allow(dead_code)] - fn get_edges(&self, parent: LayoutId) -> anyhow::Result> { - let mut edges = Vec::new(); - - for child in self.taffy.children(parent.0)? { - edges.push((parent, LayoutId(child))); - - edges.extend(self.get_edges(LayoutId(child))?); - } - - Ok(edges) - } - - #[cfg_attr(feature = "stacker", stacksafe::stacksafe)] - pub fn compute_layout( - &mut self, - id: LayoutId, - available_space: Size, - window: &mut Window, - cx: &mut App, - ) { - // Leaving this here until we have a better instrumentation approach. - // println!("Laying out {} children", self.count_all_children(id)?); - // println!("Max layout depth: {}", self.max_depth(0, id)?); - - // Output the edges (branches) of the tree in Mermaid format for visualization. - // println!("Edges:"); - // for (a, b) in self.get_edges(id)? { - // println!("N{} --> N{}", u64::from(a), u64::from(b)); - // } - // - - if !self.computed_layouts.insert(id) { - let stack = &mut self.layout_bounds_scratch_space; - stack.push(id); - while let Some(id) = stack.pop() { - self.absolute_layout_bounds.remove(&id); - self.absolute_outer_origins.remove(&id); - stack.extend( - self.taffy - .children(id.into()) - .expect(EXPECT_MESSAGE) - .into_iter() - .map(LayoutId::from), - ); - } - } - - let scale_factor = window.scale_factor(); - - let transform = |v: AvailableSpace| match v { - AvailableSpace::Definite(pixels) => { - AvailableSpace::Definite(Pixels(pixels.0 * scale_factor)) - } - AvailableSpace::MinContent => AvailableSpace::MinContent, - AvailableSpace::MaxContent => AvailableSpace::MaxContent, - }; - let available_space = size( - transform(available_space.width), - transform(available_space.height), - ); - - self.taffy - .compute_layout_with_measure( - id.into(), - available_space.into(), - |known_dimensions, available_space, _id, node_context, _style| { - let Some(node_context) = node_context else { - return taffy::geometry::Size::default(); - }; - - let known_dimensions = Size { - width: known_dimensions.width.map(|e| Pixels(e / scale_factor)), - height: known_dimensions.height.map(|e| Pixels(e / scale_factor)), - }; - - let available_space: Size = available_space.into(); - let untransform = |ev: AvailableSpace| match ev { - AvailableSpace::Definite(pixels) => { - AvailableSpace::Definite(Pixels(pixels.0 / scale_factor)) - } - AvailableSpace::MinContent => AvailableSpace::MinContent, - AvailableSpace::MaxContent => AvailableSpace::MaxContent, - }; - let available_space = size( - untransform(available_space.width), - untransform(available_space.height), - ); - - let measured_size: Size = - (node_context.measure)(known_dimensions, available_space, window, cx); - snap_measured_size_to_device_pixels(measured_size, scale_factor).into() - }, - ) - .expect(EXPECT_MESSAGE); - } - - // Pixel snapping - // - // Painting primitives at non-integer pixel coordinates produces blurry - // output. Pixel snapping converts layout coordinates into integer - // device-pixel coordinates so painted edges land exactly on physical - // pixel boundaries. - // - // Non-integer coordinates can arise for several reasons, including: - // - flex distribution, percentages, centering, and text measurement - // can produce fractional element sizes and positions; - // - at fractional scale factors (for example 125% or 150%), integer - // logical-pixel values can map to non-integer device-pixel values. - // - // We pixel-snap by rounding in device-pixel space, after multiplying - // by `scale_factor`, so that snapping targets physical pixels. Bounds - // are divided by `scale_factor` before being returned to GPUI. - // - // Midpoints are rounded toward zero. This is a stylistic choice: a - // 1-logical-pixel line at 150% scale should render as 1 dp rather than - // 2 dp. - // - // Pixel snapping is done in two phases: - // - // 1. Pre-layout metric snapping. Before Taffy computes layout, all - // authored absolute lengths are rounded in `to_taffy`. This - // includes borders, padding, gaps, and explicit sizes. - // Custom-measured leaf nodes have their measured sizes rounded up - // to integer device-pixel lengths. - // - // 2. Post-layout edge snapping. After Taffy resolves the tree, layout - // relationships such as flex shares, grid tracks, percentages, and - // centering can produce new fractional edge positions. Boxes now - // have edges in absolute coordinates, and snapping must decide - // where those edges land on the device-pixel grid. - // - // Ideally, post-layout snapping would satisfy: - // - // - Edge closure. Two raw layout edges at the same absolute position - // should snap to the same pixel column. - // - Translation stability. A component's internal geometry should not - // change when it moves to a new absolute position. - // - // These goals are in tension because rounding is not associative. - // The simple local schemes make different tradeoffs: - // - // - Absolute edge rounding gives each window coordinate one answer, - // so coincident edges always close globally. But a span's snapped - // length is `round(far) - round(near)`, which may change by 1 dp - // as its absolute origin moves. - // - // - Parent-relative edge rounding rounds each child inside its - // parent's coordinate space. This guarantees translation stability, - // but a shared edge reached through different parents can - // accumulate different rounding, causing non-closure between - // cousins. - // - // - Length rounding rounds each width, height, and thickness - // independently and then places boxes from those rounded lengths. - // Sizes stay stable under translation, but neighboring boxes derive - // their shared boundary from different sources, so closure is not - // guaranteed. - // - // We apply absolute edge rounding for each element's outer box in - // post-layout rounding to preserve closure. Border and padding widths - // are not touched by post-layout rounding; they keep their pre-layout - // rounded value so that they remain stable under translation. - // - // This gives both closure and translation stability in the case that - // all local metrics are integer device-pixel lengths. Pre-layout - // rounding covers that in most cases. The exception is metrics - // resolved by layout relationships, such as percentages. Outer box - // edges will still close globally, and painted border widths are still - // snapped independently, but the raw content-box origin can carry a - // 1dp residual into descendants. - - pub fn layout_bounds(&mut self, id: LayoutId, scale_factor: f32) -> Bounds { - if let Some(layout) = self.absolute_layout_bounds.get(&id).cloned() { - return layout; - } - - let layout = self.taffy.layout(id.into()).expect(EXPECT_MESSAGE); - let layout_location = layout.location; - let layout_size = layout.size; - let parent = self.taffy.parent(id.0); - - let absolute_outer_origin = match parent { - Some(parent_id) => { - let parent_id = LayoutId::from(parent_id); - self.layout_bounds(parent_id, scale_factor); - let parent_origin = *self - .absolute_outer_origins - .get(&parent_id) - .expect("parent absolute outer origin should be cached"); - parent_origin + Point::from(layout_location) - } - None => Point::from(layout_location), - }; - self.absolute_outer_origins - .insert(id, absolute_outer_origin); - - let absolute_far = absolute_outer_origin + Point::from(Size::from(layout_size)); - let snapped_bounds = Bounds::from_corners( - absolute_outer_origin.map(round_half_toward_zero), - absolute_far.map(round_half_toward_zero), - ); - - let bounds = (snapped_bounds / scale_factor).map(Pixels); - self.absolute_layout_bounds.insert(id, bounds); - bounds - } -} - -/// A unique identifier for a layout node, generated when requesting a layout from Taffy -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -#[repr(transparent)] -pub struct LayoutId(NodeId); - -impl LayoutId { - fn to_taffy_slice(node_ids: &[Self]) -> &[taffy::NodeId] { - // SAFETY: LayoutId is repr(transparent) to taffy::tree::NodeId. - unsafe { std::mem::transmute::<&[LayoutId], &[taffy::NodeId]>(node_ids) } - } -} - -impl std::hash::Hash for LayoutId { - fn hash(&self, state: &mut H) { - u64::from(self.0).hash(state); - } -} - -impl From for LayoutId { - fn from(node_id: NodeId) -> Self { - Self(node_id) - } -} - -impl From for NodeId { - fn from(layout_id: LayoutId) -> NodeId { - layout_id.0 - } -} - -fn snap_measured_size_to_device_pixels(size: Size, scale_factor: f32) -> Size { - size.map(|d| ceil_to_device_pixel(d.0.max(0.0), scale_factor)) -} - -fn border_widths_to_taffy( - widths: &Edges, - rem_size: Pixels, - scale_factor: f32, -) -> TaffyRect { - let snap = |w: &AbsoluteLength| { - taffy::style::LengthPercentage::length(round_stroke_to_device_pixel( - w.to_pixels(rem_size).0, - scale_factor, - )) - }; - TaffyRect { - top: snap(&widths.top), - right: snap(&widths.right), - bottom: snap(&widths.bottom), - left: snap(&widths.left), - } -} - -trait ToTaffy { - fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> Output; -} - -impl ToTaffy for Style { - fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Style { - use taffy::style_helpers::{fr, length, minmax, repeat}; - - fn to_grid_line( - placement: &Range, - ) -> taffy::Line { - taffy::Line { - start: placement.start.into(), - end: placement.end.into(), - } - } - - fn to_grid_repeat( - unit: &Option, - ) -> Vec> { - unit.map(|template| { - match template.min_size { - // grid-template-*: repeat(, minmax(0, 1fr)); - crate::GridTemplateMinSize::Zero => { - vec![repeat( - template.repeat, - vec![minmax(length(0.0_f32), fr(1.0_f32))], - )] - } - // grid-template-*: repeat(, minmax(min-content, 1fr)); - crate::GridTemplateMinSize::MinContent => { - vec![repeat( - template.repeat, - vec![minmax(min_content(), fr(1.0_f32))], - )] - } - // grid-template-*: repeat(, minmax(0, max-content)) - crate::GridTemplateMinSize::MaxContent => { - vec![repeat( - template.repeat, - vec![minmax(length(0.0_f32), max_content())], - )] - } - } - }) - .unwrap_or_default() - } - - taffy::style::Style { - display: self.display.into(), - overflow: self.overflow.into(), - scrollbar_width: self.scrollbar_width.to_taffy(rem_size, scale_factor), - position: self.position.into(), - inset: self.inset.to_taffy(rem_size, scale_factor), - size: self.size.to_taffy(rem_size, scale_factor), - min_size: self.min_size.to_taffy(rem_size, scale_factor), - max_size: self.max_size.to_taffy(rem_size, scale_factor), - aspect_ratio: self.aspect_ratio, - margin: self.margin.to_taffy(rem_size, scale_factor), - padding: self.padding.to_taffy(rem_size, scale_factor), - border: border_widths_to_taffy(&self.border_widths, rem_size, scale_factor), - align_items: self.align_items.map(|x| x.into()), - align_self: self.align_self.map(|x| x.into()), - align_content: self.align_content.map(|x| x.into()), - justify_content: self.justify_content.map(|x| x.into()), - gap: self.gap.to_taffy(rem_size, scale_factor), - flex_direction: self.flex_direction.into(), - flex_wrap: self.flex_wrap.into(), - flex_basis: self.flex_basis.to_taffy(rem_size, scale_factor), - flex_grow: self.flex_grow, - flex_shrink: self.flex_shrink, - grid_template_rows: to_grid_repeat(&self.grid_rows), - grid_template_columns: to_grid_repeat(&self.grid_cols), - grid_row: self - .grid_location - .as_ref() - .map(|location| to_grid_line(&location.row)) - .unwrap_or_default(), - grid_column: self - .grid_location - .as_ref() - .map(|location| to_grid_line(&location.column)) - .unwrap_or_default(), - ..Default::default() - } - } -} - -impl ToTaffy for AbsoluteLength { - fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> f32 { - round_to_device_pixel(self.to_pixels(rem_size).0, scale_factor) - } -} - -impl ToTaffy for Length { - fn to_taffy( - &self, - rem_size: Pixels, - scale_factor: f32, - ) -> taffy::prelude::LengthPercentageAuto { - match self { - Length::Definite(length) => length.to_taffy(rem_size, scale_factor), - Length::Auto => taffy::prelude::LengthPercentageAuto::auto(), - } - } -} - -impl ToTaffy for Length { - fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::prelude::Dimension { - match self { - Length::Definite(length) => length.to_taffy(rem_size, scale_factor), - Length::Auto => taffy::prelude::Dimension::auto(), - } - } -} - -impl ToTaffy for DefiniteLength { - fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentage { - match self { - DefiniteLength::Absolute(length) => length.to_taffy(rem_size, scale_factor), - DefiniteLength::Fraction(fraction) => { - taffy::style::LengthPercentage::percent(*fraction) - } - } - } -} - -impl ToTaffy for DefiniteLength { - fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentageAuto { - match self { - DefiniteLength::Absolute(length) => length.to_taffy(rem_size, scale_factor), - DefiniteLength::Fraction(fraction) => { - taffy::style::LengthPercentageAuto::percent(*fraction) - } - } - } -} - -impl ToTaffy for DefiniteLength { - fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Dimension { - match self { - DefiniteLength::Absolute(length) => length.to_taffy(rem_size, scale_factor), - DefiniteLength::Fraction(fraction) => taffy::style::Dimension::percent(*fraction), - } - } -} - -impl ToTaffy for AbsoluteLength { - fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentage { - taffy::style::LengthPercentage::length(self.to_taffy(rem_size, scale_factor)) - } -} - -impl ToTaffy for AbsoluteLength { - fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentageAuto { - taffy::style::LengthPercentageAuto::length(self.to_taffy(rem_size, scale_factor)) - } -} - -impl ToTaffy for AbsoluteLength { - fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Dimension { - taffy::style::Dimension::length(self.to_taffy(rem_size, scale_factor)) - } -} - -impl From> for Point -where - T: Into, - T2: Clone + Debug + Default + PartialEq, -{ - fn from(point: TaffyPoint) -> Point { - Point { - x: point.x.into(), - y: point.y.into(), - } - } -} - -impl From> for TaffyPoint -where - T: Into + Clone + Debug + Default + PartialEq, -{ - fn from(val: Point) -> Self { - TaffyPoint { - x: val.x.into(), - y: val.y.into(), - } - } -} - -impl ToTaffy> for Size -where - T: ToTaffy + Clone + Debug + Default + PartialEq, -{ - fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> TaffySize { - TaffySize { - width: self.width.to_taffy(rem_size, scale_factor), - height: self.height.to_taffy(rem_size, scale_factor), - } - } -} - -impl ToTaffy> for Edges -where - T: ToTaffy + Clone + Debug + Default + PartialEq, -{ - fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> TaffyRect { - TaffyRect { - top: self.top.to_taffy(rem_size, scale_factor), - right: self.right.to_taffy(rem_size, scale_factor), - bottom: self.bottom.to_taffy(rem_size, scale_factor), - left: self.left.to_taffy(rem_size, scale_factor), - } - } -} - -impl From> for Size -where - T: Into, - U: Clone + Debug + Default + PartialEq, -{ - fn from(taffy_size: TaffySize) -> Self { - Size { - width: taffy_size.width.into(), - height: taffy_size.height.into(), - } - } -} - -impl From> for TaffySize -where - T: Into + Clone + Debug + Default + PartialEq, -{ - fn from(size: Size) -> Self { - TaffySize { - width: size.width.into(), - height: size.height.into(), - } - } -} - -/// The space available for an element to be laid out in -#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)] -pub enum AvailableSpace { - /// The amount of space available is the specified number of pixels - Definite(Pixels), - /// The amount of space available is indefinite and the node should be laid out under a min-content constraint - #[default] - MinContent, - /// The amount of space available is indefinite and the node should be laid out under a max-content constraint - MaxContent, -} - -impl AvailableSpace { - /// Returns a `Size` with both width and height set to `AvailableSpace::MinContent`. - /// - /// This function is useful when you want to create a `Size` with the minimum content constraints - /// for both dimensions. - /// - /// # Examples - /// - /// ``` - /// use gpui::AvailableSpace; - /// let min_content_size = AvailableSpace::min_size(); - /// assert_eq!(min_content_size.width, AvailableSpace::MinContent); - /// assert_eq!(min_content_size.height, AvailableSpace::MinContent); - /// ``` - pub const fn min_size() -> Size { - Size { - width: Self::MinContent, - height: Self::MinContent, - } - } -} - -impl From for TaffyAvailableSpace { - fn from(space: AvailableSpace) -> TaffyAvailableSpace { - match space { - AvailableSpace::Definite(Pixels(value)) => TaffyAvailableSpace::Definite(value), - AvailableSpace::MinContent => TaffyAvailableSpace::MinContent, - AvailableSpace::MaxContent => TaffyAvailableSpace::MaxContent, - } - } -} - -impl From for AvailableSpace { - fn from(space: TaffyAvailableSpace) -> AvailableSpace { - match space { - TaffyAvailableSpace::Definite(value) => AvailableSpace::Definite(Pixels(value)), - TaffyAvailableSpace::MinContent => AvailableSpace::MinContent, - TaffyAvailableSpace::MaxContent => AvailableSpace::MaxContent, - } - } -} - -impl From for AvailableSpace { - fn from(pixels: Pixels) -> Self { - AvailableSpace::Definite(pixels) - } -} - -impl From> for Size { - fn from(size: Size) -> Self { - Size { - width: AvailableSpace::Definite(size.width), - height: AvailableSpace::Definite(size.height), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn border_widths_to_taffy_use_stroke_snapping() { - let border_widths = Edges { - top: Pixels(0.0).into(), - right: Pixels(0.4).into(), - bottom: Pixels(0.5).into(), - left: Pixels(1.6).into(), - }; - let taffy_border = border_widths_to_taffy(&border_widths, Pixels(16.0), 1.0); - - assert_eq!( - taffy_border.top, - taffy::style::LengthPercentage::length(0.0) - ); - assert_eq!( - taffy_border.right, - taffy::style::LengthPercentage::length(1.0) - ); - assert_eq!( - taffy_border.bottom, - taffy::style::LengthPercentage::length(1.0) - ); - assert_eq!( - taffy_border.left, - taffy::style::LengthPercentage::length(2.0) - ); - } -} diff --git a/crates/gpui_pre/src/test.rs b/crates/gpui_pre/src/test.rs deleted file mode 100644 index a93424ed..00000000 --- a/crates/gpui_pre/src/test.rs +++ /dev/null @@ -1,220 +0,0 @@ -//! Test support for GPUI. -//! -//! GPUI provides first-class support for testing, which includes a macro to run test that rely on having a context, -//! and a test implementation of the `ForegroundExecutor` and `BackgroundExecutor` which ensure that your tests run -//! deterministically even in the face of arbitrary parallelism. -//! -//! The output of the `gpui::test` macro is understood by other rust test runners, so you can use it with `cargo test` -//! or `cargo-nextest`, or another runner of your choice. -//! -//! To make it possible to test collaborative user interfaces (like Zed) you can ask for as many different contexts -//! as you need. -//! -//! ## Example -//! -//! ``` -//! use gpui; -//! -//! #[gpui::test] -//! async fn test_example(cx: &TestAppContext) { -//! assert!(true) -//! } -//! -//! #[gpui::test] -//! async fn test_collaboration_example(cx_a: &TestAppContext, cx_b: &TestAppContext) { -//! assert!(true) -//! } -//! ``` -use crate::{Entity, Subscription, TestAppContext, TestDispatcher}; -use futures::StreamExt as _; -use proptest::prelude::{Just, Strategy, any}; -use std::{ - env, - panic::{self, RefUnwindSafe, UnwindSafe}, - pin::Pin, -}; - -/// Strategy injected into `#[gpui::property_test]` tests to control the seed -/// given to the scheduler. Doesn't shrink, since all scheduler seeds are -/// equivalent in complexity. If `$SEED` is set, it always uses that value. -/// -/// Note: this function is not intended to be used directly. Rather, it is -/// public so that it can be used from the `property_test` macro. -pub fn seed_strategy() -> impl Strategy { - match std::env::var("SEED") { - Ok(val) => Just(val.parse().unwrap()).boxed(), - Err(_) => any::().no_shrink().boxed(), - } -} - -/// Applies a fixed RNG seed to a proptest config so that case generation -/// is deterministic. Uses `$SEED` if set, otherwise defaults to `0`. -/// This bridges the GPUI `SEED` env var to proptest's RNG seed, so that -/// a single variable controls both the scheduler seed and case generation. -/// -/// Note: this function is not intended to be used directly. Rather, it is -/// public so that it can be used from the `property_test` macro. -pub fn apply_seed_to_proptest_config( - mut config: proptest::test_runner::Config, -) -> proptest::test_runner::Config { - let seed = env::var("SEED") - .ok() - .and_then(|val| val.parse::().ok()) - .unwrap_or(0); - config.rng_seed = proptest::test_runner::RngSeed::Fixed(seed); - config -} - -/// Similar to [`run_test`], but only runs the callback once, allowing -/// [`FnOnce`] callbacks. This is intended for use with the -/// `gpui::property_test` macro and generally should not be used directly. -/// -/// Doesn't support many features of [`run_test`], since these are provided by -/// proptest. -pub fn run_test_once( - seed: u64, - test_fn: Box R>, -) -> R { - let result = panic::catch_unwind(|| { - let dispatcher = TestDispatcher::new(seed); - let scheduler = dispatcher.scheduler().clone(); - let res = test_fn(dispatcher); - scheduler.end_test(); - res - }); - - match result { - Ok(r) => r, - Err(e) => panic::resume_unwind(e), - } -} - -/// Run the given test function with the configured parameters. -/// This is intended for use with the `gpui::test` macro -/// and generally should not be used directly. -pub fn run_test( - num_iterations: usize, - explicit_seeds: &[u64], - max_retries: usize, - test_fn: &mut (dyn RefUnwindSafe + Fn(TestDispatcher, u64)), - on_fail_fn: Option, -) { - let (seeds, is_multiple_runs) = calculate_seeds(num_iterations as u64, explicit_seeds); - - for seed in seeds { - let mut attempt = 0; - loop { - if is_multiple_runs { - eprintln!("seed = {seed}"); - } - let result = panic::catch_unwind(|| { - let dispatcher = TestDispatcher::new(seed); - let scheduler = dispatcher.scheduler().clone(); - test_fn(dispatcher, seed); - scheduler.end_test(); - }); - - match result { - Ok(_) => break, - Err(error) => { - if attempt < max_retries { - println!("attempt {} failed, retrying", attempt); - attempt += 1; - // The panic payload might itself trigger an unwind on drop: - // https://doc.rust-lang.org/std/panic/fn.catch_unwind.html#notes - std::mem::forget(error); - } else { - if is_multiple_runs { - eprintln!("failing seed: {seed}"); - eprintln!( - "You can rerun from this seed by setting the environmental variable SEED to {seed}" - ); - } - if let Some(on_fail_fn) = on_fail_fn { - on_fail_fn() - } - panic::resume_unwind(error); - } - } - } - } - } -} - -fn calculate_seeds( - iterations: u64, - explicit_seeds: &[u64], -) -> (impl Iterator + '_, bool) { - let iterations = env::var("ITERATIONS") - .ok() - .map(|var| var.parse().expect("invalid ITERATIONS variable")) - .unwrap_or(iterations); - - let env_num = env::var("SEED") - .map(|seed| seed.parse().expect("invalid SEED variable as integer")) - .ok(); - - let empty_range = || 0..0; - - let iter = { - let env_range = if let Some(env_num) = env_num { - env_num..env_num + 1 - } else { - empty_range() - }; - - // if `iterations` is 1 and !(`explicit_seeds` is non-empty || `SEED` is set), then add the run `0` - // if `iterations` is 1 and (`explicit_seeds` is non-empty || `SEED` is set), then discard the run `0` - // if `iterations` isn't 1 and `SEED` is set, do `SEED..SEED+iterations` - // otherwise, do `0..iterations` - let iterations_range = match (iterations, env_num) { - (1, None) if explicit_seeds.is_empty() => 0..1, - (1, None) | (1, Some(_)) => empty_range(), - (iterations, Some(env)) => env..env + iterations, - (iterations, None) => 0..iterations, - }; - - // if `SEED` is set, ignore `explicit_seeds` - let explicit_seeds = if env_num.is_some() { - &[] - } else { - explicit_seeds - }; - - env_range - .chain(iterations_range) - .chain(explicit_seeds.iter().copied()) - }; - let is_multiple_runs = iter.clone().nth(1).is_some(); - (iter, is_multiple_runs) -} - -/// A test struct for converting an observation callback into a stream. -pub struct Observation { - rx: Pin>>, - _subscription: Subscription, -} - -impl futures::Stream for Observation { - type Item = T; - - fn poll_next( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - self.rx.poll_next_unpin(cx) - } -} - -/// observe returns a stream of the change events from the given `Entity` -pub fn observe(entity: &Entity, cx: &mut TestAppContext) -> Observation<()> { - let (tx, rx) = async_channel::unbounded(); - let _subscription = cx.update(|cx| { - cx.observe(entity, move |_, _| { - let _ = gpui::block_on(tx.send(())); - }) - }); - let rx = Box::pin(rx); - - Observation { rx, _subscription } -} diff --git a/crates/gpui_pre/src/text_system.rs b/crates/gpui_pre/src/text_system.rs deleted file mode 100644 index 33463893..00000000 --- a/crates/gpui_pre/src/text_system.rs +++ /dev/null @@ -1,1222 +0,0 @@ -mod font_fallbacks; -mod font_features; -mod line; -mod line_layout; -mod line_wrapper; - -pub use font_fallbacks::*; -pub use font_features::*; -pub use line::*; -pub use line_layout::*; -pub use line_wrapper::*; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -use crate::{ - Bounds, DevicePixels, Hsla, Pixels, PlatformTextSystem, Point, Result, SharedString, Size, - StrikethroughStyle, TextRenderingMode, UnderlineStyle, px, -}; -use anyhow::{Context as _, anyhow}; -use collections::FxHashMap; -use core::fmt; -use derive_more::{Add, Deref, FromStr, Sub}; -use itertools::Itertools; -use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; -use smallvec::{SmallVec, smallvec}; -use std::{ - borrow::Cow, - cmp, - fmt::{Debug, Display, Formatter}, - hash::{Hash, Hasher}, - ops::{Deref, DerefMut, Range}, - sync::Arc, -}; - -/// An opaque identifier for a specific font. -#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)] -#[repr(C)] -pub struct FontId(pub usize); - -/// An opaque identifier for a specific font family. -#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)] -pub struct FontFamilyId(pub usize); - -/// Number of subpixel glyph variants along the X axis. -pub const SUBPIXEL_VARIANTS_X: u8 = 4; - -/// Number of subpixel glyph variants along the Y axis. -pub const SUBPIXEL_VARIANTS_Y: u8 = 1; - -/// The GPUI text rendering sub system. -pub struct TextSystem { - platform_text_system: Arc, - font_ids_by_font: RwLock>>, - font_metrics: RwLock>, - raster_bounds: RwLock>>, - wrapper_pool: Mutex>>, - font_runs_pool: Mutex>>, - fallback_font_stack: SmallVec<[Font; 2]>, -} - -impl TextSystem { - /// Create a new TextSystem with the given platform text system. - pub fn new(platform_text_system: Arc) -> Self { - TextSystem { - platform_text_system, - font_metrics: RwLock::default(), - raster_bounds: RwLock::default(), - font_ids_by_font: RwLock::default(), - wrapper_pool: Mutex::default(), - font_runs_pool: Mutex::default(), - fallback_font_stack: smallvec![ - // TODO: Remove this when Linux have implemented setting fallbacks. - font(".ZedMono"), - font(".ZedSans"), - font("Helvetica"), - font("Segoe UI"), // Windows - font("Ubuntu"), // Gnome (Ubuntu) - font("Adwaita Sans"), // Gnome 47 - font("Cantarell"), // Gnome - font("Noto Sans"), // KDE - font("DejaVu Sans"), - font("Arial"), // macOS, Windows - ], - } - } - - /// Get a list of all available font names from the operating system. - pub fn all_font_names(&self) -> Vec { - let mut names = self.platform_text_system.all_font_names(); - names.extend( - self.fallback_font_stack - .iter() - .map(|font| font.family.to_string()), - ); - names.push(".SystemUIFont".to_string()); - names.sort_unstable(); - names.dedup(); - names - } - - /// Add a font's data to the text system. - pub fn add_fonts(&self, fonts: Vec>) -> Result<()> { - self.platform_text_system.add_fonts(fonts) - } - - /// Get the FontId for the configure font family and style. - fn font_id(&self, font: &Font) -> Result { - fn clone_font_id_result(font_id: &Result) -> Result { - match font_id { - Ok(font_id) => Ok(*font_id), - Err(err) => Err(anyhow!("{err}")), - } - } - - let font_id = self - .font_ids_by_font - .read() - .get(font) - .map(clone_font_id_result); - if let Some(font_id) = font_id { - font_id - } else { - let font_id = self.platform_text_system.font_id(font); - self.font_ids_by_font - .write() - .insert(font.clone(), clone_font_id_result(&font_id)); - font_id - } - } - - /// Get the Font for the Font Id. - pub fn get_font_for_id(&self, id: FontId) -> Option { - let lock = self.font_ids_by_font.read(); - lock.iter() - .filter_map(|(font, result)| match result { - Ok(font_id) if *font_id == id => Some(font.clone()), - _ => None, - }) - .next() - } - - /// Resolves the specified font, falling back to the default font stack if - /// the font fails to load. - /// - /// # Panics - /// - /// Panics if the font and none of the fallbacks can be resolved. - pub fn resolve_font(&self, font: &Font) -> FontId { - if let Ok(font_id) = self.font_id(font) { - return font_id; - } - for fallback in &self.fallback_font_stack { - if let Ok(font_id) = self.font_id(fallback) { - return font_id; - } - } - - panic!( - "failed to resolve font '{}' or any of the fallbacks: {}", - font.family, - self.fallback_font_stack - .iter() - .map(|fallback| &fallback.family) - .join(", ") - ); - } - - /// Prewarm any system font caches needed to shape text. - /// - /// This may be expensive, so callers should generally invoke it on a - /// background executor. Missing entries are still populated on demand by - /// the normal shaping path. - pub fn prewarm_fonts(&self, fonts: &[Font]) { - let mut font_ids = SmallVec::<[FontId; 8]>::new(); - for font in fonts { - let font_id = self.resolve_font(font); - if !font_ids.contains(&font_id) { - font_ids.push(font_id); - } - } - self.platform_text_system.prewarm_fonts(&font_ids); - } - - /// Get the bounding box for the given font and font size. - /// A font's bounding box is the smallest rectangle that could enclose all glyphs - /// in the font. superimposed over one another. - pub fn bounding_box(&self, font_id: FontId, font_size: Pixels) -> Bounds { - self.read_metrics(font_id, |metrics| metrics.bounding_box(font_size)) - } - - /// Get the typographic bounds for the given character, in the given font and size. - pub fn typographic_bounds( - &self, - font_id: FontId, - font_size: Pixels, - character: char, - ) -> Result> { - let glyph_id = self - .platform_text_system - .glyph_for_char(font_id, character) - .with_context(|| format!("glyph not found for character '{character}'"))?; - let bounds = self - .platform_text_system - .typographic_bounds(font_id, glyph_id)?; - Ok(self.read_metrics(font_id, |metrics| { - (bounds / metrics.units_per_em as f32 * font_size.0).map(px) - })) - } - - /// Get the advance width for the given character, in the given font and size. - pub fn advance(&self, font_id: FontId, font_size: Pixels, ch: char) -> Result> { - let glyph_id = self - .platform_text_system - .glyph_for_char(font_id, ch) - .with_context(|| format!("glyph not found for character '{ch}'"))?; - let result = self.platform_text_system.advance(font_id, glyph_id)? - / self.units_per_em(font_id) as f32; - - Ok(result * font_size) - } - - // Consider removing this? - /// Returns the shaped layout width of for the given character, in the given font and size. - pub fn layout_width(&self, font_id: FontId, font_size: Pixels, ch: char) -> Pixels { - let mut buffer = [0; 4]; - let buffer = ch.encode_utf8(&mut buffer); - self.platform_text_system - .layout_line( - buffer, - font_size, - &[FontRun { - len: buffer.len(), - font_id, - }], - ) - .width - } - - /// Returns the width of an `em`. - /// - /// Uses the width of the `m` character in the given font and size. - pub fn em_width(&self, font_id: FontId, font_size: Pixels) -> Result { - Ok(self.typographic_bounds(font_id, font_size, 'm')?.size.width) - } - - /// Returns the advance width of an `em`. - /// - /// Uses the advance width of the `m` character in the given font and size. - pub fn em_advance(&self, font_id: FontId, font_size: Pixels) -> Result { - Ok(self.advance(font_id, font_size, 'm')?.width) - } - - /// Returns the width of an `ch`. - /// - /// Uses the width of the `0` character in the given font and size. - pub fn ch_width(&self, font_id: FontId, font_size: Pixels) -> Result { - Ok(self.typographic_bounds(font_id, font_size, '0')?.size.width) - } - - /// Returns the advance width of an `ch`. - /// - /// Uses the advance width of the `0` character in the given font and size. - pub fn ch_advance(&self, font_id: FontId, font_size: Pixels) -> Result { - Ok(self.advance(font_id, font_size, '0')?.width) - } - - /// Get the number of font size units per 'em square', - /// Per MDN: "an abstract square whose height is the intended distance between - /// lines of type in the same type size" - pub fn units_per_em(&self, font_id: FontId) -> u32 { - self.read_metrics(font_id, |metrics| metrics.units_per_em) - } - - /// Get the height of a capital letter in the given font and size. - pub fn cap_height(&self, font_id: FontId, font_size: Pixels) -> Pixels { - self.read_metrics(font_id, |metrics| metrics.cap_height(font_size)) - } - - /// Get the height of the x character in the given font and size. - pub fn x_height(&self, font_id: FontId, font_size: Pixels) -> Pixels { - self.read_metrics(font_id, |metrics| metrics.x_height(font_size)) - } - - /// Get the recommended distance from the baseline for the given font - pub fn ascent(&self, font_id: FontId, font_size: Pixels) -> Pixels { - self.read_metrics(font_id, |metrics| metrics.ascent(font_size)) - } - - /// Get the recommended distance below the baseline for the given font, - /// in single spaced text. - pub fn descent(&self, font_id: FontId, font_size: Pixels) -> Pixels { - self.read_metrics(font_id, |metrics| metrics.descent(font_size)) - } - - /// Get the recommended baseline offset for the given font and line height. - pub fn baseline_offset( - &self, - font_id: FontId, - font_size: Pixels, - line_height: Pixels, - ) -> Pixels { - 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 - } - - fn read_metrics(&self, font_id: FontId, read: impl FnOnce(&FontMetrics) -> T) -> T { - let lock = self.font_metrics.upgradable_read(); - - if let Some(metrics) = lock.get(&font_id) { - read(metrics) - } else { - let mut lock = RwLockUpgradableReadGuard::upgrade(lock); - let metrics = lock - .entry(font_id) - .or_insert_with(|| self.platform_text_system.font_metrics(font_id)); - read(metrics) - } - } - - /// Returns a handle to a line wrapper, for the given font and font size. - pub fn line_wrapper(self: &Arc, font: Font, font_size: Pixels) -> LineWrapperHandle { - let lock = &mut self.wrapper_pool.lock(); - let font_id = self.resolve_font(&font); - let wrappers = lock - .entry(FontIdWithSize { font_id, font_size }) - .or_default(); - let wrapper = wrappers - .pop() - .unwrap_or_else(|| LineWrapper::new(font_id, font_size, self.clone())); - - LineWrapperHandle { - wrapper: Some(wrapper), - text_system: self.clone(), - } - } - - /// Get the rasterized size and location of a specific, rendered glyph. - pub(crate) fn raster_bounds(&self, params: &RenderGlyphParams) -> Result> { - let raster_bounds = self.raster_bounds.upgradable_read(); - if let Some(bounds) = raster_bounds.get(params) { - Ok(*bounds) - } else { - let mut raster_bounds = RwLockUpgradableReadGuard::upgrade(raster_bounds); - let bounds = self.platform_text_system.glyph_raster_bounds(params)?; - raster_bounds.insert(params.clone(), bounds); - Ok(bounds) - } - } - - pub(crate) fn rasterize_glyph( - &self, - params: &RenderGlyphParams, - ) -> Result<(Size, Vec)> { - let raster_bounds = self.raster_bounds(params)?; - self.platform_text_system - .rasterize_glyph(params, raster_bounds) - } - - /// Returns the dilation level to use for a glyph painted in the given color. - pub(crate) fn glyph_dilation_for_color(&self, color: Hsla) -> u8 { - self.platform_text_system.glyph_dilation_for_color(color) - } - - /// Returns the text rendering mode recommended by the platform for the given font and size. - /// The return value will never be [`TextRenderingMode::PlatformDefault`]. - pub(crate) fn recommended_rendering_mode( - &self, - font_id: FontId, - font_size: Pixels, - ) -> TextRenderingMode { - self.platform_text_system - .recommended_rendering_mode(font_id, font_size) - } -} - -/// The GPUI text layout subsystem. -#[derive(Deref)] -pub struct WindowTextSystem { - line_layout_cache: LineLayoutCache, - #[deref] - text_system: Arc, -} - -impl WindowTextSystem { - /// Create a new WindowTextSystem with the given TextSystem. - pub fn new(text_system: Arc) -> Self { - Self { - line_layout_cache: LineLayoutCache::new(text_system.platform_text_system.clone()), - text_system, - } - } - - pub(crate) fn layout_index(&self) -> LineLayoutIndex { - self.line_layout_cache.layout_index() - } - - pub(crate) fn reuse_layouts(&self, index: Range) { - self.line_layout_cache.reuse_layouts(index) - } - - pub(crate) fn truncate_layouts(&self, index: LineLayoutIndex) { - self.line_layout_cache.truncate_layouts(index) - } - - /// Shape the given line, at the given font_size, for painting to the screen. - /// Subsets of the line can be styled independently with the `runs` parameter. - /// - /// Note that this method can only shape a single line of text. It will panic - /// if the text contains newlines. If you need to shape multiple lines of text, - /// use [`Self::shape_text`] instead. - pub fn shape_line( - &self, - text: SharedString, - font_size: Pixels, - runs: &[TextRun], - force_width: Option, - ) -> ShapedLine { - debug_assert!( - text.find('\n').is_none(), - "text argument should not contain newlines" - ); - - let mut decoration_runs = SmallVec::<[DecorationRun; 32]>::new(); - for run in runs { - if let Some(last_run) = decoration_runs.last_mut() - && last_run.color == run.color - && last_run.underline == run.underline - && last_run.strikethrough == run.strikethrough - && last_run.background_color == run.background_color - { - last_run.len += run.len as u32; - continue; - } - decoration_runs.push(DecorationRun { - len: run.len as u32, - color: run.color, - background_color: run.background_color, - underline: run.underline, - strikethrough: run.strikethrough, - }); - } - - let layout = self.layout_line(&text, font_size, runs, force_width); - - ShapedLine { - layout, - text, - decoration_runs, - } - } - - /// Shape the given line using a caller-provided content hash as the cache key. - /// - /// This enables cache hits without materializing a contiguous `SharedString` for the text. - /// If the cache misses, `materialize_text` is invoked to produce the `SharedString` for shaping. - /// - /// Contract (caller enforced): - /// - Same `text_hash` implies identical text content (collision risk accepted by caller). - /// - `text_len` should be the UTF-8 byte length of the text (helps reduce accidental collisions). - /// - /// Like [`Self::shape_line`], this must be used only for single-line text (no `\n`). - pub fn shape_line_by_hash( - &self, - text_hash: u64, - text_len: usize, - font_size: Pixels, - runs: &[TextRun], - force_width: Option, - materialize_text: impl FnOnce() -> SharedString, - ) -> ShapedLine { - let mut decoration_runs = SmallVec::<[DecorationRun; 32]>::new(); - for run in runs { - if let Some(last_run) = decoration_runs.last_mut() - && last_run.color == run.color - && last_run.underline == run.underline - && last_run.strikethrough == run.strikethrough - && last_run.background_color == run.background_color - { - last_run.len += run.len as u32; - continue; - } - decoration_runs.push(DecorationRun { - len: run.len as u32, - color: run.color, - background_color: run.background_color, - underline: run.underline, - strikethrough: run.strikethrough, - }); - } - - let mut used_force_width = force_width; - let layout = self.layout_line_by_hash( - text_hash, - text_len, - font_size, - runs, - used_force_width, - || { - let text = materialize_text(); - debug_assert!( - text.find('\n').is_none(), - "text argument should not contain newlines" - ); - text - }, - ); - - // We only materialize actual text on cache miss; on hit we avoid allocations. - // Since `ShapedLine` carries a `SharedString`, use an empty placeholder for hits. - // NOTE: Callers must not rely on `ShapedLine.text` for content when using this API. - let text: SharedString = SharedString::new_static(""); - - ShapedLine { - layout, - text, - decoration_runs, - } - } - - /// Shape a multi line string of text, at the given font_size, for painting to the screen. - /// Subsets of the text can be styled independently with the `runs` parameter. - /// If `wrap_width` is provided, the line breaks will be adjusted to fit within the given width. - pub fn shape_text( - &self, - text: SharedString, - font_size: Pixels, - runs: &[TextRun], - wrap_width: Option, - line_clamp: Option, - ) -> Result> { - let mut runs = runs.iter().filter(|run| run.len > 0).cloned().peekable(); - let mut font_runs = self.font_runs_pool.lock().pop().unwrap_or_default(); - - let mut lines = SmallVec::new(); - let mut max_wrap_lines = line_clamp; - let mut wrapped_lines = 0; - - let mut process_line = |line_text: SharedString, line_start, line_end| { - font_runs.clear(); - - let mut decoration_runs = >::with_capacity(32); - let mut run_start = line_start; - while run_start < line_end { - let Some(run) = runs.peek_mut() else { - log::warn!("`TextRun`s do not cover the entire to be shaped text"); - break; - }; - - let run_len_within_line = cmp::min(line_end - run_start, run.len); - - let decoration_changed = if let Some(last_run) = decoration_runs.last_mut() - && last_run.color == run.color - && last_run.underline == run.underline - && last_run.strikethrough == run.strikethrough - && last_run.background_color == run.background_color - { - last_run.len += run_len_within_line as u32; - false - } else { - decoration_runs.push(DecorationRun { - len: run_len_within_line as u32, - color: run.color, - background_color: run.background_color, - underline: run.underline, - strikethrough: run.strikethrough, - }); - true - }; - - let font_id = self.resolve_font(&run.font); - if let Some(font_run) = font_runs.last_mut() - && font_id == font_run.font_id - && !decoration_changed - { - font_run.len += run_len_within_line; - } else { - font_runs.push(FontRun { - len: run_len_within_line, - font_id, - }); - } - - // Preserve the remainder of the run for the next line - run.len -= run_len_within_line; - if run.len == 0 { - runs.next(); - } - run_start += run_len_within_line; - } - - let layout = self.line_layout_cache.layout_wrapped_line( - &line_text, - font_size, - &font_runs, - wrap_width, - max_wrap_lines.map(|max| max.saturating_sub(wrapped_lines)), - ); - wrapped_lines += layout.wrap_boundaries.len(); - - lines.push(WrappedLine { - layout, - decoration_runs, - text: line_text, - }); - - // Skip `\n` character. - if let Some(run) = runs.peek_mut() { - run.len -= 1; - if run.len == 0 { - runs.next(); - } - } - }; - - let mut split_lines = text.split('\n'); - - // Special case single lines to prevent allocating a sharedstring - if let Some(first_line) = split_lines.next() - && let Some(second_line) = split_lines.next() - { - let mut line_start = 0; - process_line( - SharedString::new(first_line), - line_start, - line_start + first_line.len(), - ); - line_start += first_line.len() + '\n'.len_utf8(); - process_line( - SharedString::new(second_line), - line_start, - line_start + second_line.len(), - ); - for line_text in split_lines { - line_start += line_text.len() + '\n'.len_utf8(); - process_line( - SharedString::new(line_text), - line_start, - line_start + line_text.len(), - ); - } - } else { - let end = text.len(); - process_line(text, 0, end); - } - - self.font_runs_pool.lock().push(font_runs); - - Ok(lines) - } - - pub(crate) fn finish_frame(&self) { - self.line_layout_cache.finish_frame() - } - - /// Layout the given line of text, at the given font_size. - /// Subsets of the line can be styled independently with the `runs` parameter. - /// Generally, you should prefer to use [`Self::shape_line`] instead, which - /// can be painted directly. - pub fn layout_line( - &self, - text: &str, - font_size: Pixels, - runs: &[TextRun], - force_width: Option, - ) -> Arc { - let mut last_run = None::<&TextRun>; - let mut font_runs = self.font_runs_pool.lock().pop().unwrap_or_default(); - font_runs.clear(); - - for run in runs.iter() { - let decoration_changed = if let Some(last_run) = last_run - && last_run.color == run.color - && last_run.underline == run.underline - && last_run.strikethrough == run.strikethrough - // we do not consider differing background color relevant, as it does not affect glyphs - // && last_run.background_color == run.background_color - { - false - } else { - last_run = Some(run); - true - }; - - let font_id = self.resolve_font(&run.font); - if let Some(font_run) = font_runs.last_mut() - && font_id == font_run.font_id - && !decoration_changed - { - font_run.len += run.len; - } else { - font_runs.push(FontRun { - len: run.len, - font_id, - }); - } - } - - let layout = self.line_layout_cache.layout_line( - &SharedString::new(text), - font_size, - &font_runs, - force_width, - ); - - self.font_runs_pool.lock().push(font_runs); - - layout - } - - /// Returns the shaped layout width of for the given character, in the given font and size. - pub fn layout_width(&self, font_id: FontId, font_size: Pixels, ch: char) -> Pixels { - let mut buffer = [0; 4]; - let buffer: &_ = ch.encode_utf8(&mut buffer); - self.line_layout_cache - .layout_line( - buffer, - font_size, - &[FontRun { - len: buffer.len(), - font_id, - }], - None, - ) - .width - } - - /// Returns the shaped layout width of an `em`. - pub fn em_layout_width(&self, font_id: FontId, font_size: Pixels) -> Pixels { - self.layout_width(font_id, font_size, 'm') - } - - /// Probe the line layout cache using a caller-provided content hash, without allocating. - /// - /// Returns `Some(layout)` if the layout is already cached in either the current frame - /// or the previous frame. Returns `None` if it is not cached. - /// - /// Contract (caller enforced): - /// - Same `text_hash` implies identical text content (collision risk accepted by caller). - /// - `text_len` should be the UTF-8 byte length of the text (helps reduce accidental collisions). - pub fn try_layout_line_by_hash( - &self, - text_hash: u64, - text_len: usize, - font_size: Pixels, - runs: &[TextRun], - force_width: Option, - ) -> Option> { - let mut last_run = None::<&TextRun>; - let mut font_runs = self.font_runs_pool.lock().pop().unwrap_or_default(); - font_runs.clear(); - - for run in runs.iter() { - let decoration_changed = if let Some(last_run) = last_run - && last_run.color == run.color - && last_run.underline == run.underline - && last_run.strikethrough == run.strikethrough - // we do not consider differing background color relevant, as it does not affect glyphs - // && last_run.background_color == run.background_color - { - false - } else { - last_run = Some(run); - true - }; - - let font_id = self.resolve_font(&run.font); - if let Some(font_run) = font_runs.last_mut() - && font_id == font_run.font_id - && !decoration_changed - { - font_run.len += run.len; - } else { - font_runs.push(FontRun { - len: run.len, - font_id, - }); - } - } - - let layout = self.line_layout_cache.try_layout_line_by_hash( - text_hash, - text_len, - font_size, - &font_runs, - force_width, - ); - - self.font_runs_pool.lock().push(font_runs); - - layout - } - - /// Layout the given line of text using a caller-provided content hash as the cache key. - /// - /// This enables cache hits without materializing a contiguous `SharedString` for the text. - /// If the cache misses, `materialize_text` is invoked to produce the `SharedString` for shaping. - /// - /// Contract (caller enforced): - /// - Same `text_hash` implies identical text content (collision risk accepted by caller). - /// - `text_len` should be the UTF-8 byte length of the text (helps reduce accidental collisions). - pub fn layout_line_by_hash( - &self, - text_hash: u64, - text_len: usize, - font_size: Pixels, - runs: &[TextRun], - force_width: Option, - materialize_text: impl FnOnce() -> SharedString, - ) -> Arc { - let mut last_run = None::<&TextRun>; - let mut font_runs = self.font_runs_pool.lock().pop().unwrap_or_default(); - font_runs.clear(); - - for run in runs.iter() { - let decoration_changed = if let Some(last_run) = last_run - && last_run.color == run.color - && last_run.underline == run.underline - && last_run.strikethrough == run.strikethrough - // we do not consider differing background color relevant, as it does not affect glyphs - // && last_run.background_color == run.background_color - { - false - } else { - last_run = Some(run); - true - }; - - let font_id = self.resolve_font(&run.font); - if let Some(font_run) = font_runs.last_mut() - && font_id == font_run.font_id - && !decoration_changed - { - font_run.len += run.len; - } else { - font_runs.push(FontRun { - len: run.len, - font_id, - }); - } - } - - let layout = self.line_layout_cache.layout_line_by_hash( - text_hash, - text_len, - font_size, - &font_runs, - force_width, - materialize_text, - ); - - self.font_runs_pool.lock().push(font_runs); - - layout - } -} - -#[derive(Hash, Eq, PartialEq)] -struct FontIdWithSize { - font_id: FontId, - font_size: Pixels, -} - -/// A handle into the text system, which can be used to compute the wrapped layout of text -pub struct LineWrapperHandle { - wrapper: Option, - text_system: Arc, -} - -impl Drop for LineWrapperHandle { - fn drop(&mut self) { - let mut state = self.text_system.wrapper_pool.lock(); - let wrapper = self.wrapper.take().unwrap(); - state - .get_mut(&FontIdWithSize { - font_id: wrapper.font_id, - font_size: 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() - } -} - -/// The degree of blackness or stroke thickness of a font. This value ranges from 100.0 to 900.0, -/// with 400.0 as normal. -#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Serialize, Deserialize, Add, Sub, FromStr)] -#[serde(transparent)] -pub struct FontWeight(pub f32); - -impl Display for FontWeight { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl From for FontWeight { - fn from(weight: f32) -> Self { - FontWeight(weight) - } -} - -impl Default for FontWeight { - #[inline] - fn default() -> FontWeight { - FontWeight::NORMAL - } -} - -impl Hash for FontWeight { - fn hash(&self, state: &mut H) { - state.write_u32(u32::from_be_bytes(self.0.to_be_bytes())); - } -} - -impl Eq for FontWeight {} - -impl FontWeight { - /// Thin weight (100), the thinnest value. - pub const THIN: FontWeight = FontWeight(100.0); - /// Extra light weight (200). - pub const EXTRA_LIGHT: FontWeight = FontWeight(200.0); - /// Light weight (300). - pub const LIGHT: FontWeight = FontWeight(300.0); - /// Normal (400). - pub const NORMAL: FontWeight = FontWeight(400.0); - /// Medium weight (500, higher than normal). - pub const MEDIUM: FontWeight = FontWeight(500.0); - /// Semibold weight (600). - pub const SEMIBOLD: FontWeight = FontWeight(600.0); - /// Bold weight (700). - pub const BOLD: FontWeight = FontWeight(700.0); - /// Extra-bold weight (800). - pub const EXTRA_BOLD: FontWeight = FontWeight(800.0); - /// Black weight (900), the thickest value. - pub const BLACK: FontWeight = FontWeight(900.0); - - /// All of the font weights, in order from thinnest to thickest. - pub const ALL: [FontWeight; 9] = [ - Self::THIN, - Self::EXTRA_LIGHT, - Self::LIGHT, - Self::NORMAL, - Self::MEDIUM, - Self::SEMIBOLD, - Self::BOLD, - Self::EXTRA_BOLD, - Self::BLACK, - ]; -} - -impl schemars::JsonSchema for FontWeight { - fn schema_name() -> std::borrow::Cow<'static, str> { - "FontWeight".into() - } - - fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { - use schemars::json_schema; - json_schema!({ - "type": "number", - "minimum": Self::THIN, - "maximum": Self::BLACK, - "default": Self::default(), - "description": "Font weight value between 100 (thin) and 900 (black)" - }) - } -} - -/// Allows italic or oblique faces to be selected. -#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash, Default, Serialize, Deserialize, JsonSchema)] -pub enum FontStyle { - /// A face that is neither italic not obliqued. - #[default] - Normal, - /// A form that is generally cursive in nature. - Italic, - /// A typically-sloped version of the regular face. - Oblique, -} - -impl Display for FontStyle { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Debug::fmt(self, f) - } -} - -/// A styled run of text, for use in [`crate::TextLayout`]. -#[derive(Clone, Debug, PartialEq, Eq, Default)] -pub struct TextRun { - /// A number of utf8 bytes - pub len: usize, - /// The font to use for this run. - pub font: Font, - /// The color - pub color: Hsla, - /// The background color (if any) - pub background_color: Option, - /// The underline style (if any) - pub underline: Option, - /// The strikethrough style (if any) - pub strikethrough: Option, -} - -#[cfg(all(target_os = "macos", test))] -impl TextRun { - fn with_len(&self, len: usize) -> Self { - let mut this = self.clone(); - this.len = len; - this - } -} - -/// An identifier for a specific glyph, as returned by [`WindowTextSystem::layout_line`]. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -#[repr(C)] -pub struct GlyphId(pub u32); - -/// Parameters for rendering a glyph, used as cache keys for raster bounds. -/// -/// This struct identifies a specific glyph rendering configuration including -/// font, size, subpixel positioning, and scale factor. It's used to look up -/// cached raster bounds and sprite atlas entries. -#[derive(Clone, Debug, PartialEq)] -#[expect(missing_docs)] -pub struct RenderGlyphParams { - pub font_id: FontId, - pub glyph_id: GlyphId, - pub font_size: Pixels, - pub subpixel_variant: Point, - pub scale_factor: f32, - pub is_emoji: bool, - pub subpixel_rendering: bool, - pub dilation: u8, -} - -impl Eq for RenderGlyphParams {} - -impl Hash for RenderGlyphParams { - fn hash(&self, state: &mut H) { - self.font_id.0.hash(state); - self.glyph_id.0.hash(state); - self.font_size.0.to_bits().hash(state); - self.subpixel_variant.hash(state); - self.scale_factor.to_bits().hash(state); - self.is_emoji.hash(state); - self.subpixel_rendering.hash(state); - self.dilation.hash(state); - } -} - -/// The configuration details for identifying a specific font. -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub struct Font { - /// The font family name. - /// - /// The special name ".SystemUIFont" is used to identify the system UI font, which varies based on platform. - pub family: SharedString, - - /// The font features to use. - pub features: FontFeatures, - - /// The fallbacks fonts to use. - pub fallbacks: Option, - - /// The font weight. - pub weight: FontWeight, - - /// The font style. - pub style: FontStyle, -} - -impl Default for Font { - fn default() -> Self { - font(".SystemUIFont") - } -} - -/// Get a [`Font`] for a given name. -pub fn font(family: impl Into) -> Font { - Font { - family: family.into(), - features: FontFeatures::default(), - weight: FontWeight::default(), - style: FontStyle::default(), - fallbacks: None, - } -} - -impl Font { - /// Set this Font to be bold - pub fn bold(mut self) -> Self { - self.weight = FontWeight::BOLD; - self - } - - /// Set this Font to be italic - pub fn italic(mut self) -> Self { - self.style = FontStyle::Italic; - self - } -} - -/// A struct for storing font metrics. -/// It is used to define the measurements of a typeface. -#[derive(Clone, Copy, Debug)] -pub struct FontMetrics { - /// The number of font units that make up the "em square", - /// a scalable grid for determining the size of a typeface. - pub units_per_em: u32, - - /// The vertical distance from the baseline of the font to the top of the glyph covers. - pub ascent: f32, - - /// The vertical distance from the baseline of the font to the bottom of the glyph covers. - pub descent: f32, - - /// The recommended additional space to add between lines of type. - pub line_gap: f32, - - /// The suggested position of the underline. - pub underline_position: f32, - - /// The suggested thickness of the underline. - pub underline_thickness: f32, - - /// The height of a capital letter measured from the baseline of the font. - pub cap_height: f32, - - /// The height of a lowercase x. - pub x_height: f32, - - /// The outer limits of the area that the font covers. - /// Corresponds to the xMin / xMax / yMin / yMax values in the OpenType `head` table - pub bounding_box: Bounds, -} - -impl FontMetrics { - /// Returns the vertical distance from the baseline of the font to the top of the glyph covers in pixels. - pub fn ascent(&self, font_size: Pixels) -> Pixels { - Pixels((self.ascent / self.units_per_em as f32) * font_size.0) - } - - /// Returns the vertical distance from the baseline of the font to the bottom of the glyph covers in pixels. - pub fn descent(&self, font_size: Pixels) -> Pixels { - Pixels((self.descent / self.units_per_em as f32) * font_size.0) - } - - /// Returns the recommended additional space to add between lines of type in pixels. - pub fn line_gap(&self, font_size: Pixels) -> Pixels { - Pixels((self.line_gap / self.units_per_em as f32) * font_size.0) - } - - /// Returns the suggested position of the underline in pixels. - pub fn underline_position(&self, font_size: Pixels) -> Pixels { - Pixels((self.underline_position / self.units_per_em as f32) * font_size.0) - } - - /// Returns the suggested thickness of the underline in pixels. - pub fn underline_thickness(&self, font_size: Pixels) -> Pixels { - Pixels((self.underline_thickness / self.units_per_em as f32) * font_size.0) - } - - /// Returns the height of a capital letter measured from the baseline of the font in pixels. - pub fn cap_height(&self, font_size: Pixels) -> Pixels { - Pixels((self.cap_height / self.units_per_em as f32) * font_size.0) - } - - /// Returns the height of a lowercase x in pixels. - pub fn x_height(&self, font_size: Pixels) -> Pixels { - Pixels((self.x_height / self.units_per_em as f32) * font_size.0) - } - - /// Returns the outer limits of the area that the font covers in pixels. - pub fn bounding_box(&self, font_size: Pixels) -> Bounds { - (self.bounding_box / self.units_per_em as f32 * font_size.0).map(px) - } -} - -/// Maps well-known virtual font names to their concrete equivalents. -#[allow(unused)] -pub fn font_name_with_fallbacks<'a>(name: &'a str, system: &'a str) -> &'a str { - // Note: the "Zed Plex" fonts were deprecated as we are not allowed to use "Plex" - // in a derived font name. They are essentially indistinguishable from IBM Plex/Lilex, - // and so retained here for backward compatibility. - match name { - ".SystemUIFont" => system, - ".ZedSans" | "Zed Plex Sans" => "IBM Plex Sans", - ".ZedMono" | "Zed Plex Mono" => "Lilex", - _ => name, - } -} - -/// Like [`font_name_with_fallbacks`] but accepts and returns [`SharedString`] references. -#[allow(unused)] -pub fn font_name_with_fallbacks_shared<'a>( - name: &'a SharedString, - system: &'a SharedString, -) -> &'a SharedString { - // Note: the "Zed Plex" fonts were deprecated as we are not allowed to use "Plex" - // in a derived font name. They are essentially indistinguishable from IBM Plex/Lilex, - // and so retained here for backward compatibility. - match name.as_str() { - ".SystemUIFont" => system, - ".ZedSans" | "Zed Plex Sans" => const { &SharedString::new_static("IBM Plex Sans") }, - ".ZedMono" | "Zed Plex Mono" => const { &SharedString::new_static("Lilex") }, - _ => name, - } -} diff --git a/crates/gpui_pre/src/text_system/font_fallbacks.rs b/crates/gpui_pre/src/text_system/font_fallbacks.rs deleted file mode 100644 index 63dc89ba..00000000 --- a/crates/gpui_pre/src/text_system/font_fallbacks.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::sync::Arc; - -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -/// The fallback fonts that can be configured for a given font. -/// Fallback fonts family names are stored here. -#[derive(Default, Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize, JsonSchema)] -pub struct FontFallbacks(pub Arc>); - -impl FontFallbacks { - /// Get the fallback fonts family names - pub fn fallback_list(&self) -> &[String] { - self.0.as_slice() - } - - /// Create a font fallback from a list of strings - pub fn from_fonts(fonts: Vec) -> Self { - FontFallbacks(Arc::new(fonts)) - } -} diff --git a/crates/gpui_pre/src/text_system/font_features.rs b/crates/gpui_pre/src/text_system/font_features.rs deleted file mode 100644 index c1ab72b4..00000000 --- a/crates/gpui_pre/src/text_system/font_features.rs +++ /dev/null @@ -1,154 +0,0 @@ -use std::borrow::Cow; -use std::sync::Arc; - -use schemars::{JsonSchema, json_schema}; - -/// The OpenType features that can be configured for a given font. -#[derive(Default, Clone, Eq, PartialEq, Hash)] -pub struct FontFeatures(pub Arc>); - -impl FontFeatures { - /// Disables `calt`. - pub fn disable_ligatures() -> Self { - Self(Arc::new(vec![("calt".into(), 0)])) - } - - /// Get the tag name list of the font OpenType features - /// only enabled or disabled features are returned - pub fn tag_value_list(&self) -> &[(String, u32)] { - self.0.as_slice() - } - - /// Returns whether the `calt` feature is enabled. - /// - /// Returns `None` if the feature is not present. - pub fn is_calt_enabled(&self) -> Option { - self.0 - .iter() - .find(|(feature, _)| feature == "calt") - .map(|(_, value)| *value == 1) - } -} - -impl std::fmt::Debug for FontFeatures { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut debug = f.debug_struct("FontFeatures"); - for (tag, value) in self.tag_value_list() { - debug.field(tag, value); - } - - debug.finish() - } -} - -#[derive(Debug, serde::Serialize, serde::Deserialize)] -#[serde(untagged)] -enum FeatureValue { - Bool(bool), - Number(serde_json::Number), -} - -impl<'de> serde::Deserialize<'de> for FontFeatures { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - use serde::de::{MapAccess, Visitor}; - use std::fmt; - - struct FontFeaturesVisitor; - - impl<'de> Visitor<'de> for FontFeaturesVisitor { - type Value = FontFeatures; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a map of font features") - } - - fn visit_map(self, mut access: M) -> Result - where - M: MapAccess<'de>, - { - let mut feature_list = Vec::new(); - - while let Some((key, value)) = - access.next_entry::>()? - { - if !is_valid_feature_tag(&key) { - log::error!("Incorrect font feature tag: {}", key); - continue; - } - if let Some(value) = value { - match value { - FeatureValue::Bool(enable) => { - if enable { - feature_list.push((key, 1)); - } else { - feature_list.push((key, 0)); - } - } - FeatureValue::Number(value) => { - if value.is_u64() { - feature_list.push((key, value.as_u64().unwrap() as u32)); - } else { - log::error!( - "Incorrect font feature value {} for feature tag {}", - value, - key - ); - continue; - } - } - } - } - } - - Ok(FontFeatures(Arc::new(feature_list))) - } - } - - let features = deserializer.deserialize_map(FontFeaturesVisitor)?; - Ok(features) - } -} - -impl serde::Serialize for FontFeatures { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - use serde::ser::SerializeMap; - - let mut map = serializer.serialize_map(None)?; - - for (tag, value) in self.tag_value_list() { - map.serialize_entry(tag, value)?; - } - - map.end() - } -} - -impl JsonSchema for FontFeatures { - fn schema_name() -> Cow<'static, str> { - "FontFeatures".into() - } - - fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { - json_schema!({ - "type": "object", - "patternProperties": { - "[0-9a-zA-Z]{4}$": { - "type": ["boolean", "integer"], - "minimum": 0, - "multipleOf": 1 - } - }, - "additionalProperties": false - }) - } -} - -fn is_valid_feature_tag(tag: &str) -> bool { - tag.len() == 4 && tag.chars().all(|c| c.is_ascii_alphanumeric()) -} diff --git a/crates/gpui_pre/src/text_system/line.rs b/crates/gpui_pre/src/text_system/line.rs deleted file mode 100644 index 15101e6a..00000000 --- a/crates/gpui_pre/src/text_system/line.rs +++ /dev/null @@ -1,1025 +0,0 @@ -use crate::{ - App, Bounds, DevicePixels, Half, Hsla, LineLayout, Pixels, Point, RenderGlyphParams, Result, - SharedString, StrikethroughStyle, TextAlign, UnderlineStyle, Window, WrapBoundary, - WrappedLineLayout, black, fill, point, px, size, -}; -use derive_more::{Deref, DerefMut}; -use smallvec::SmallVec; -use std::sync::Arc; - -/// Pre-computed glyph data for efficient painting without per-glyph cache lookups. -/// -/// This is produced by `ShapedLine::compute_glyph_raster_data` during prepaint -/// and consumed by `ShapedLine::paint_with_raster_data` during paint. -#[derive(Clone, Debug)] -pub struct GlyphRasterData { - /// The raster bounds for each glyph, in paint order. - pub bounds: Vec>, - /// The render params for each glyph (needed for sprite atlas lookup). - pub params: Vec, -} - -/// Set the text decoration for a run of text. -#[derive(Debug, Clone)] -pub struct DecorationRun { - /// The length of the run in utf-8 bytes. - pub len: u32, - - /// The color for this run - pub color: Hsla, - - /// The background color for this run - pub background_color: Option, - - /// The underline style for this run - pub underline: Option, - - /// The strikethrough style for this run - pub strikethrough: Option, -} - -/// A line of text that has been shaped and decorated. -#[derive(Clone, Default, Debug, Deref, DerefMut)] -pub struct ShapedLine { - #[deref] - #[deref_mut] - pub(crate) layout: Arc, - /// The text that was shaped for this line. - pub text: SharedString, - pub(crate) decoration_runs: SmallVec<[DecorationRun; 32]>, -} - -impl ShapedLine { - /// The length of the line in utf-8 bytes. - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> usize { - self.layout.len - } - - /// The width of the shaped line in pixels. - /// - /// This is the glyph advance width computed by the text shaping system and is useful for - /// incrementally advancing a "pen" when painting multiple fragments on the same row. - pub fn width(&self) -> Pixels { - self.layout.width - } - - /// Override the len, useful if you're rendering text a - /// as text b (e.g. rendering invisibles). - pub fn with_len(mut self, len: usize) -> Self { - let layout = self.layout.as_ref(); - self.layout = Arc::new(LineLayout { - font_size: layout.font_size, - width: layout.width, - ascent: layout.ascent, - descent: layout.descent, - runs: layout.runs.clone(), - len, - }); - self - } - - /// Paint the line of text to the window. - pub fn paint( - &self, - origin: Point, - line_height: Pixels, - align: TextAlign, - align_width: Option, - window: &mut Window, - cx: &mut App, - ) -> Result<()> { - paint_line( - origin, - &self.layout, - line_height, - align, - align_width, - &self.decoration_runs, - &[], - window, - cx, - )?; - - Ok(()) - } - - /// Paint the background of the line to the window. - pub fn paint_background( - &self, - origin: Point, - line_height: Pixels, - align: TextAlign, - align_width: Option, - window: &mut Window, - cx: &mut App, - ) -> Result<()> { - paint_line_background( - origin, - &self.layout, - line_height, - align, - align_width, - &self.decoration_runs, - &[], - window, - cx, - )?; - - Ok(()) - } - - /// Split this shaped line at a byte index, returning `(prefix, suffix)`. - /// - /// - `prefix` contains glyphs for bytes `[0, byte_index)` with original positions. - /// Its width equals the x-advance up to the split point. - /// - `suffix` contains glyphs for bytes `[byte_index, len)` with positions - /// shifted left so the first glyph starts at x=0, and byte indices rebased to 0. - /// - Decoration runs are partitioned at the boundary; a run that straddles it is - /// split into two with adjusted lengths. - /// - `font_size`, `ascent`, and `descent` are copied to both halves. - pub fn split_at(&self, byte_index: usize) -> (ShapedLine, ShapedLine) { - let (left_layout, right_layout) = self.layout.split_at(byte_index); - - // Partition decoration runs. A run straddling the boundary is split into two. - let mut left_decorations = SmallVec::new(); - let mut right_decorations = SmallVec::new(); - let mut decoration_offset = 0u32; - let split_point = byte_index as u32; - - for decoration in &self.decoration_runs { - let run_end = decoration_offset + decoration.len; - - if run_end <= split_point { - left_decorations.push(decoration.clone()); - } else if decoration_offset >= split_point { - right_decorations.push(decoration.clone()); - } else { - let left_len = split_point - decoration_offset; - let right_len = run_end - split_point; - left_decorations.push(DecorationRun { - len: left_len, - color: decoration.color, - background_color: decoration.background_color, - underline: decoration.underline, - strikethrough: decoration.strikethrough, - }); - right_decorations.push(DecorationRun { - len: right_len, - color: decoration.color, - background_color: decoration.background_color, - underline: decoration.underline, - strikethrough: decoration.strikethrough, - }); - } - - decoration_offset = run_end; - } - - // Split text - let left_text = if byte_index == self.text.len() { - self.text.clone() - } else { - SharedString::new(&self.text[..byte_index]) - }; - let right_text = if byte_index == 0 { - self.text.clone() - } else { - SharedString::new(&self.text[byte_index..]) - }; - - let left = ShapedLine { - layout: Arc::new(left_layout), - text: left_text, - decoration_runs: left_decorations, - }; - - let right = ShapedLine { - layout: Arc::new(right_layout), - text: right_text, - decoration_runs: right_decorations, - }; - - (left, right) - } -} - -impl LineLayout { - /// Paint this layout to the window, using the given decoration runs to color - /// glyphs and draw underlines and strikethroughs. - /// - /// This is a lower-level alternative to [`ShapedLine::paint`] for callers that - /// hold a bare layout and track decorations themselves. - pub fn paint( - &self, - origin: Point, - line_height: Pixels, - align: TextAlign, - align_width: Option, - decoration_runs: &[DecorationRun], - window: &mut Window, - cx: &mut App, - ) -> Result<()> { - paint_line( - origin, - self, - line_height, - align, - align_width, - decoration_runs, - &[], - window, - cx, - ) - } - - /// Paint the background of this layout to the window, using the given - /// decoration runs to determine background colors. - /// - /// This is a lower-level alternative to [`ShapedLine::paint_background`] for - /// callers that hold a bare layout and track decorations themselves. - pub fn paint_background( - &self, - origin: Point, - line_height: Pixels, - align: TextAlign, - align_width: Option, - decoration_runs: &[DecorationRun], - window: &mut Window, - cx: &mut App, - ) -> Result<()> { - paint_line_background( - origin, - self, - line_height, - align, - align_width, - decoration_runs, - &[], - window, - cx, - ) - } -} - -/// A line of text that has been shaped, decorated, and wrapped by the text layout system. -#[derive(Default, Debug, Deref, DerefMut)] -pub struct WrappedLine { - #[deref] - #[deref_mut] - pub(crate) layout: Arc, - /// The text that was shaped for this line. - pub text: SharedString, - pub(crate) decoration_runs: Vec, -} - -impl WrappedLine { - /// The length of the underlying, unwrapped layout, in utf-8 bytes. - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> usize { - self.layout.len() - } - - /// Paint this line of text to the window. - pub fn paint( - &self, - origin: Point, - line_height: Pixels, - align: TextAlign, - bounds: Option>, - window: &mut Window, - cx: &mut App, - ) -> Result<()> { - let align_width = match bounds { - Some(bounds) => Some(bounds.size.width), - None => self.layout.wrap_width, - }; - - paint_line( - origin, - &self.layout.unwrapped_layout, - line_height, - align, - align_width, - &self.decoration_runs, - &self.wrap_boundaries, - window, - cx, - )?; - - Ok(()) - } - - /// Paint the background of line of text to the window. - pub fn paint_background( - &self, - origin: Point, - line_height: Pixels, - align: TextAlign, - bounds: Option>, - window: &mut Window, - cx: &mut App, - ) -> Result<()> { - let align_width = match bounds { - Some(bounds) => Some(bounds.size.width), - None => self.layout.wrap_width, - }; - - paint_line_background( - origin, - &self.layout.unwrapped_layout, - line_height, - align, - align_width, - &self.decoration_runs, - &self.wrap_boundaries, - window, - cx, - )?; - - Ok(()) - } -} - -fn paint_line( - origin: Point, - layout: &LineLayout, - line_height: Pixels, - align: TextAlign, - align_width: Option, - decoration_runs: &[DecorationRun], - wrap_boundaries: &[WrapBoundary], - window: &mut Window, - cx: &mut App, -) -> Result<()> { - let line_bounds = Bounds::new( - origin, - size( - layout.width, - line_height * (wrap_boundaries.len() as f32 + 1.), - ), - ); - window.paint_layer(line_bounds, |window| { - let padding_top = (line_height - layout.ascent - layout.descent) / 2.; - let baseline_offset = point(px(0.), padding_top + layout.ascent); - let mut decoration_runs = decoration_runs.iter(); - let mut wraps = wrap_boundaries.iter().peekable(); - let mut run_end = 0; - let mut color = black(); - let mut current_underline: Option<(Point, UnderlineStyle)> = None; - let mut current_strikethrough: Option<(Point, StrikethroughStyle)> = None; - let text_system = cx.text_system().clone(); - let mut glyph_origin = point( - aligned_origin_x( - origin, - align_width.unwrap_or(layout.width), - px(0.0), - &align, - layout, - wraps.peek(), - ), - origin.y, - ); - let mut prev_glyph_position = Point::default(); - let mut max_glyph_size = size(px(0.), px(0.)); - let mut first_glyph_x = origin.x; - for (run_ix, run) in layout.runs.iter().enumerate() { - max_glyph_size = text_system.bounding_box(run.font_id, layout.font_size).size; - - for (glyph_ix, glyph) in run.glyphs.iter().enumerate() { - glyph_origin.x += glyph.position.x - prev_glyph_position.x; - if glyph_ix == 0 && run_ix == 0 { - first_glyph_x = glyph_origin.x; - } - - if wraps.peek() == Some(&&WrapBoundary { run_ix, glyph_ix }) { - wraps.next(); - if let Some((underline_origin, underline_style)) = current_underline.as_mut() { - if glyph_origin.x == underline_origin.x { - underline_origin.x -= max_glyph_size.width.half(); - }; - window.paint_underline( - *underline_origin, - glyph_origin.x - underline_origin.x, - underline_style, - ); - if glyph.index < run_end { - underline_origin.x = origin.x; - underline_origin.y += line_height; - } else { - current_underline = None; - } - } - if let Some((strikethrough_origin, strikethrough_style)) = - current_strikethrough.as_mut() - { - if glyph_origin.x == strikethrough_origin.x { - strikethrough_origin.x -= max_glyph_size.width.half(); - }; - window.paint_strikethrough( - *strikethrough_origin, - glyph_origin.x - strikethrough_origin.x, - strikethrough_style, - ); - if glyph.index < run_end { - strikethrough_origin.x = origin.x; - strikethrough_origin.y += line_height; - } else { - current_strikethrough = None; - } - } - - glyph_origin.x = aligned_origin_x( - origin, - align_width.unwrap_or(layout.width), - glyph.position.x, - &align, - layout, - wraps.peek(), - ); - glyph_origin.y += line_height; - } - prev_glyph_position = glyph.position; - - let mut finished_underline: Option<(Point, UnderlineStyle)> = None; - let mut finished_strikethrough: Option<(Point, StrikethroughStyle)> = None; - if glyph.index >= run_end { - let mut style_run = decoration_runs.next(); - - // ignore style runs that apply to a partial glyph - while let Some(run) = style_run { - if glyph.index < run_end + (run.len as usize) { - break; - } - run_end += run.len as usize; - style_run = decoration_runs.next(); - } - - if let Some(style_run) = style_run { - if let Some((_, underline_style)) = &mut current_underline - && style_run.underline.as_ref() != Some(underline_style) - { - finished_underline = current_underline.take(); - } - if let Some(run_underline) = style_run.underline.as_ref() { - current_underline.get_or_insert(( - point( - glyph_origin.x, - glyph_origin.y + baseline_offset.y + (layout.descent * 0.618), - ), - UnderlineStyle { - color: Some(run_underline.color.unwrap_or(style_run.color)), - thickness: run_underline.thickness, - wavy: run_underline.wavy, - }, - )); - } - if let Some((_, strikethrough_style)) = &mut current_strikethrough - && style_run.strikethrough.as_ref() != Some(strikethrough_style) - { - finished_strikethrough = current_strikethrough.take(); - } - if let Some(run_strikethrough) = style_run.strikethrough.as_ref() { - current_strikethrough.get_or_insert(( - point( - glyph_origin.x, - glyph_origin.y - + (((layout.ascent * 0.5) + baseline_offset.y) * 0.5), - ), - StrikethroughStyle { - color: Some(run_strikethrough.color.unwrap_or(style_run.color)), - thickness: run_strikethrough.thickness, - }, - )); - } - - run_end += style_run.len as usize; - color = style_run.color; - } else { - run_end = layout.len; - finished_underline = current_underline.take(); - finished_strikethrough = current_strikethrough.take(); - } - } - - if let Some((mut underline_origin, underline_style)) = finished_underline { - if underline_origin.x == glyph_origin.x { - underline_origin.x -= max_glyph_size.width.half(); - }; - window.paint_underline( - underline_origin, - glyph_origin.x - underline_origin.x, - &underline_style, - ); - } - - if let Some((mut strikethrough_origin, strikethrough_style)) = - finished_strikethrough - { - if strikethrough_origin.x == glyph_origin.x { - strikethrough_origin.x -= max_glyph_size.width.half(); - }; - window.paint_strikethrough( - strikethrough_origin, - glyph_origin.x - strikethrough_origin.x, - &strikethrough_style, - ); - } - - let max_glyph_bounds = Bounds { - origin: glyph_origin, - size: max_glyph_size, - }; - - let content_mask = window.content_mask(); - if max_glyph_bounds.intersects(&content_mask.bounds) { - let vertical_offset = point(px(0.0), glyph.position.y); - if glyph.is_emoji { - window.paint_emoji( - glyph_origin + baseline_offset + vertical_offset, - run.font_id, - glyph.id, - layout.font_size, - )?; - } else { - window.paint_glyph( - glyph_origin + baseline_offset + vertical_offset, - run.font_id, - glyph.id, - layout.font_size, - color, - )?; - } - } - } - } - - let mut last_line_end_x = first_glyph_x + layout.width; - if let Some(boundary) = wrap_boundaries.last() { - let run = &layout.runs[boundary.run_ix]; - let glyph = &run.glyphs[boundary.glyph_ix]; - last_line_end_x -= glyph.position.x; - } - - if let Some((mut underline_start, underline_style)) = current_underline.take() { - if last_line_end_x == underline_start.x { - underline_start.x -= max_glyph_size.width.half() - }; - window.paint_underline( - underline_start, - last_line_end_x - underline_start.x, - &underline_style, - ); - } - - if let Some((mut strikethrough_start, strikethrough_style)) = current_strikethrough.take() { - if last_line_end_x == strikethrough_start.x { - strikethrough_start.x -= max_glyph_size.width.half() - }; - window.paint_strikethrough( - strikethrough_start, - last_line_end_x - strikethrough_start.x, - &strikethrough_style, - ); - } - - Ok(()) - }) -} - -fn paint_line_background( - origin: Point, - layout: &LineLayout, - line_height: Pixels, - align: TextAlign, - align_width: Option, - decoration_runs: &[DecorationRun], - wrap_boundaries: &[WrapBoundary], - window: &mut Window, - cx: &mut App, -) -> Result<()> { - let line_bounds = Bounds::new( - origin, - size( - layout.width, - line_height * (wrap_boundaries.len() as f32 + 1.), - ), - ); - window.paint_layer(line_bounds, |window| { - let mut decoration_runs = decoration_runs.iter(); - let mut wraps = wrap_boundaries.iter().peekable(); - let mut run_end = 0; - let mut current_background: Option<(Point, Hsla)> = None; - let text_system = cx.text_system().clone(); - let mut glyph_origin = point( - aligned_origin_x( - origin, - align_width.unwrap_or(layout.width), - px(0.0), - &align, - layout, - wraps.peek(), - ), - origin.y, - ); - let mut prev_glyph_position = Point::default(); - let mut max_glyph_size = size(px(0.), px(0.)); - for (run_ix, run) in layout.runs.iter().enumerate() { - max_glyph_size = text_system.bounding_box(run.font_id, layout.font_size).size; - - for (glyph_ix, glyph) in run.glyphs.iter().enumerate() { - glyph_origin.x += glyph.position.x - prev_glyph_position.x; - - if wraps.peek() == Some(&&WrapBoundary { run_ix, glyph_ix }) { - wraps.next(); - if let Some((background_origin, background_color)) = current_background.as_mut() - { - if glyph_origin.x == background_origin.x { - background_origin.x -= max_glyph_size.width.half() - } - window.paint_quad(fill( - Bounds { - origin: *background_origin, - size: size(glyph_origin.x - background_origin.x, line_height), - }, - *background_color, - )); - if glyph.index < run_end { - background_origin.x = origin.x; - background_origin.y += line_height; - } else { - current_background = None; - } - } - - glyph_origin.x = aligned_origin_x( - origin, - align_width.unwrap_or(layout.width), - glyph.position.x, - &align, - layout, - wraps.peek(), - ); - glyph_origin.y += line_height; - } - prev_glyph_position = glyph.position; - - let mut finished_background: Option<(Point, Hsla)> = None; - if glyph.index >= run_end { - let mut style_run = decoration_runs.next(); - - // ignore style runs that apply to a partial glyph - while let Some(run) = style_run { - if glyph.index < run_end + (run.len as usize) { - break; - } - run_end += run.len as usize; - style_run = decoration_runs.next(); - } - - if let Some(style_run) = style_run { - if let Some((_, background_color)) = &mut current_background - && style_run.background_color.as_ref() != Some(background_color) - { - finished_background = current_background.take(); - } - if let Some(run_background) = style_run.background_color { - current_background.get_or_insert(( - point(glyph_origin.x, glyph_origin.y), - run_background, - )); - } - run_end += style_run.len as usize; - } else { - run_end = layout.len; - finished_background = current_background.take(); - } - } - - if let Some((mut background_origin, background_color)) = finished_background { - let mut width = glyph_origin.x - background_origin.x; - if background_origin.x == glyph_origin.x { - background_origin.x -= max_glyph_size.width.half(); - }; - window.paint_quad(fill( - Bounds { - origin: background_origin, - size: size(width, line_height), - }, - background_color, - )); - } - } - } - - let mut last_line_end_x = origin.x + layout.width; - if let Some(boundary) = wrap_boundaries.last() { - let run = &layout.runs[boundary.run_ix]; - let glyph = &run.glyphs[boundary.glyph_ix]; - last_line_end_x -= glyph.position.x; - } - - if let Some((mut background_origin, background_color)) = current_background.take() { - if last_line_end_x == background_origin.x { - background_origin.x -= max_glyph_size.width.half() - }; - window.paint_quad(fill( - Bounds { - origin: background_origin, - size: size(last_line_end_x - background_origin.x, line_height), - }, - background_color, - )); - } - - Ok(()) - }) -} - -fn aligned_origin_x( - origin: Point, - align_width: Pixels, - last_glyph_x: Pixels, - align: &TextAlign, - layout: &LineLayout, - wrap_boundary: Option<&&WrapBoundary>, -) -> Pixels { - let end_of_line = if let Some(WrapBoundary { run_ix, glyph_ix }) = wrap_boundary { - layout.runs[*run_ix].glyphs[*glyph_ix].position.x - } else { - layout.width - }; - - let line_width = end_of_line - last_glyph_x; - - match align { - TextAlign::Left => origin.x, - TextAlign::Center => (origin.x * 2.0 + align_width - line_width) / 2.0, - TextAlign::Right => origin.x + align_width - line_width, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{FontId, GlyphId, ShapedGlyph, ShapedRun}; - - /// Helper: build a ShapedLine from glyph descriptors without the platform text system. - /// Each glyph is described as (byte_index, x_position). - fn make_shaped_line( - text: &str, - glyphs: &[(usize, f32)], - width: f32, - decorations: &[DecorationRun], - ) -> ShapedLine { - let shaped_glyphs: Vec = glyphs - .iter() - .map(|&(index, x)| ShapedGlyph { - id: GlyphId(0), - position: point(px(x), px(0.0)), - index, - is_emoji: false, - }) - .collect(); - - ShapedLine { - layout: Arc::new(LineLayout { - font_size: px(16.0), - width: px(width), - ascent: px(12.0), - descent: px(4.0), - runs: vec![ShapedRun { - font_id: FontId(0), - glyphs: shaped_glyphs, - }], - len: text.len(), - }), - text: SharedString::new(text), - decoration_runs: SmallVec::from(decorations.to_vec()), - } - } - - #[test] - fn test_split_at_invariants() { - // Split "abcdef" at every possible byte index and verify structural invariants. - let line = make_shaped_line( - "abcdef", - &[ - (0, 0.0), - (1, 10.0), - (2, 20.0), - (3, 30.0), - (4, 40.0), - (5, 50.0), - ], - 60.0, - &[], - ); - - for i in 0..=6 { - let (left, right) = line.split_at(i); - - assert_eq!( - left.width() + right.width(), - line.width(), - "widths must sum at split={i}" - ); - assert_eq!( - left.len() + right.len(), - line.len(), - "lengths must sum at split={i}" - ); - assert_eq!( - format!("{}{}", left.text.as_ref(), right.text.as_ref()), - "abcdef", - "text must concatenate at split={i}" - ); - assert_eq!(left.font_size, line.font_size, "font_size at split={i}"); - assert_eq!(right.ascent, line.ascent, "ascent at split={i}"); - assert_eq!(right.descent, line.descent, "descent at split={i}"); - } - - // Edge: split at 0 produces no left runs, full content on right - let (left, right) = line.split_at(0); - assert_eq!(left.runs.len(), 0); - assert_eq!(right.runs[0].glyphs.len(), 6); - - // Edge: split at end produces full content on left, no right runs - let (left, right) = line.split_at(6); - assert_eq!(left.runs[0].glyphs.len(), 6); - assert_eq!(right.runs.len(), 0); - } - - #[test] - fn test_split_at_glyph_rebasing() { - // Two font runs (simulating a font fallback boundary at byte 3): - // run A (FontId 0): glyphs at bytes 0,1,2 positions 0,10,20 - // run B (FontId 1): glyphs at bytes 3,4,5 positions 30,40,50 - // Successive splits simulate the incremental splitting done during wrap. - let line = ShapedLine { - layout: Arc::new(LineLayout { - font_size: px(16.0), - width: px(60.0), - ascent: px(12.0), - descent: px(4.0), - runs: vec![ - ShapedRun { - font_id: FontId(0), - glyphs: vec![ - ShapedGlyph { - id: GlyphId(0), - position: point(px(0.0), px(0.0)), - index: 0, - is_emoji: false, - }, - ShapedGlyph { - id: GlyphId(0), - position: point(px(10.0), px(0.0)), - index: 1, - is_emoji: false, - }, - ShapedGlyph { - id: GlyphId(0), - position: point(px(20.0), px(0.0)), - index: 2, - is_emoji: false, - }, - ], - }, - ShapedRun { - font_id: FontId(1), - glyphs: vec![ - ShapedGlyph { - id: GlyphId(0), - position: point(px(30.0), px(0.0)), - index: 3, - is_emoji: false, - }, - ShapedGlyph { - id: GlyphId(0), - position: point(px(40.0), px(0.0)), - index: 4, - is_emoji: false, - }, - ShapedGlyph { - id: GlyphId(0), - position: point(px(50.0), px(0.0)), - index: 5, - is_emoji: false, - }, - ], - }, - ], - len: 6, - }), - text: "abcdef".into(), - decoration_runs: SmallVec::new(), - }; - - // First split at byte 2 — mid-run in run A - let (first, remainder) = line.split_at(2); - assert_eq!(first.text.as_ref(), "ab"); - assert_eq!(first.runs.len(), 1); - assert_eq!(first.runs[0].font_id, FontId(0)); - - // Remainder "cdef" should have two runs: tail of A (1 glyph) + all of B (3 glyphs) - assert_eq!(remainder.text.as_ref(), "cdef"); - assert_eq!(remainder.runs.len(), 2); - assert_eq!(remainder.runs[0].font_id, FontId(0)); - assert_eq!(remainder.runs[0].glyphs.len(), 1); - assert_eq!(remainder.runs[0].glyphs[0].index, 0); - assert_eq!(remainder.runs[0].glyphs[0].position.x, px(0.0)); - assert_eq!(remainder.runs[1].font_id, FontId(1)); - assert_eq!(remainder.runs[1].glyphs[0].index, 1); - assert_eq!(remainder.runs[1].glyphs[0].position.x, px(10.0)); - - // Second split at byte 2 within remainder — crosses the run boundary - let (second, final_part) = remainder.split_at(2); - assert_eq!(second.text.as_ref(), "cd"); - assert_eq!(final_part.text.as_ref(), "ef"); - assert_eq!(final_part.runs[0].glyphs[0].index, 0); - assert_eq!(final_part.runs[0].glyphs[0].position.x, px(0.0)); - - // Widths must sum across all three pieces - assert_eq!( - first.width() + second.width() + final_part.width(), - line.width() - ); - } - - #[test] - fn test_split_at_decorations() { - // Three decoration runs: red [0..2), green [2..5), blue [5..6). - // Split at byte 3 — red goes entirely left, green straddles, blue goes entirely right. - let red = Hsla { - h: 0.0, - s: 1.0, - l: 0.5, - a: 1.0, - }; - let green = Hsla { - h: 0.3, - s: 1.0, - l: 0.5, - a: 1.0, - }; - let blue = Hsla { - h: 0.6, - s: 1.0, - l: 0.5, - a: 1.0, - }; - - let line = make_shaped_line( - "abcdef", - &[ - (0, 0.0), - (1, 10.0), - (2, 20.0), - (3, 30.0), - (4, 40.0), - (5, 50.0), - ], - 60.0, - &[ - DecorationRun { - len: 2, - color: red, - background_color: None, - underline: None, - strikethrough: None, - }, - DecorationRun { - len: 3, - color: green, - background_color: None, - underline: None, - strikethrough: None, - }, - DecorationRun { - len: 1, - color: blue, - background_color: None, - underline: None, - strikethrough: None, - }, - ], - ); - - let (left, right) = line.split_at(3); - - // Left: red(2) + green(1) — green straddled, left portion has len 1 - assert_eq!(left.decoration_runs.len(), 2); - assert_eq!(left.decoration_runs[0].len, 2); - assert_eq!(left.decoration_runs[0].color, red); - assert_eq!(left.decoration_runs[1].len, 1); - assert_eq!(left.decoration_runs[1].color, green); - - // Right: green(2) + blue(1) — green straddled, right portion has len 2 - assert_eq!(right.decoration_runs.len(), 2); - assert_eq!(right.decoration_runs[0].len, 2); - assert_eq!(right.decoration_runs[0].color, green); - assert_eq!(right.decoration_runs[1].len, 1); - assert_eq!(right.decoration_runs[1].color, blue); - } -} diff --git a/crates/gpui_pre/src/text_system/line_layout.rs b/crates/gpui_pre/src/text_system/line_layout.rs deleted file mode 100644 index e7362f71..00000000 --- a/crates/gpui_pre/src/text_system/line_layout.rs +++ /dev/null @@ -1,1140 +0,0 @@ -use crate::{FontId, GlyphId, Pixels, PlatformTextSystem, Point, SharedString, Size, point, px}; -use collections::FxHashMap; -use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; -use smallvec::SmallVec; -use std::{ - borrow::Borrow, - hash::{Hash, Hasher}, - ops::Range, - sync::Arc, -}; - -use super::LineWrapper; - -/// A laid out and styled line of text -#[derive(Default, Debug)] -pub struct LineLayout { - /// The font size for this line - pub font_size: Pixels, - /// The width of the line - pub width: Pixels, - /// The ascent of the line - pub ascent: Pixels, - /// The descent of the line - pub descent: Pixels, - /// The shaped runs that make up this line - pub runs: Vec, - /// The length of the line in utf-8 bytes - pub len: usize, -} - -/// A run of text that has been shaped . -#[derive(Debug, Clone)] -pub struct ShapedRun { - /// The font id for this run - pub font_id: FontId, - /// The glyphs that make up this run - pub glyphs: Vec, -} - -/// A single glyph, ready to paint. -#[derive(Clone, Debug)] -pub struct ShapedGlyph { - /// The ID for this glyph, as determined by the text system. - pub id: GlyphId, - - /// The position of this glyph in its containing line. - pub position: Point, - - /// The index of this glyph in the original text. - pub index: usize, - - /// Whether this glyph is an emoji - pub is_emoji: bool, -} - -impl LineLayout { - /// The index for the character at the given x coordinate - pub fn index_for_x(&self, x: Pixels) -> Option { - if x >= self.width { - None - } else { - for run in self.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: Pixels) -> usize { - let mut prev_index = 0; - let mut prev_x = px(0.); - - for run in self.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; - } - } - - if self.len == 1 { - if x > self.width / 2. { - return 1; - } else { - return 0; - } - } - - self.len - } - - /// The x position of the character at the given index - pub fn x_for_index(&self, index: usize) -> Pixels { - for run in &self.runs { - for glyph in &run.glyphs { - if glyph.index >= index { - return glyph.position.x; - } - } - } - self.width - } - - /// The corresponding Font at the given index - pub fn font_id_for_index(&self, index: usize) -> Option { - for run in &self.runs { - for glyph in &run.glyphs { - if glyph.index >= index { - return Some(run.font_id); - } - } - } - None - } - - /// Split this layout at a byte index, returning `(prefix, suffix)`. - /// - /// - `prefix` contains glyphs for bytes `[0, byte_index)` with original positions. - /// Its width equals the x-advance up to the split point. - /// - `suffix` contains glyphs for bytes `[byte_index, len)` with positions - /// shifted left so the first glyph starts at x=0, and byte indices rebased to 0. - /// - `font_size`, `ascent`, and `descent` are copied to both halves. - pub fn split_at(&self, byte_index: usize) -> (LineLayout, LineLayout) { - let x_offset = self.x_for_index(byte_index); - - // Partition glyph runs. A single run may contribute glyphs to both halves. - let mut left_runs = Vec::new(); - let mut right_runs = Vec::new(); - - for run in &self.runs { - let split_pos = run.glyphs.partition_point(|g| g.index < byte_index); - - if split_pos > 0 { - left_runs.push(ShapedRun { - font_id: run.font_id, - glyphs: run.glyphs[..split_pos].to_vec(), - }); - } - - if split_pos < run.glyphs.len() { - let right_glyphs = run.glyphs[split_pos..] - .iter() - .map(|g| ShapedGlyph { - id: g.id, - position: point(g.position.x - x_offset, g.position.y), - index: g.index - byte_index, - is_emoji: g.is_emoji, - }) - .collect(); - right_runs.push(ShapedRun { - font_id: run.font_id, - glyphs: right_glyphs, - }); - } - } - - let left = LineLayout { - font_size: self.font_size, - width: x_offset, - ascent: self.ascent, - descent: self.descent, - runs: left_runs, - len: byte_index, - }; - - let right = LineLayout { - font_size: self.font_size, - width: self.width - x_offset, - ascent: self.ascent, - descent: self.descent, - runs: right_runs, - len: self.len - byte_index, - }; - - (left, right) - } - - fn compute_wrap_boundaries( - &self, - text: &str, - wrap_width: Pixels, - max_lines: Option, - ) -> SmallVec<[WrapBoundary; 1]> { - let mut boundaries = SmallVec::new(); - let mut first_non_whitespace_ix = None; - let mut last_candidate_ix = None; - let mut last_candidate_x = px(0.); - let mut last_boundary = WrapBoundary { - run_ix: 0, - glyph_ix: 0, - }; - let mut last_boundary_x = px(0.); - let mut prev_ch = '\0'; - let mut glyphs = self - .runs - .iter() - .enumerate() - .flat_map(move |(run_ix, run)| { - run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| { - let character = text[glyph.index..].chars().next().unwrap(); - ( - WrapBoundary { run_ix, glyph_ix }, - character, - glyph.position.x, - ) - }) - }) - .peekable(); - - while let Some((boundary, ch, x)) = glyphs.next() { - if ch == '\n' { - continue; - } - - // Here is very similar to `LineWrapper::wrap_line` to determine text wrapping, - // but there are some differences, so we have to duplicate the code here. - if LineWrapper::is_word_char(ch) { - if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() { - last_candidate_ix = Some(boundary); - last_candidate_x = x; - } - } else { - if ch != ' ' && first_non_whitespace_ix.is_some() { - last_candidate_ix = Some(boundary); - last_candidate_x = x; - } - } - - if ch != ' ' && first_non_whitespace_ix.is_none() { - first_non_whitespace_ix = Some(boundary); - } - - let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x); - let width = next_x - last_boundary_x; - - if width > wrap_width && boundary > last_boundary { - // When used line_clamp, we should limit the number of lines. - if let Some(max_lines) = max_lines - && boundaries.len() >= max_lines.saturating_sub(1) - { - break; - } - - if let Some(last_candidate_ix) = last_candidate_ix.take() { - last_boundary = last_candidate_ix; - last_boundary_x = last_candidate_x; - } else { - last_boundary = boundary; - last_boundary_x = x; - } - boundaries.push(last_boundary); - } - prev_ch = ch; - } - - boundaries - } -} - -/// A line of text that has been wrapped to fit a given width -#[derive(Default, Debug)] -pub struct WrappedLineLayout { - /// The line layout, pre-wrapping. - pub unwrapped_layout: Arc, - - /// The boundaries at which the line was wrapped - pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>, - - /// The width of the line, if it was wrapped - pub wrap_width: Option, -} - -/// A boundary at which a line was wrapped -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct WrapBoundary { - /// The index in the run just before the line was wrapped - pub run_ix: usize, - /// The index of the glyph just before the line was wrapped - pub glyph_ix: usize, -} - -impl WrappedLineLayout { - /// The length of the underlying text, in utf8 bytes. - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> usize { - self.unwrapped_layout.len - } - - /// The width of this line, in pixels, whether or not it was wrapped. - pub fn width(&self) -> Pixels { - self.wrap_width - .unwrap_or(Pixels::MAX) - .min(self.unwrapped_layout.width) - } - - /// The size of the whole wrapped text, for the given line_height. - /// can span multiple lines if there are multiple wrap boundaries. - pub fn size(&self, line_height: Pixels) -> Size { - Size { - width: self.width(), - height: line_height * (self.wrap_boundaries.len() + 1), - } - } - - /// The ascent of a line in this layout - pub fn ascent(&self) -> Pixels { - self.unwrapped_layout.ascent - } - - /// The descent of a line in this layout - pub fn descent(&self) -> Pixels { - self.unwrapped_layout.descent - } - - /// The wrap boundaries in this layout - pub fn wrap_boundaries(&self) -> &[WrapBoundary] { - &self.wrap_boundaries - } - - /// The font size of this layout - pub fn font_size(&self) -> Pixels { - self.unwrapped_layout.font_size - } - - /// The runs in this layout, sans wrapping - pub fn runs(&self) -> &[ShapedRun] { - &self.unwrapped_layout.runs - } - - /// The index corresponding to a given position in this layout for the given line height. - /// - /// See also [`Self::closest_index_for_position`]. - pub fn index_for_position( - &self, - position: Point, - line_height: Pixels, - ) -> Result { - self._index_for_position(position, line_height, false) - } - - /// The closest index to a given position in this layout for the given line height. - /// - /// Closest means the character boundary closest to the given position. - /// - /// See also [`LineLayout::closest_index_for_x`]. - pub fn closest_index_for_position( - &self, - position: Point, - line_height: Pixels, - ) -> Result { - self._index_for_position(position, line_height, true) - } - - fn _index_for_position( - &self, - mut position: Point, - line_height: Pixels, - closest: bool, - ) -> Result { - let wrapped_line_ix = (position.y / line_height) as usize; - - let wrapped_line_start_index; - let wrapped_line_start_x; - if wrapped_line_ix > 0 { - let Some(line_start_boundary) = self.wrap_boundaries.get(wrapped_line_ix - 1) else { - return Err(0); - }; - let run = &self.unwrapped_layout.runs[line_start_boundary.run_ix]; - let glyph = &run.glyphs[line_start_boundary.glyph_ix]; - wrapped_line_start_index = glyph.index; - wrapped_line_start_x = glyph.position.x; - } else { - wrapped_line_start_index = 0; - wrapped_line_start_x = Pixels::ZERO; - }; - - let wrapped_line_end_index; - let wrapped_line_end_x; - if wrapped_line_ix < self.wrap_boundaries.len() { - let next_wrap_boundary_ix = wrapped_line_ix; - let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix]; - let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix]; - let glyph = &run.glyphs[next_wrap_boundary.glyph_ix]; - wrapped_line_end_index = glyph.index; - wrapped_line_end_x = glyph.position.x; - } else { - wrapped_line_end_index = self.unwrapped_layout.len; - wrapped_line_end_x = self.unwrapped_layout.width; - }; - - let mut position_in_unwrapped_line = position; - position_in_unwrapped_line.x += wrapped_line_start_x; - if position_in_unwrapped_line.x < wrapped_line_start_x { - Err(wrapped_line_start_index) - } else if position_in_unwrapped_line.x >= wrapped_line_end_x { - Err(wrapped_line_end_index) - } else { - if closest { - Ok(self - .unwrapped_layout - .closest_index_for_x(position_in_unwrapped_line.x)) - } else { - Ok(self - .unwrapped_layout - .index_for_x(position_in_unwrapped_line.x) - .unwrap()) - } - } - } - - /// Returns the pixel position for the given byte index. - pub fn position_for_index(&self, index: usize, line_height: Pixels) -> Option> { - let mut line_start_ix = 0; - let mut line_end_indices = self - .wrap_boundaries - .iter() - .map(|wrap_boundary| { - let run = &self.unwrapped_layout.runs[wrap_boundary.run_ix]; - let glyph = &run.glyphs[wrap_boundary.glyph_ix]; - glyph.index - }) - .chain([self.len()]) - .enumerate(); - for (ix, line_end_ix) in line_end_indices { - let line_y = ix as f32 * line_height; - if index < line_start_ix { - break; - } else if index > line_end_ix { - line_start_ix = line_end_ix; - continue; - } else { - let line_start_x = self.unwrapped_layout.x_for_index(line_start_ix); - let x = self.unwrapped_layout.x_for_index(index) - line_start_x; - return Some(point(x, line_y)); - } - } - - None - } -} - -pub(crate) struct LineLayoutCache { - previous_frame: Mutex, - current_frame: RwLock, - platform_text_system: Arc, -} - -#[derive(Default)] -struct FrameCache { - lines: FxHashMap, Arc>, - wrapped_lines: FxHashMap, Arc>, - used_lines: Vec>, - used_wrapped_lines: Vec>, - - // Content-addressable caches keyed by caller-provided text hash + layout params. - // These allow cache hits without materializing a contiguous `SharedString`. - // - // IMPORTANT: To support allocation-free lookups, we store these maps using a key type - // (`HashedCacheKeyRef`) that can be computed without building a contiguous `&str`/`SharedString`. - // On miss, we allocate once and store under an owned `HashedCacheKey`. - lines_by_hash: FxHashMap, Arc>, - wrapped_lines_by_hash: FxHashMap, Arc>, - used_lines_by_hash: Vec>, - used_wrapped_lines_by_hash: Vec>, -} - -#[derive(Clone, Default)] -pub(crate) struct LineLayoutIndex { - lines_index: usize, - wrapped_lines_index: usize, - lines_by_hash_index: usize, - wrapped_lines_by_hash_index: usize, -} - -impl LineLayoutCache { - pub fn new(platform_text_system: Arc) -> Self { - Self { - previous_frame: Mutex::default(), - current_frame: RwLock::default(), - platform_text_system, - } - } - - pub fn layout_index(&self) -> LineLayoutIndex { - let frame = self.current_frame.read(); - LineLayoutIndex { - lines_index: frame.used_lines.len(), - wrapped_lines_index: frame.used_wrapped_lines.len(), - lines_by_hash_index: frame.used_lines_by_hash.len(), - wrapped_lines_by_hash_index: frame.used_wrapped_lines_by_hash.len(), - } - } - - pub fn reuse_layouts(&self, range: Range) { - let mut previous_frame = &mut *self.previous_frame.lock(); - let mut current_frame = &mut *self.current_frame.write(); - - for key in &previous_frame.used_lines[range.start.lines_index..range.end.lines_index] { - if let Some((key, line)) = previous_frame.lines.remove_entry(key) { - current_frame.lines.insert(key, line); - } - current_frame.used_lines.push(key.clone()); - } - - for key in &previous_frame.used_wrapped_lines - [range.start.wrapped_lines_index..range.end.wrapped_lines_index] - { - if let Some((key, line)) = previous_frame.wrapped_lines.remove_entry(key) { - current_frame.wrapped_lines.insert(key, line); - } - current_frame.used_wrapped_lines.push(key.clone()); - } - - for key in &previous_frame.used_lines_by_hash - [range.start.lines_by_hash_index..range.end.lines_by_hash_index] - { - if let Some((key, line)) = previous_frame.lines_by_hash.remove_entry(key) { - current_frame.lines_by_hash.insert(key, line); - } - current_frame.used_lines_by_hash.push(key.clone()); - } - - for key in &previous_frame.used_wrapped_lines_by_hash - [range.start.wrapped_lines_by_hash_index..range.end.wrapped_lines_by_hash_index] - { - if let Some((key, line)) = previous_frame.wrapped_lines_by_hash.remove_entry(key) { - current_frame.wrapped_lines_by_hash.insert(key, line); - } - current_frame.used_wrapped_lines_by_hash.push(key.clone()); - } - } - - pub fn truncate_layouts(&self, index: LineLayoutIndex) { - let mut current_frame = &mut *self.current_frame.write(); - current_frame.used_lines.truncate(index.lines_index); - current_frame - .used_wrapped_lines - .truncate(index.wrapped_lines_index); - current_frame - .used_lines_by_hash - .truncate(index.lines_by_hash_index); - current_frame - .used_wrapped_lines_by_hash - .truncate(index.wrapped_lines_by_hash_index); - } - - pub fn finish_frame(&self) { - let mut prev_frame = self.previous_frame.lock(); - let mut curr_frame = self.current_frame.write(); - std::mem::swap(&mut *prev_frame, &mut *curr_frame); - curr_frame.lines.clear(); - curr_frame.wrapped_lines.clear(); - curr_frame.used_lines.clear(); - curr_frame.used_wrapped_lines.clear(); - - curr_frame.lines_by_hash.clear(); - curr_frame.wrapped_lines_by_hash.clear(); - curr_frame.used_lines_by_hash.clear(); - curr_frame.used_wrapped_lines_by_hash.clear(); - } - - pub fn layout_wrapped_line( - &self, - text: Text, - font_size: Pixels, - runs: &[FontRun], - wrap_width: Option, - max_lines: Option, - ) -> Arc - where - Text: AsRef, - SharedString: From, - { - let key = &CacheKeyRef { - text: text.as_ref(), - font_size, - runs, - wrap_width, - force_width: None, - } as &dyn AsCacheKeyRef; - - let current_frame = self.current_frame.upgradable_read(); - if let Some(layout) = current_frame.wrapped_lines.get(key) { - return layout.clone(); - } - - let previous_frame_entry = self.previous_frame.lock().wrapped_lines.remove_entry(key); - if let Some((key, layout)) = previous_frame_entry { - let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame); - current_frame - .wrapped_lines - .insert(key.clone(), layout.clone()); - current_frame.used_wrapped_lines.push(key); - layout - } else { - drop(current_frame); - let text = SharedString::from(text); - let unwrapped_layout = self.layout_line::<&SharedString>(&text, font_size, runs, None); - let wrap_boundaries = if let Some(wrap_width) = wrap_width { - unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width, max_lines) - } else { - SmallVec::new() - }; - let layout = Arc::new(WrappedLineLayout { - unwrapped_layout, - wrap_boundaries, - wrap_width, - }); - let key = Arc::new(CacheKey { - text, - font_size, - runs: SmallVec::from(runs), - wrap_width, - force_width: None, - }); - - let mut current_frame = self.current_frame.write(); - current_frame - .wrapped_lines - .insert(key.clone(), layout.clone()); - current_frame.used_wrapped_lines.push(key); - - layout - } - } - - pub fn layout_line( - &self, - text: Text, - font_size: Pixels, - runs: &[FontRun], - force_width: Option, - ) -> Arc - where - Text: AsRef, - SharedString: From, - { - let key = &CacheKeyRef { - text: text.as_ref(), - font_size, - runs, - wrap_width: None, - force_width, - } as &dyn AsCacheKeyRef; - - let current_frame = self.current_frame.upgradable_read(); - if let Some(layout) = current_frame.lines.get(key) { - return layout.clone(); - } - - let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame); - if let Some((key, layout)) = self.previous_frame.lock().lines.remove_entry(key) { - current_frame.lines.insert(key.clone(), layout.clone()); - current_frame.used_lines.push(key); - layout - } else { - let text = SharedString::from(text); - let mut layout = self - .platform_text_system - .layout_line(&text, font_size, runs); - - if let Some(force_width) = force_width { - apply_force_width_to_layout(&mut layout, force_width); - } - - let key = Arc::new(CacheKey { - text, - font_size, - runs: SmallVec::from(runs), - wrap_width: None, - force_width, - }); - let layout = Arc::new(layout); - current_frame.lines.insert(key.clone(), layout.clone()); - current_frame.used_lines.push(key); - layout - } - } - - /// Try to retrieve a previously-shaped line layout using a caller-provided content hash. - /// - /// This is a *non-allocating* cache probe: it does not materialize any text. If the layout - /// is not already cached in either the current frame or previous frame, returns `None`. - /// - /// Contract (caller enforced): - /// - Same `text_hash` implies identical text content (collision risk accepted by caller). - /// - `text_len` should be the UTF-8 byte length of the text (helps reduce accidental collisions). - pub fn try_layout_line_by_hash( - &self, - text_hash: u64, - text_len: usize, - font_size: Pixels, - runs: &[FontRun], - force_width: Option, - ) -> Option> { - let key_ref = HashedCacheKeyRef { - text_hash, - text_len, - font_size, - runs, - wrap_width: None, - force_width, - }; - - let current_frame = self.current_frame.read(); - if let Some((_, layout)) = current_frame.lines_by_hash.iter().find(|(key, _)| { - HashedCacheKeyRef { - text_hash: key.text_hash, - text_len: key.text_len, - font_size: key.font_size, - runs: key.runs.as_slice(), - wrap_width: key.wrap_width, - force_width: key.force_width, - } == key_ref - }) { - return Some(layout.clone()); - } - - let previous_frame = self.previous_frame.lock(); - if let Some((_, layout)) = previous_frame.lines_by_hash.iter().find(|(key, _)| { - HashedCacheKeyRef { - text_hash: key.text_hash, - text_len: key.text_len, - font_size: key.font_size, - runs: key.runs.as_slice(), - wrap_width: key.wrap_width, - force_width: key.force_width, - } == key_ref - }) { - return Some(layout.clone()); - } - - None - } - - /// Layout a line of text using a caller-provided content hash as the cache key. - /// - /// This enables cache hits without materializing a contiguous `SharedString` for `text`. - /// If the cache misses, `materialize_text` is invoked to produce the `SharedString` for shaping. - /// - /// Contract (caller enforced): - /// - Same `text_hash` implies identical text content (collision risk accepted by caller). - /// - `text_len` should be the UTF-8 byte length of the text (helps reduce accidental collisions). - pub fn layout_line_by_hash( - &self, - text_hash: u64, - text_len: usize, - font_size: Pixels, - runs: &[FontRun], - force_width: Option, - materialize_text: impl FnOnce() -> SharedString, - ) -> Arc { - let key_ref = HashedCacheKeyRef { - text_hash, - text_len, - font_size, - runs, - wrap_width: None, - force_width, - }; - - // Fast path: already cached (no allocation). - let current_frame = self.current_frame.upgradable_read(); - if let Some((_, layout)) = current_frame.lines_by_hash.iter().find(|(key, _)| { - HashedCacheKeyRef { - text_hash: key.text_hash, - text_len: key.text_len, - font_size: key.font_size, - runs: key.runs.as_slice(), - wrap_width: key.wrap_width, - force_width: key.force_width, - } == key_ref - }) { - return layout.clone(); - } - - let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame); - - // Try to reuse from previous frame without allocating; do a linear scan to find a matching key. - // (We avoid `drain()` here because it would eagerly move all entries.) - let mut previous_frame = self.previous_frame.lock(); - if let Some(existing_key) = previous_frame - .used_lines_by_hash - .iter() - .find(|key| { - HashedCacheKeyRef { - text_hash: key.text_hash, - text_len: key.text_len, - font_size: key.font_size, - runs: key.runs.as_slice(), - wrap_width: key.wrap_width, - force_width: key.force_width, - } == key_ref - }) - .cloned() - { - if let Some((key, layout)) = previous_frame.lines_by_hash.remove_entry(&existing_key) { - current_frame - .lines_by_hash - .insert(key.clone(), layout.clone()); - current_frame.used_lines_by_hash.push(key); - return layout; - } - } - - let text = materialize_text(); - let mut layout = self - .platform_text_system - .layout_line(&text, font_size, runs); - - if let Some(force_width) = force_width { - apply_force_width_to_layout(&mut layout, force_width); - } - - let key = Arc::new(HashedCacheKey { - text_hash, - text_len, - font_size, - runs: SmallVec::from(runs), - wrap_width: None, - force_width, - }); - let layout = Arc::new(layout); - current_frame - .lines_by_hash - .insert(key.clone(), layout.clone()); - current_frame.used_lines_by_hash.push(key); - layout - } -} - -// Combining marks (e.g. Thai vowel signs, Arabic diacritics) are shaped by -// HarfBuzz at the same x position as their base character. The force-width -// loop must not advance the cell counter for these zero-advance glyphs, -// otherwise they get displaced into the next cell. We detect them by checking -// whether shaped x has advanced by at least half a cell beyond the last base. -fn apply_force_width_to_layout(layout: &mut LineLayout, force_width: Pixels) { - let mut glyph_pos: usize = 0; - // NEG_INFINITY ensures the first glyph is always classified as a base. - let mut last_base_shaped_x = px(f32::NEG_INFINITY); - let mut last_base_actual_x = px(0.); - - for run in layout.runs.iter_mut() { - for glyph in run.glyphs.iter_mut() { - let shaped_x = glyph.position.x; - - if shaped_x > last_base_shaped_x + force_width * 0.5 { - let forced_x = glyph_pos * force_width; - if (shaped_x - forced_x).abs() > px(1.) { - glyph.position.x = forced_x; - } - last_base_shaped_x = shaped_x; - last_base_actual_x = glyph.position.x; - glyph_pos += 1; - } else { - glyph.position.x = last_base_actual_x + (shaped_x - last_base_shaped_x); - } - } - } -} - -/// A run of text with a single font. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -#[expect(missing_docs)] -pub struct FontRun { - pub len: usize, - pub font_id: FontId, -} - -trait AsCacheKeyRef { - fn as_cache_key_ref(&self) -> CacheKeyRef<'_>; -} - -#[derive(Clone, Debug, Eq)] -struct CacheKey { - text: SharedString, - font_size: Pixels, - runs: SmallVec<[FontRun; 1]>, - wrap_width: Option, - force_width: Option, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -struct CacheKeyRef<'a> { - text: &'a str, - font_size: Pixels, - runs: &'a [FontRun], - wrap_width: Option, - force_width: Option, -} - -#[derive(Clone, Debug)] -struct HashedCacheKey { - text_hash: u64, - text_len: usize, - font_size: Pixels, - runs: SmallVec<[FontRun; 1]>, - wrap_width: Option, - force_width: Option, -} - -#[derive(Copy, Clone)] -struct HashedCacheKeyRef<'a> { - text_hash: u64, - text_len: usize, - font_size: Pixels, - runs: &'a [FontRun], - wrap_width: Option, - force_width: Option, -} - -impl PartialEq for dyn AsCacheKeyRef + '_ { - fn eq(&self, other: &dyn AsCacheKeyRef) -> bool { - self.as_cache_key_ref() == other.as_cache_key_ref() - } -} - -impl PartialEq for HashedCacheKey { - fn eq(&self, other: &Self) -> bool { - self.text_hash == other.text_hash - && self.text_len == other.text_len - && self.font_size == other.font_size - && self.runs.as_slice() == other.runs.as_slice() - && self.wrap_width == other.wrap_width - && self.force_width == other.force_width - } -} - -impl Eq for HashedCacheKey {} - -impl Hash for HashedCacheKey { - fn hash(&self, state: &mut H) { - self.text_hash.hash(state); - self.text_len.hash(state); - self.font_size.hash(state); - self.runs.as_slice().hash(state); - self.wrap_width.hash(state); - self.force_width.hash(state); - } -} - -impl PartialEq for HashedCacheKeyRef<'_> { - fn eq(&self, other: &Self) -> bool { - self.text_hash == other.text_hash - && self.text_len == other.text_len - && self.font_size == other.font_size - && self.runs == other.runs - && self.wrap_width == other.wrap_width - && self.force_width == other.force_width - } -} - -impl Eq for HashedCacheKeyRef<'_> {} - -impl Hash for HashedCacheKeyRef<'_> { - fn hash(&self, state: &mut H) { - self.text_hash.hash(state); - self.text_len.hash(state); - self.font_size.hash(state); - self.runs.hash(state); - self.wrap_width.hash(state); - self.force_width.hash(state); - } -} - -impl Eq for dyn AsCacheKeyRef + '_ {} - -impl Hash for dyn AsCacheKeyRef + '_ { - fn hash(&self, state: &mut H) { - self.as_cache_key_ref().hash(state) - } -} - -impl AsCacheKeyRef for CacheKey { - fn as_cache_key_ref(&self) -> CacheKeyRef<'_> { - CacheKeyRef { - text: &self.text, - font_size: self.font_size, - runs: self.runs.as_slice(), - wrap_width: self.wrap_width, - force_width: self.force_width, - } - } -} - -impl PartialEq for CacheKey { - fn eq(&self, other: &Self) -> bool { - self.as_cache_key_ref().eq(&other.as_cache_key_ref()) - } -} - -impl Hash for CacheKey { - fn hash(&self, state: &mut H) { - self.as_cache_key_ref().hash(state); - } -} - -impl<'a> Borrow for Arc { - fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) { - self.as_ref() as &dyn AsCacheKeyRef - } -} - -impl AsCacheKeyRef for CacheKeyRef<'_> { - fn as_cache_key_ref(&self) -> CacheKeyRef<'_> { - *self - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::GlyphId; - - fn glyph_at(x: f32, index: usize) -> ShapedGlyph { - ShapedGlyph { - id: GlyphId(0), - position: point(px(x), px(0.)), - index, - is_emoji: false, - } - } - - fn make_layout(glyphs: Vec) -> LineLayout { - LineLayout { - font_size: px(16.), - width: px(100.), - ascent: px(12.), - descent: px(4.), - runs: vec![ShapedRun { - font_id: FontId(0), - glyphs, - }], - len: 0, - } - } - - fn glyph_x_positions(layout: &LineLayout) -> Vec { - layout.runs[0] - .glyphs - .iter() - .map(|g| f32::from(g.position.x)) - .collect() - } - - #[test] - fn test_force_width_latin_unchanged() { - let cell_width = px(8.); - let mut layout = make_layout(vec![glyph_at(0., 0), glyph_at(8., 1), glyph_at(16., 2)]); - - apply_force_width_to_layout(&mut layout, cell_width); - - let positions = glyph_x_positions(&layout); - assert_eq!(positions, vec![0., 8., 16.]); - } - - #[test] - fn test_force_width_combining_marks_not_advanced() { - let cell_width = px(8.); - // Simulates Thai "กี" — base consonant at x=0, combining vowel also at x=0 - let mut layout = make_layout(vec![ - glyph_at(0., 0), // ก (base) - glyph_at(0., 3), // ี (combining mark, same x) - ]); - - apply_force_width_to_layout(&mut layout, cell_width); - - let positions = glyph_x_positions(&layout); - assert_eq!(positions, vec![0., 0.]); - } - - #[test] - fn test_force_width_base_after_combining_mark() { - let cell_width = px(8.); - let mut layout = make_layout(vec![glyph_at(0., 0), glyph_at(0., 3), glyph_at(8., 6)]); - - apply_force_width_to_layout(&mut layout, cell_width); - - let positions = glyph_x_positions(&layout); - assert_eq!(positions, vec![0., 0., 8.]); - } - - #[test] - fn test_force_width_multiple_combining_marks() { - let cell_width = px(8.); - // Simulates "ก้" — base + vowel + tone mark (two combining marks stacked) - let mut layout = make_layout(vec![ - glyph_at(0., 0), // ก (base) - glyph_at(0., 3), // vowel (combining) - glyph_at(0., 6), // tone mark (combining) - glyph_at(8., 9), // next base - ]); - - apply_force_width_to_layout(&mut layout, cell_width); - - let positions = glyph_x_positions(&layout); - assert_eq!(positions, vec![0., 0., 0., 8.]); - } - - #[test] - fn test_force_width_corrects_drifted_base_positions() { - let cell_width = px(8.); - // Font metrics don't perfectly match cell grid — glyphs drift >1px from cell boundary - let mut layout = make_layout(vec![ - glyph_at(0.5, 0), // within 1px tolerance, kept as-is - glyph_at(10.2, 1), // >1px off from 8.0, corrected - glyph_at(19.8, 2), // >1px off from 16.0, corrected - ]); - - apply_force_width_to_layout(&mut layout, cell_width); - - let positions = glyph_x_positions(&layout); - assert_eq!(positions, vec![0.5, 8., 16.]); - } - - #[test] - fn test_force_width_combining_mark_after_within_tolerance_base() { - let cell_width = px(8.); - // Base glyph is within 1px of grid so it keeps its shaped position. - // The combining mark must align to the base's actual position, not the grid slot. - let mut layout = make_layout(vec![glyph_at(0.5, 0), glyph_at(0.5, 3)]); - - apply_force_width_to_layout(&mut layout, cell_width); - - let positions = glyph_x_positions(&layout); - assert_eq!(positions, vec![0.5, 0.5]); - } -} diff --git a/crates/gpui_pre/src/text_system/line_wrapper.rs b/crates/gpui_pre/src/text_system/line_wrapper.rs deleted file mode 100644 index 678e9e0f..00000000 --- a/crates/gpui_pre/src/text_system/line_wrapper.rs +++ /dev/null @@ -1,1587 +0,0 @@ -use crate::{FontId, Pixels, SharedString, TextRun, TextSystem, px}; -use collections::HashMap; -use std::{borrow::Cow, iter, sync::Arc}; - -/// Determines whether to truncate text from the start or end. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum TruncateFrom { - /// Truncate text from the start. - Start, - /// Truncate text from the end. - End, - /// Truncate text from the middle, preserving the start and end. - Middle, -} - -/// The GPUI line wrapper, used to wrap lines of text to a given width. -pub struct LineWrapper { - text_system: Arc, - pub(crate) font_id: FontId, - pub(crate) font_size: Pixels, - cached_ascii_char_widths: [Option; 128], - cached_other_char_widths: HashMap, -} - -impl LineWrapper { - /// The maximum indent that can be applied to a line. - pub const MAX_INDENT: u32 = 256; - - pub(crate) fn new(font_id: FontId, font_size: Pixels, text_system: Arc) -> Self { - Self { - text_system, - font_id, - font_size, - cached_ascii_char_widths: [None; 128], - cached_other_char_widths: HashMap::default(), - } - } - - /// Wrap a line of text to the given width with this wrapper's font and font size. - pub fn wrap_line<'a>( - &'a mut self, - fragments: &'a [LineFragment], - wrap_width: Pixels, - ) -> impl Iterator + 'a { - let mut width = px(0.); - let mut first_non_whitespace_ix = None; - let mut indent = None; - let mut last_candidate_ix = 0; - let mut last_candidate_width = px(0.); - let mut last_wrap_ix = 0; - let mut prev_c = '\0'; - let mut index = 0; - let mut candidates = fragments - .iter() - .flat_map(move |fragment| fragment.wrap_boundary_candidates()) - .peekable(); - iter::from_fn(move || { - for candidate in candidates.by_ref() { - let ix = index; - index += candidate.len_utf8(); - let mut new_prev_c = prev_c; - let item_width = match candidate { - WrapBoundaryCandidate::Char { character: c } => { - if c == '\n' { - continue; - } - - if Self::is_word_char(c) { - if prev_c == ' ' && c != ' ' && first_non_whitespace_ix.is_some() { - last_candidate_ix = ix; - last_candidate_width = width; - } - } else { - // CJK may not be space separated, e.g.: `Hello world你好世界` - if 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); - } - - new_prev_c = c; - - self.width_for_char(c) - } - WrapBoundaryCandidate::Element { - width: element_width, - .. - } => { - if prev_c == ' ' && first_non_whitespace_ix.is_some() { - last_candidate_ix = ix; - last_candidate_width = width; - } - - if first_non_whitespace_ix.is_none() { - first_non_whitespace_ix = Some(ix); - } - - element_width - } - }; - - width += item_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 = item_width; - } - - if let Some(indent) = indent { - width += self.width_for_char(' ') * indent as f32; - } - - return Some(Boundary::new(last_wrap_ix, indent.unwrap_or(0))); - } - - prev_c = new_prev_c; - } - - None - }) - } - - /// Determines if a line should be truncated based on its width. - /// - /// Returns the truncation index in `line`. - pub fn should_truncate_line( - &mut self, - line: &str, - truncate_width: Pixels, - truncation_affix: &str, - truncate_from: TruncateFrom, - ) -> Option { - let mut width = px(0.); - let suffix_width = truncation_affix - .chars() - .map(|c| self.width_for_char(c)) - .fold(px(0.0), |a, x| a + x); - let mut truncate_ix = 0; - - match truncate_from { - TruncateFrom::Start => { - for (ix, c) in line.char_indices().rev() { - if width + suffix_width < truncate_width { - truncate_ix = ix; - } - - let char_width = self.width_for_char(c); - width += char_width; - - if width.floor() > truncate_width { - return Some(truncate_ix); - } - } - } - TruncateFrom::End => { - for (ix, c) in line.char_indices() { - if width + suffix_width < truncate_width { - truncate_ix = ix; - } - - let char_width = self.width_for_char(c); - width += char_width; - - if width.floor() > truncate_width { - return Some(truncate_ix); - } - } - } - TruncateFrom::Middle => {} - } - - None - } - - fn should_truncate_line_middle( - &mut self, - line: &str, - truncate_width: Pixels, - truncation_affix: &str, - ) -> Option<(usize, usize)> { - let suffix_width = truncation_affix - .chars() - .map(|c| self.width_for_char(c)) - .fold(px(0.0), |a, x| a + x); - - let total_width: Pixels = line - .chars() - .map(|c| self.width_for_char(c)) - .fold(px(0.0), |a, x| a + x); - - if total_width <= truncate_width { - return None; - } - - let content_budget = truncate_width - suffix_width; - if content_budget <= px(0.) { - return Some((0, line.len())); - } - - let front_budget = content_budget * (2.0 / 3.0); - let back_budget = content_budget - front_budget; - - let mut front_width = px(0.); - let mut front_end_ix = 0usize; - for (ix, c) in line.char_indices() { - let char_width = self.width_for_char(c); - if front_width + char_width > front_budget { - break; - } - front_width += char_width; - front_end_ix = ix + c.len_utf8(); - } - - let mut back_width = px(0.); - let mut back_start_ix = line.len(); - for (ix, c) in line.char_indices().rev() { - let char_width = self.width_for_char(c); - if back_width + char_width > back_budget { - break; - } - back_width += char_width; - back_start_ix = ix; - } - - if front_end_ix >= back_start_ix { - return Some((0, line.len())); - } - - Some((front_end_ix, back_start_ix)) - } - - /// Truncate a line of text to the given width with this wrapper's font and font size. - pub fn truncate_line<'a>( - &mut self, - line: SharedString, - truncate_width: Pixels, - truncation_affix: &str, - runs: &'a [TextRun], - truncate_from: TruncateFrom, - ) -> (SharedString, Cow<'a, [TextRun]>) { - if truncate_from == TruncateFrom::Middle { - if let Some((front_end_ix, back_start_ix)) = - self.should_truncate_line_middle(&line, truncate_width, truncation_affix) - { - let result = SharedString::from(format!( - "{}{truncation_affix}{}", - &line[..front_end_ix], - &line[back_start_ix..] - )); - let mut runs = runs.to_vec(); - update_runs_after_middle_truncation( - truncation_affix, - &mut runs, - front_end_ix, - back_start_ix, - ); - return (result, Cow::Owned(runs)); - } else { - return (line, Cow::Borrowed(runs)); - } - } - - if let Some(truncate_ix) = - self.should_truncate_line(&line, truncate_width, truncation_affix, truncate_from) - { - let result = match truncate_from { - TruncateFrom::Start => SharedString::from(format!( - "{truncation_affix}{}", - &line[line.ceil_char_boundary(truncate_ix + 1)..] - )), - TruncateFrom::End => SharedString::from(format!( - "{}{truncation_affix}", - line[..truncate_ix] - .trim_end_matches(|c: char| c.is_whitespace() || c.is_ascii_punctuation()) - )), - TruncateFrom::Middle => unreachable!("Middle truncation is handled above"), - }; - let mut runs = runs.to_vec(); - update_runs_after_truncation(&result, truncation_affix, &mut runs, truncate_from); - (result, Cow::Owned(runs)) - } else { - (line, Cow::Borrowed(runs)) - } - } - - /// Truncate text to fit within a given number of wrapped lines. - /// - /// Unlike `truncate_line` which treats the text as a flat width budget - /// (`width * max_lines`), this method accounts for word-boundary wrapping: - /// it walks through characters once, tracking wrap boundaries and the - /// truncation point simultaneously. When text overflows on the last - /// allowed line, it truncates there and appends the affix. - /// - /// For `max_lines == 1`, this delegates to `truncate_line`. - pub fn truncate_wrapped_line<'a>( - &mut self, - text: SharedString, - wrap_width: Pixels, - max_lines: usize, - truncation_affix: &str, - runs: &'a [TextRun], - truncate_from: TruncateFrom, - ) -> (SharedString, Cow<'a, [TextRun]>) { - if max_lines <= 1 || truncate_from == TruncateFrom::Start { - return self.truncate_line( - text, - wrap_width * max_lines, - truncation_affix, - runs, - truncate_from, - ); - } - if truncate_from == TruncateFrom::Middle { - return self.truncate_line(text, wrap_width, truncation_affix, runs, truncate_from); - } - - let affix_width: Pixels = truncation_affix - .chars() - .map(|c| self.width_for_char(c)) - .sum(); - - let mut width = px(0.); - let mut line = 0usize; - let mut first_non_whitespace_ix = None; - let mut last_candidate_ix = 0usize; - let mut last_candidate_width = px(0.); - let mut last_wrap_ix = 0usize; - let mut prev_c = '\0'; - let mut indent: Option = None; - let mut truncate_ix = 0usize; - - for (ix, c) in text.char_indices() { - if c == '\n' { - if line >= max_lines - 1 && !text[ix + 1..].trim().is_empty() { - // Newline on the last allowed line with real content - // below. Truncate here. - let truncated = text[..truncate_ix] - .trim_end_matches(|c: char| c.is_whitespace() || c.is_ascii_punctuation()); - let result = SharedString::from(format!("{truncated}{truncation_affix}")); - let mut runs = runs.to_vec(); - update_runs_after_truncation( - &result, - truncation_affix, - &mut runs, - TruncateFrom::End, - ); - return (result, Cow::Owned(runs)); - } - - // Newline before the last line: it consumes a line. - line += 1; - width = px(0.); - first_non_whitespace_ix = None; - last_candidate_ix = 0; - last_candidate_width = px(0.); - last_wrap_ix = ix + 1; - prev_c = '\0'; - indent = None; - truncate_ix = ix + 1; - continue; - } - - let char_width = self.width_for_char(c); - - if Self::is_word_char(c) { - if prev_c == ' ' && first_non_whitespace_ix.is_some() { - last_candidate_ix = ix; - last_candidate_width = width; - } - } else if 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); - } - - width += char_width; - - if line < max_lines - 1 { - // Before the last line: replicate wrap_line's boundary logic. - if width > wrap_width && ix > last_wrap_ix { - if let (None, Some(first_nw)) = (indent, first_non_whitespace_ix) { - indent = Some(Self::MAX_INDENT.min((first_nw - last_wrap_ix) as u32)); - } - - if last_candidate_ix > last_wrap_ix { - last_wrap_ix = last_candidate_ix; - width -= last_candidate_width; - last_candidate_ix = 0; - } else { - last_wrap_ix = ix; - width = char_width; - } - - if let Some(ind) = indent { - width += self.width_for_char(' ') * ind as f32; - } - - line += 1; - truncate_ix = last_wrap_ix; - } - } else { - // On the last line: track the furthest point where the affix - // still fits, and stop as soon as the line overflows. - if width + affix_width <= wrap_width { - truncate_ix = ix + c.len_utf8(); - } - - if width > wrap_width { - let truncated = text[..truncate_ix] - .trim_end_matches(|c: char| c.is_whitespace() || c.is_ascii_punctuation()); - let result = SharedString::from(format!("{truncated}{truncation_affix}")); - let mut runs = runs.to_vec(); - update_runs_after_truncation( - &result, - truncation_affix, - &mut runs, - TruncateFrom::End, - ); - return (result, Cow::Owned(runs)); - } - } - - prev_c = c; - } - - // Text fits within max_lines without truncation. - (text, Cow::Borrowed(runs)) - } - - /// Any character in this list should be treated as a word character, - /// meaning it can be part of a word that should not be wrapped. - pub(crate) fn is_word_char(c: char) -> bool { - // ASCII alphanumeric characters, for English, numbers: `Hello123`, etc. - c.is_ascii_alphanumeric() || - // Latin script in Unicode for French, German, Spanish, etc. - // Latin-1 Supplement - // https://en.wikipedia.org/wiki/Latin-1_Supplement - matches!(c, '\u{00C0}'..='\u{00FF}') || - // Latin Extended-A - // https://en.wikipedia.org/wiki/Latin_Extended-A - matches!(c, '\u{0100}'..='\u{017F}') || - // Latin Extended-B - // https://en.wikipedia.org/wiki/Latin_Extended-B - matches!(c, '\u{0180}'..='\u{024F}') || - // Cyrillic for Russian, Ukrainian, etc. - // https://en.wikipedia.org/wiki/Cyrillic_script_in_Unicode - matches!(c, '\u{0400}'..='\u{04FF}') || - - // Vietnamese (https://vietunicode.sourceforge.net/charset/) - matches!(c, '\u{1E00}'..='\u{1EFF}') || // Latin Extended Additional - matches!(c, '\u{0300}'..='\u{036F}') || // Combining Diacritical Marks - - // Bengali (https://en.wikipedia.org/wiki/Bengali_(Unicode_block)) - matches!(c, '\u{0980}'..='\u{09FF}') || - - // Some other known special characters that should be treated as word characters, - // e.g. `a-b`, `var_name`, `I'm`/`won’t`, '@mention`, `#hashtag`, `100%`, `3.1415`, - // `2^3`, `a~b`, `a=1`, `Self::new`, etc. Trailing punctuation like `,`, `.`, `:`, `;` - // is included so it stays attached to the preceding word when wrapping. - matches!(c, '-' | '_' | '.' | '\'' | '’' | '‘' | '$' | '%' | '@' | '#' | '^' | '~' | ',' | '=' | ':' | ';') || - // Closing punctuation never starts a line (UAX #14 LB13: no break - // before `!`, `)`, `]`, `}`, closing quotes or an ellipsis) — `plz!`, - // `see)`, `quoted”` wrap as one word instead of orphaning the mark on - // the next line. `/` and `?` stay break opportunities so long paths - // and URLs (`a/b`, `foo?b=2`) can wrap. - matches!(c, '!' | ')' | ']' | '}' | '"' | '”' | '»' | '…') || - // `⋯` character is special used in Zed, to keep this at the end of the line. - matches!(c, '⋯') || - - // Non-breaking glue characters - matches!(c, '\u{202F}' | '\u{00A0}' | '\u{2011}') - } - - #[inline(always)] - fn width_for_char(&mut self, c: char) -> Pixels { - if (c as u32) < 128 { - if let Some(cached_width) = self.cached_ascii_char_widths[c as usize] { - cached_width - } else { - let width = self - .text_system - .layout_width(self.font_id, self.font_size, c); - self.cached_ascii_char_widths[c as usize] = Some(width); - width - } - } else if let Some(cached_width) = self.cached_other_char_widths.get(&c) { - *cached_width - } else { - let width = self - .text_system - .layout_width(self.font_id, self.font_size, c); - self.cached_other_char_widths.insert(c, width); - width - } - } -} - -fn update_runs_after_truncation( - result: &str, - ellipsis: &str, - runs: &mut Vec, - truncate_from: TruncateFrom, -) { - let mut truncate_at = result.len() - ellipsis.len(); - match truncate_from { - TruncateFrom::Start => { - for (run_index, run) in runs.iter_mut().enumerate().rev() { - if run.len <= truncate_at { - truncate_at -= run.len; - } else { - run.len = truncate_at + ellipsis.len(); - runs.splice(..run_index, std::iter::empty()); - break; - } - } - } - TruncateFrom::End => { - for (run_index, run) in runs.iter_mut().enumerate() { - if run.len <= truncate_at { - truncate_at -= run.len; - } else { - run.len = truncate_at + ellipsis.len(); - runs.truncate(run_index + 1); - break; - } - } - } - TruncateFrom::Middle => { - unreachable!("Middle truncation calls this function with TruncateFrom::End directly") - } - } -} - -fn update_runs_after_middle_truncation( - ellipsis: &str, - runs: &mut Vec, - front_end_ix: usize, - back_start_ix: usize, -) { - let original_runs = std::mem::take(runs); - let mut result_runs: Vec = Vec::with_capacity(original_runs.len()); - - // Front segment [0, front_end_ix) + ellipsis: walk forward until the run - // that straddles or ends at front_end_ix, then extend that run's length - // to include the ellipsis. - let mut front_remaining = front_end_ix; - let mut front_done = false; - for run in &original_runs { - if front_done { - break; - } - if run.len <= front_remaining { - result_runs.push(run.clone()); - front_remaining -= run.len; - } else { - let mut partial = run.clone(); - partial.len = front_remaining + ellipsis.len(); - result_runs.push(partial); - front_done = true; - } - } - if !front_done { - // front_end_ix landed exactly on a run boundary; append ellipsis to - // the last front run (or, if the front is empty, to the first back run). - if let Some(last) = result_runs.last_mut() { - last.len += ellipsis.len(); - } else if let Some(first) = original_runs.first() { - let mut affix_run = first.clone(); - affix_run.len = ellipsis.len(); - result_runs.push(affix_run); - } - } - - // Back segment [back_start_ix, original.len()): skip runs entirely in the - // removed middle, keep the rest. - let mut byte_pos = 0usize; - for run in &original_runs { - let run_end = byte_pos + run.len; - if run_end > back_start_ix { - if byte_pos < back_start_ix { - // Run straddles back_start_ix; keep only the tail. - let mut partial = run.clone(); - partial.len = run_end - back_start_ix; - result_runs.push(partial); - } else { - result_runs.push(run.clone()); - } - } - byte_pos = run_end; - } - - *runs = result_runs; -} - -/// A fragment of a line that can be wrapped. -pub enum LineFragment<'a> { - /// A text fragment consisting of characters. - Text { - /// The text content of the fragment. - text: &'a str, - }, - /// A non-text element with a fixed width. - Element { - /// The width of the element in pixels. - width: Pixels, - /// The UTF-8 encoded length of the element. - len_utf8: usize, - }, -} - -impl<'a> LineFragment<'a> { - /// Creates a new text fragment from the given text. - pub fn text(text: &'a str) -> Self { - LineFragment::Text { text } - } - - /// Creates a new non-text element with the given width and UTF-8 encoded length. - pub fn element(width: Pixels, len_utf8: usize) -> Self { - LineFragment::Element { width, len_utf8 } - } - - fn wrap_boundary_candidates(&self) -> impl Iterator { - let text = match self { - LineFragment::Text { text } => text, - LineFragment::Element { .. } => "\0", - }; - text.chars().map(move |character| { - if let LineFragment::Element { width, len_utf8 } = self { - WrapBoundaryCandidate::Element { - width: *width, - len_utf8: *len_utf8, - } - } else { - WrapBoundaryCandidate::Char { character } - } - }) - } -} - -enum WrapBoundaryCandidate { - Char { character: char }, - Element { width: Pixels, len_utf8: usize }, -} - -impl WrapBoundaryCandidate { - pub fn len_utf8(&self) -> usize { - match self { - WrapBoundaryCandidate::Char { character } => character.len_utf8(), - WrapBoundaryCandidate::Element { len_utf8: len, .. } => *len, - } - } -} - -/// A boundary between two lines of text. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Boundary { - /// The index of the last character in a line - pub ix: usize, - /// The indent of the next line. - pub next_indent: u32, -} - -impl Boundary { - fn new(ix: usize, next_indent: u32) -> Self { - Self { ix, next_indent } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{Font, FontFeatures, FontStyle, FontWeight, TestAppContext, TestDispatcher, font}; - #[cfg(target_os = "macos")] - use crate::{TextRun, WindowTextSystem, WrapBoundary}; - - fn build_wrapper() -> LineWrapper { - let dispatcher = TestDispatcher::new(0); - let cx = TestAppContext::build(dispatcher, None); - let id = cx.text_system().resolve_font(&font(".ZedMono")); - LineWrapper::new(id, px(16.), cx.text_system().clone()) - } - - fn generate_test_runs(input_run_len: &[usize]) -> Vec { - input_run_len - .iter() - .map(|run_len| TextRun { - len: *run_len, - font: Font { - family: "Dummy".into(), - features: FontFeatures::default(), - fallbacks: None, - weight: FontWeight::default(), - style: FontStyle::Normal, - }, - ..Default::default() - }) - .collect() - } - - #[test] - fn test_wrap_line() { - let mut wrapper = build_wrapper(); - - assert_eq!( - wrapper - .wrap_line(&[LineFragment::text("aa bbb cccc ddddd eeee")], px(72.)) - .collect::>(), - &[ - Boundary::new(7, 0), - Boundary::new(12, 0), - Boundary::new(18, 0) - ], - ); - assert_eq!( - wrapper - .wrap_line(&[LineFragment::text("aaa aaaaaaaaaaaaaaaaaa")], px(72.0)) - .collect::>(), - &[ - Boundary::new(4, 0), - Boundary::new(11, 0), - Boundary::new(18, 0) - ], - ); - assert_eq!( - wrapper - .wrap_line(&[LineFragment::text(" aaaaaaa")], px(72.)) - .collect::>(), - &[ - Boundary::new(7, 5), - Boundary::new(9, 5), - Boundary::new(11, 5), - ] - ); - assert_eq!( - wrapper - .wrap_line( - &[LineFragment::text(" ")], - px(72.) - ) - .collect::>(), - &[ - Boundary::new(7, 0), - Boundary::new(14, 0), - Boundary::new(21, 0) - ] - ); - assert_eq!( - wrapper - .wrap_line(&[LineFragment::text(" aaaaaaaaaaaaaa")], px(72.)) - .collect::>(), - &[ - Boundary::new(7, 0), - Boundary::new(14, 3), - Boundary::new(18, 3), - Boundary::new(22, 3), - ] - ); - - // Test wrapping multiple text fragments - assert_eq!( - wrapper - .wrap_line( - &[ - LineFragment::text("aa bbb "), - LineFragment::text("cccc ddddd eeee") - ], - px(72.) - ) - .collect::>(), - &[ - Boundary::new(7, 0), - Boundary::new(12, 0), - Boundary::new(18, 0) - ], - ); - - // Test wrapping with a mix of text and element fragments - assert_eq!( - wrapper - .wrap_line( - &[ - LineFragment::text("aa "), - LineFragment::element(px(20.), 1), - LineFragment::text(" bbb "), - LineFragment::element(px(30.), 1), - LineFragment::text(" cccc") - ], - px(72.) - ) - .collect::>(), - &[ - Boundary::new(5, 0), - Boundary::new(9, 0), - Boundary::new(11, 0) - ], - ); - - // Test with element at the beginning and text afterward - assert_eq!( - wrapper - .wrap_line( - &[ - LineFragment::element(px(50.), 1), - LineFragment::text(" aaaa bbbb cccc dddd") - ], - px(72.) - ) - .collect::>(), - &[ - Boundary::new(2, 0), - Boundary::new(7, 0), - Boundary::new(12, 0), - Boundary::new(17, 0) - ], - ); - - // Test with a large element that forces wrapping by itself - assert_eq!( - wrapper - .wrap_line( - &[ - LineFragment::text("short text "), - LineFragment::element(px(100.), 1), - LineFragment::text(" more text") - ], - px(72.) - ) - .collect::>(), - &[ - Boundary::new(6, 0), - Boundary::new(11, 0), - Boundary::new(12, 0), - Boundary::new(18, 0) - ], - ); - - // Test with non-breaking glue characters - assert_eq!( - wrapper - .wrap_line( - &[LineFragment::text("a\u{202F}b\u{00A0}c\u{2011}d e")], - px(72.0) - ) - .collect::>(), - &[Boundary::new(12, 0),], // special chars above take up 3, 2 and 3 bytes, so boundary ends up at 12 - ); - } - - #[test] - fn test_truncate_line_end() { - let mut wrapper = build_wrapper(); - - fn perform_test( - wrapper: &mut LineWrapper, - text: &'static str, - expected: &'static str, - ellipsis: &str, - ) { - let dummy_run_lens = vec![text.len()]; - let dummy_runs = generate_test_runs(&dummy_run_lens); - let (result, dummy_runs) = wrapper.truncate_line( - text.into(), - px(220.), - ellipsis, - &dummy_runs, - TruncateFrom::End, - ); - assert_eq!(result, expected); - assert_eq!(dummy_runs.first().unwrap().len, result.len()); - } - - perform_test( - &mut wrapper, - "aa bbb cccc ddddd eeee ffff gggg", - "aa bbb cccc ddddd eeee", - "", - ); - perform_test( - &mut wrapper, - "aa bbb cccc ddddd eeee ffff gggg", - "aa bbb cccc ddddd eee…", - "…", - ); - perform_test( - &mut wrapper, - "aa bbb cccc ddddd eeee ffff gggg", - "aa bbb cccc dddd......", - "......", - ); - perform_test( - &mut wrapper, - "aa bbb cccc 🦀🦀🦀🦀🦀 eeee ffff gggg", - "aa bbb cccc 🦀🦀🦀🦀…", - "…", - ); - } - - #[test] - fn test_truncate_line_start() { - let mut wrapper = build_wrapper(); - - #[track_caller] - fn perform_test( - wrapper: &mut LineWrapper, - text: &'static str, - expected: &'static str, - ellipsis: &str, - ) { - let dummy_run_lens = vec![text.len()]; - let dummy_runs = generate_test_runs(&dummy_run_lens); - let (result, dummy_runs) = wrapper.truncate_line( - text.into(), - px(220.), - ellipsis, - &dummy_runs, - TruncateFrom::Start, - ); - assert_eq!(result, expected); - assert_eq!(dummy_runs.first().unwrap().len, result.len()); - } - - perform_test( - &mut wrapper, - "aaaa bbbb cccc ddddd eeee fff gg", - "cccc ddddd eeee fff gg", - "", - ); - perform_test( - &mut wrapper, - "aaaa bbbb cccc ddddd eeee fff gg", - "…ccc ddddd eeee fff gg", - "…", - ); - perform_test( - &mut wrapper, - "aaaa bbbb cccc ddddd eeee fff gg", - "......dddd eeee fff gg", - "......", - ); - perform_test( - &mut wrapper, - "aaaa bbbb cccc 🦀🦀🦀🦀🦀 eeee fff gg", - "…🦀🦀🦀🦀 eeee fff gg", - "…", - ); - } - - #[test] - fn test_truncate_multiple_runs_end() { - let mut wrapper = build_wrapper(); - - fn perform_test( - wrapper: &mut LineWrapper, - text: &'static str, - expected: &str, - run_lens: &[usize], - result_run_len: &[usize], - line_width: Pixels, - ) { - let dummy_runs = generate_test_runs(run_lens); - let (result, dummy_runs) = - wrapper.truncate_line(text.into(), line_width, "…", &dummy_runs, TruncateFrom::End); - assert_eq!(result, expected); - for (run, result_len) in dummy_runs.iter().zip(result_run_len) { - assert_eq!(run.len, *result_len); - } - } - // Case 0: Normal - // Text: abcdefghijkl - // Runs: Run0 { len: 12, ... } - // - // Truncate res: abcd… (truncate_at = 4) - // Run res: Run0 { string: abcd…, len: 7, ... } - perform_test(&mut wrapper, "abcdefghijkl", "abcd…", &[12], &[7], px(50.)); - // Case 1: Drop some runs - // Text: abcdefghijkl - // Runs: Run0 { len: 4, ... }, Run1 { len: 4, ... }, Run2 { len: 4, ... } - // - // Truncate res: abcdef… (truncate_at = 6) - // Runs res: Run0 { string: abcd, len: 4, ... }, Run1 { string: ef…, len: - // 5, ... } - perform_test( - &mut wrapper, - "abcdefghijkl", - "abcdef…", - &[4, 4, 4], - &[4, 5], - px(70.), - ); - // Case 2: Truncate at start of some run - // Text: abcdefghijkl - // Runs: Run0 { len: 4, ... }, Run1 { len: 4, ... }, Run2 { len: 4, ... } - // - // Truncate res: abcdefgh… (truncate_at = 8) - // Runs res: Run0 { string: abcd, len: 4, ... }, Run1 { string: efgh, len: - // 4, ... }, Run2 { string: …, len: 3, ... } - perform_test( - &mut wrapper, - "abcdefghijkl", - "abcdefgh…", - &[4, 4, 4], - &[4, 4, 3], - px(90.), - ); - } - - #[test] - fn test_truncate_multiple_runs_start() { - let mut wrapper = build_wrapper(); - - #[track_caller] - fn perform_test( - wrapper: &mut LineWrapper, - text: &'static str, - expected: &str, - run_lens: &[usize], - result_run_len: &[usize], - line_width: Pixels, - ) { - let dummy_runs = generate_test_runs(run_lens); - let (result, dummy_runs) = wrapper.truncate_line( - text.into(), - line_width, - "…", - &dummy_runs, - TruncateFrom::Start, - ); - assert_eq!(result, expected); - for (run, result_len) in dummy_runs.iter().zip(result_run_len) { - assert_eq!(run.len, *result_len); - } - } - // Case 0: Normal - // Text: abcdefghijkl - // Runs: Run0 { len: 12, ... } - // - // Truncate res: …ijkl (truncate_at = 9) - // Run res: Run0 { string: …ijkl, len: 7, ... } - perform_test(&mut wrapper, "abcdefghijkl", "…ijkl", &[12], &[7], px(50.)); - // Case 1: Drop some runs - // Text: abcdefghijkl - // Runs: Run0 { len: 4, ... }, Run1 { len: 4, ... }, Run2 { len: 4, ... } - // - // Truncate res: …ghijkl (truncate_at = 7) - // Runs res: Run0 { string: …gh, len: 5, ... }, Run1 { string: ijkl, len: - // 4, ... } - perform_test( - &mut wrapper, - "abcdefghijkl", - "…ghijkl", - &[4, 4, 4], - &[5, 4], - px(70.), - ); - // Case 2: Truncate at start of some run - // Text: abcdefghijkl - // Runs: Run0 { len: 4, ... }, Run1 { len: 4, ... }, Run2 { len: 4, ... } - // - // Truncate res: abcdefgh… (truncate_at = 3) - // Runs res: Run0 { string: …, len: 3, ... }, Run1 { string: efgh, len: - // 4, ... }, Run2 { string: ijkl, len: 4, ... } - perform_test( - &mut wrapper, - "abcdefghijkl", - "…efghijkl", - &[4, 4, 4], - &[3, 4, 4], - px(90.), - ); - } - - #[test] - fn test_update_run_after_truncation_end() { - fn perform_test(result: &str, run_lens: &[usize], result_run_lens: &[usize]) { - let mut dummy_runs = generate_test_runs(run_lens); - update_runs_after_truncation(result, "…", &mut dummy_runs, TruncateFrom::End); - for (run, result_len) in dummy_runs.iter().zip(result_run_lens) { - assert_eq!(run.len, *result_len); - } - } - // Case 0: Normal - // Text: abcdefghijkl - // Runs: Run0 { len: 12, ... } - // - // Truncate res: abcd… (truncate_at = 4) - // Run res: Run0 { string: abcd…, len: 7, ... } - perform_test("abcd…", &[12], &[7]); - // Case 1: Drop some runs - // Text: abcdefghijkl - // Runs: Run0 { len: 4, ... }, Run1 { len: 4, ... }, Run2 { len: 4, ... } - // - // Truncate res: abcdef… (truncate_at = 6) - // Runs res: Run0 { string: abcd, len: 4, ... }, Run1 { string: ef…, len: - // 5, ... } - perform_test("abcdef…", &[4, 4, 4], &[4, 5]); - // Case 2: Truncate at start of some run - // Text: abcdefghijkl - // Runs: Run0 { len: 4, ... }, Run1 { len: 4, ... }, Run2 { len: 4, ... } - // - // Truncate res: abcdefgh… (truncate_at = 8) - // Runs res: Run0 { string: abcd, len: 4, ... }, Run1 { string: efgh, len: - // 4, ... }, Run2 { string: …, len: 3, ... } - perform_test("abcdefgh…", &[4, 4, 4], &[4, 4, 3]); - } - - #[test] - fn test_is_word_char() { - #[track_caller] - fn assert_word(word: &str) { - for c in word.chars() { - assert!( - LineWrapper::is_word_char(c), - "assertion failed for '{}' (unicode 0x{:x})", - c, - c as u32 - ); - } - } - - #[track_caller] - fn assert_not_word(word: &str) { - let found = word.chars().any(|c| !LineWrapper::is_word_char(c)); - assert!(found, "assertion failed for '{}'", word); - } - - assert_word("Hello123"); - assert_word("non-English"); - assert_word("var_name"); - assert_word("123456"); - assert_word("3.1415"); - assert_word("10^2"); - assert_word("1~2"); - assert_word("100%"); - assert_word("@mention"); - assert_word("#hashtag"); - assert_word("$variable"); - assert_word("a=1"); - assert_word("Self::is_word_char"); - assert_word("on;"); - assert_word("more⋯"); - assert_word("won’t"); - assert_word("‘twas"); - assert_word("plz!"); - assert_word("see)"); - assert_word("quoted”"); - assert_word("well…"); - - // Space - assert_not_word("foo bar"); - - // URL case - assert_word("github.com"); - assert_not_word("zed-industries/zed"); - assert_not_word("zed-industries\\zed"); - assert_not_word("a=1&b=2"); - assert_not_word("foo?b=2"); - - // Latin-1 Supplement - assert_word("ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ"); - // Latin Extended-A - assert_word("ĀāĂ㥹ĆćĈĉĊċČčĎď"); - // Latin Extended-B - assert_word("ƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏ"); - // Cyrillic - assert_word("АБВГДЕЖЗИЙКЛМНОП"); - // Vietnamese (https://github.com/zed-industries/zed/issues/23245) - assert_word("ThậmchíđếnkhithuachạychúngcònnhẫntâmgiếtnốtsốđôngtùchínhtrịởYênBáivàCaoBằng"); - // Bengali - assert_word("গিয়েছিলেন"); - assert_word("ছেলে"); - assert_word("হচ্ছিল"); - - // non-word characters - assert_not_word("你好"); - assert_not_word("안녕하세요"); - assert_not_word("こんにちは"); - assert_not_word("😀😁😂"); - assert_not_word("()[]{}<>"); - - // Non-breaking ("Glue") characters, see https://www.unicode.org/reports/tr14/ - // (https://github.com/zed-industries/zed/issues/59664) - assert_word("\u{202F}"); // NNBSP " " - assert_word("\u{00A0}"); // NBSP " " - assert_word("\u{2011}"); // NBH "‑" - } - - // For compatibility with the test macro - #[cfg(target_os = "macos")] - use crate as gpui; - - // These seem to vary wildly based on the text system. - #[cfg(target_os = "macos")] - #[crate::test] - fn test_wrap_shaped_line(cx: &mut TestAppContext) { - cx.update(|cx| { - let text_system = WindowTextSystem::new(cx.text_system().clone()); - - let normal = TextRun { - len: 0, - font: font("Helvetica"), - color: Default::default(), - underline: Default::default(), - ..Default::default() - }; - let bold = TextRun { - len: 0, - font: font("Helvetica").bold(), - ..Default::default() - }; - - let text = "aa bbb cccc ddddd eeee".into(); - let lines = text_system - .shape_text( - text, - px(16.), - &[ - normal.with_len(4), - bold.with_len(5), - normal.with_len(6), - bold.with_len(1), - normal.with_len(7), - ], - Some(px(72.)), - None, - ) - .unwrap(); - - assert_eq!( - lines[0].layout.wrap_boundaries(), - &[ - WrapBoundary { - run_ix: 0, - glyph_ix: 7 - }, - WrapBoundary { - run_ix: 0, - glyph_ix: 12 - }, - WrapBoundary { - run_ix: 0, - glyph_ix: 18 - } - ], - ); - }); - } - - #[test] - fn test_multiline_truncation_fits_within_wrapped_lines() { - let mut wrapper = build_wrapper(); - - // With .ZedMono at 16px, each char is 9.6px wide. - // wrap_width = 72px fits ~7 chars per line. - // - // "aa bbbbbb cccccc dddddd eeee ffff" with wrap_width=72px wraps as: - // Line 1: "aa " (28.8px, wraps because "bbbbbb" won't fit) - // Line 2: "bbbbbb " (67.2px) - // Line 3: "cccccc " (67.2px) - // ... - // - // truncate_wrapped_line should wrap first to find line 2 starts at - // "bbbbbb...", then truncate only that line to fit with ellipsis. - let text: &str = "aa bbbbbb cccccc dddddd eeee ffff"; - let wrap_width = px(72.); - let max_lines: usize = 2; - - let runs = generate_test_runs(&[text.len()]); - let (truncated, _) = wrapper.truncate_wrapped_line( - text.into(), - wrap_width, - max_lines, - "\u{2026}", - &runs, - TruncateFrom::End, - ); - - // The truncated text, when wrapped, must fit within max_lines lines. - let wrap_count = wrapper - .wrap_line(&[LineFragment::text(&truncated)], wrap_width) - .count(); - - assert!( - wrap_count < max_lines, - "Truncated text '{}' wraps into {} visual lines, expected at most {}", - truncated, - wrap_count + 1, - max_lines - ); - - // The truncated text should end with the ellipsis. - assert!( - truncated.ends_with('\u{2026}'), - "Truncated text '{}' should end with ellipsis", - truncated - ); - } - - #[test] - fn test_multiline_truncation_no_truncation_needed() { - let mut wrapper = build_wrapper(); - - // Text that fits in 2 lines shouldn't be truncated. - // Line 1: "aa bbb " (67.2px), Line 2: "cccccc" (57.6px) - let text: &str = "aa bbb cccccc"; - let wrap_width = px(72.); - let max_lines: usize = 2; - - let runs = generate_test_runs(&[text.len()]); - let (result, _) = wrapper.truncate_wrapped_line( - text.into(), - wrap_width, - max_lines, - "\u{2026}", - &runs, - TruncateFrom::End, - ); - - assert_eq!( - result.as_ref(), - text, - "Text that fits should not be modified" - ); - } - - #[test] - fn test_multiline_truncation_three_lines() { - let mut wrapper = build_wrapper(); - - let text: &str = "aa bbb cccc ddddd eeee ffff gggg hhhh iiii jjjj"; - let wrap_width = px(72.); - let max_lines: usize = 3; - - let runs = generate_test_runs(&[text.len()]); - let (truncated, _) = wrapper.truncate_wrapped_line( - text.into(), - wrap_width, - max_lines, - "\u{2026}", - &runs, - TruncateFrom::End, - ); - - let wrap_count = wrapper - .wrap_line(&[LineFragment::text(&truncated)], wrap_width) - .count(); - - assert!( - wrap_count < max_lines, - "Truncated text '{}' wraps into {} visual lines, expected at most {}", - truncated, - wrap_count + 1, - max_lines - ); - - assert!( - truncated.ends_with('\u{2026}'), - "Truncated text '{}' should end with ellipsis", - truncated - ); - } - - #[test] - fn test_multiline_truncation_with_newlines() { - let mut wrapper = build_wrapper(); - - // "hello\nworld foo bar baz" with line_clamp(2): - // shape_text splits on \n, giving physical lines "hello" and - // "world foo bar baz". The newline consumes line 1, so the - // second physical line should be truncated on line 2. - let text: &str = "hello\nworld foo bar baz"; - let wrap_width = px(72.); - let max_lines: usize = 2; - - let runs = generate_test_runs(&[text.len()]); - let (truncated, _) = wrapper.truncate_wrapped_line( - text.into(), - wrap_width, - max_lines, - "\u{2026}", - &runs, - TruncateFrom::End, - ); - - // The newline should be preserved. - let parts: Vec<&str> = truncated.splitn(2, '\n').collect(); - assert_eq!( - parts.len(), - 2, - "Newline should be preserved: '{}'", - truncated - ); - assert_eq!(parts[0], "hello"); - - // The second line should fit within wrap_width and end with ellipsis. - let second_line_width: Pixels = parts[1].chars().map(|c| wrapper.width_for_char(c)).sum(); - assert!( - second_line_width <= wrap_width, - "Second line '{}' ({}px) exceeds wrap_width ({}px)", - parts[1], - second_line_width, - wrap_width - ); - assert!( - truncated.ends_with('\u{2026}'), - "Should end with ellipsis: '{}'", - truncated - ); - } - - #[test] - fn test_multiline_truncation_newline_on_last_line() { - let mut wrapper = build_wrapper(); - - // "hello\nworld\nmore" with line_clamp(2): - // Line 1: "hello", Line 2: "world" — but there's a third line, - // so line 2 should be truncated with ellipsis. - let text: &str = "hello\nworld\nmore"; - let wrap_width = px(72.); - let max_lines: usize = 2; - - let runs = generate_test_runs(&[text.len()]); - let (truncated, _) = wrapper.truncate_wrapped_line( - text.into(), - wrap_width, - max_lines, - "\u{2026}", - &runs, - TruncateFrom::End, - ); - - let parts: Vec<&str> = truncated.splitn(2, '\n').collect(); - assert_eq!(parts[0], "hello"); - assert!( - truncated.ends_with('\u{2026}'), - "Should end with ellipsis since there's more content: '{}'", - truncated - ); - } - - #[test] - fn test_truncate_line_middle() { - let mut wrapper = build_wrapper(); - - // No truncation when text fits within a very wide budget. - let short_text = "hello world"; - let runs = generate_test_runs(&[short_text.len()]); - let (result, result_runs) = wrapper.truncate_line( - short_text.into(), - px(10000.), - "…", - &runs, - TruncateFrom::Middle, - ); - assert_eq!(result.as_ref(), short_text); - assert_eq!(result_runs.len(), 1); - assert_eq!(result_runs[0].len, short_text.len()); - - // Basic middle truncation: long string with px(100.) budget. - let long_text = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz"; - let runs = generate_test_runs(&[long_text.len()]); - let (result, _result_runs) = - wrapper.truncate_line(long_text.into(), px(100.), "…", &runs, TruncateFrom::Middle); - assert!( - result.contains('…'), - "Middle-truncated result should contain '…', got: '{}'", - result - ); - assert!( - result.chars().count() < long_text.chars().count(), - "Middle-truncated result should be shorter than original" - ); - assert_eq!( - result.chars().next(), - long_text.chars().next(), - "Result should start with the same first character as original" - ); - assert_eq!( - result.chars().last(), - long_text.chars().last(), - "Result should end with the same last character as original" - ); - - // Degenerate case: budget so narrow that middle truncation cannot find a valid split. - // Still show the truncation affix instead of returning the original overflowing text. - let text = "abcdef"; - let runs = generate_test_runs(&[text.len()]); - let (result, result_runs) = - wrapper.truncate_line(text.into(), px(1.), "…", &runs, TruncateFrom::Middle); - assert_eq!(result.as_ref(), "…"); - assert_eq!(result_runs.len(), 1); - assert_eq!(result_runs[0].len, "…".len()); - - // Run adjustment correctness: multiple runs across the string. - // Verify that the returned runs' lengths sum to result.len(). - let multi_run_text = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz"; - let run_lens = [20, 20, multi_run_text.len() - 40]; - let runs = generate_test_runs(&run_lens); - let (result, result_runs) = wrapper.truncate_line( - multi_run_text.into(), - px(100.), - "…", - &runs, - TruncateFrom::Middle, - ); - let total_run_len: usize = result_runs.iter().map(|r| r.len).sum(); - assert_eq!( - total_run_len, - result.len(), - "Sum of run lengths ({}) should equal result byte length ({})", - total_run_len, - result.len() - ); - } - - #[test] - fn test_multiline_truncation_trailing_newline() { - let mut wrapper = build_wrapper(); - - // "hello\nworld\n" with line_clamp(2): - // The trailing newline has no content after it, so no ellipsis. - let text: &str = "hello\nworld\n"; - let wrap_width = px(72.); - let max_lines: usize = 2; - - let runs = generate_test_runs(&[text.len()]); - let (result, _) = wrapper.truncate_wrapped_line( - text.into(), - wrap_width, - max_lines, - "\u{2026}", - &runs, - TruncateFrom::End, - ); - - assert!( - !result.ends_with('\u{2026}'), - "Trailing newline with no content should not add ellipsis: '{}'", - result - ); - } - - #[test] - fn test_multiline_truncation_newline_fits_exactly() { - let mut wrapper = build_wrapper(); - - // "hello\nworld" with line_clamp(2): - // Exactly 2 lines, no truncation needed. - let text: &str = "hello\nworld"; - let wrap_width = px(72.); - let max_lines: usize = 2; - - let runs = generate_test_runs(&[text.len()]); - let (result, _) = wrapper.truncate_wrapped_line( - text.into(), - wrap_width, - max_lines, - "\u{2026}", - &runs, - TruncateFrom::End, - ); - - assert_eq!( - result.as_ref(), - text, - "Text that fits exactly should not be modified: '{}'", - result - ); - } -} diff --git a/crates/gpui_pre/src/util.rs b/crates/gpui_pre/src/util.rs deleted file mode 100644 index 13ee752d..00000000 --- a/crates/gpui_pre/src/util.rs +++ /dev/null @@ -1,252 +0,0 @@ -use crate::{BackgroundExecutor, Task}; -use std::{ - future::Future, - pin::Pin, - sync::atomic::{AtomicUsize, Ordering::SeqCst}, - task, - time::Duration, -}; - -/// A helper trait for building complex objects with imperative conditionals in a fluent style. -pub trait FluentBuilder { - /// Imperatively modify self with the given closure. - fn map(self, f: impl FnOnce(Self) -> U) -> U - where - Self: Sized, - { - f(self) - } - - /// Conditionally modify self with the given closure. - fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self - where - Self: Sized, - { - self.map(|this| if condition { then(this) } else { this }) - } - - /// Conditionally modify self with the given closure. - fn when_else( - self, - condition: bool, - then: impl FnOnce(Self) -> Self, - else_fn: impl FnOnce(Self) -> Self, - ) -> Self - where - Self: Sized, - { - self.map(|this| if condition { then(this) } else { else_fn(this) }) - } - - /// Conditionally unwrap and modify self with the given closure, if the given option is Some. - fn when_some(self, option: Option, then: impl FnOnce(Self, T) -> Self) -> Self - where - Self: Sized, - { - self.map(|this| { - if let Some(value) = option { - then(this, value) - } else { - this - } - }) - } - /// Conditionally unwrap and modify self with the given closure, if the given option is None. - fn when_none(self, option: &Option, then: impl FnOnce(Self) -> Self) -> Self - where - Self: Sized, - { - self.map(|this| if option.is_some() { this } else { then(this) }) - } -} - -/// Extensions for Future types that provide additional combinators and utilities. -pub trait FutureExt { - /// Requires a Future to complete before the specified duration has elapsed. - /// Similar to tokio::timeout. - fn with_timeout(self, timeout: Duration, executor: &BackgroundExecutor) -> WithTimeout - where - Self: Sized; -} - -impl FutureExt for T { - fn with_timeout(self, timeout: Duration, executor: &BackgroundExecutor) -> WithTimeout - where - Self: Sized, - { - WithTimeout { - future: self, - timer: executor.timer(timeout), - } - } -} - -#[pin_project::pin_project] -pub struct WithTimeout { - #[pin] - future: T, - #[pin] - timer: Task<()>, -} - -#[derive(Debug, thiserror::Error)] -#[error("Timed out before future resolved")] -/// Error returned by with_timeout when the timeout duration elapsed before the future resolved -pub struct Timeout; - -impl Future for WithTimeout { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut task::Context) -> task::Poll { - let this = self.project(); - - if let task::Poll::Ready(output) = this.future.poll(cx) { - task::Poll::Ready(Ok(output)) - } else if this.timer.poll(cx).is_ready() { - task::Poll::Ready(Err(Timeout)) - } else { - task::Poll::Pending - } - } -} - -/// Increment the given atomic counter if it is not zero. -/// Return the new value of the counter. -pub(crate) fn atomic_incr_if_not_zero(counter: &AtomicUsize) -> usize { - let mut loaded = counter.load(SeqCst); - loop { - if loaded == 0 { - return 0; - } - match counter.compare_exchange_weak(loaded, loaded + 1, SeqCst, SeqCst) { - Ok(x) => return x + 1, - Err(actual) => loaded = actual, - } - } -} - -/// Rounds to the nearest integer with 0.5 ties toward zero. -#[inline] -pub(crate) fn round_half_toward_zero(value: f32) -> f32 { - (value.abs() - 0.5).ceil().copysign(value) -} - -#[inline] -pub(crate) fn round_half_toward_zero_f64(value: f64) -> f64 { - (value.abs() - 0.5).ceil().copysign(value) -} - -#[inline] -pub(crate) fn round_to_device_pixel(logical: f32, scale_factor: f32) -> f32 { - round_half_toward_zero(logical * scale_factor) -} - -#[inline] -pub(crate) fn round_stroke_to_device_pixel(logical: f32, scale_factor: f32) -> f32 { - if logical == 0.0 { - 0.0 - } else { - round_to_device_pixel(logical.max(0.0), scale_factor).max(1.0) - } -} - -#[inline] -pub(crate) fn floor_to_device_pixel(logical: f32, scale_factor: f32) -> f32 { - (logical * scale_factor).floor() -} - -#[inline] -pub(crate) fn ceil_to_device_pixel(logical: f32, scale_factor: f32) -> f32 { - (logical * scale_factor).ceil() -} - -#[cfg(test)] -mod tests { - use crate::TestAppContext; - - use super::*; - - #[test] - fn test_round_half_toward_zero() { - // Midpoint ties go toward zero - assert_eq!(round_half_toward_zero(0.5), 0.0); - assert_eq!(round_half_toward_zero(1.5), 1.0); - assert_eq!(round_half_toward_zero(2.5), 2.0); - assert_eq!(round_half_toward_zero(-0.5), 0.0); - assert_eq!(round_half_toward_zero(-1.5), -1.0); - assert_eq!(round_half_toward_zero(-2.5), -2.0); - - // Non-midpoint values round to nearest - assert_eq!(round_half_toward_zero(1.5001), 2.0); - assert_eq!(round_half_toward_zero(1.4999), 1.0); - assert_eq!(round_half_toward_zero(-1.5001), -2.0); - assert_eq!(round_half_toward_zero(-1.4999), -1.0); - - // Integers are unchanged - assert_eq!(round_half_toward_zero(0.0), 0.0); - assert_eq!(round_half_toward_zero(3.0), 3.0); - assert_eq!(round_half_toward_zero(-3.0), -3.0); - } - - #[test] - fn test_device_pixel_helpers() { - // Snap uses half-toward-zero: 1.0 * 1.5 = 1.5 ties toward 1.0. - assert_eq!(round_to_device_pixel(1.0, 1.5), 1.0); - // Below the tie rounds down, above rounds up. - assert_eq!(round_to_device_pixel(0.3, 2.0), 1.0); - assert_eq!(round_to_device_pixel(1.4, 1.0), 1.0); - assert_eq!(round_to_device_pixel(1.6, 1.0), 2.0); - - // Stroke uses snap, but clamps non-zero input up to at least 1dp. - assert_eq!(round_stroke_to_device_pixel(0.0, 1.0), 0.0); - assert_eq!(round_stroke_to_device_pixel(0.4, 1.0), 1.0); - assert_eq!(round_stroke_to_device_pixel(0.5, 1.0), 1.0); - assert_eq!(round_stroke_to_device_pixel(1.0, 1.5), 1.0); - assert_eq!(round_stroke_to_device_pixel(1.6, 1.0), 2.0); - - // Cover's near edge floors, far edge ceils. Together they form a strict superset. - assert_eq!(floor_to_device_pixel(0.3, 2.0), 0.0); - assert_eq!(ceil_to_device_pixel(0.3, 2.0), 1.0); - assert_eq!(floor_to_device_pixel(2.1, 1.0), 2.0); - assert_eq!(ceil_to_device_pixel(2.1, 1.0), 3.0); - - // Integer device-pixel inputs are stable under all three. - assert_eq!(round_to_device_pixel(2.0, 2.0), 4.0); - assert_eq!(floor_to_device_pixel(2.0, 2.0), 4.0); - assert_eq!(ceil_to_device_pixel(2.0, 2.0), 4.0); - } - - #[test] - fn test_round_half_toward_zero_f64() { - assert_eq!(round_half_toward_zero_f64(0.5), 0.0); - assert_eq!(round_half_toward_zero_f64(-0.5), 0.0); - assert_eq!(round_half_toward_zero_f64(1.5), 1.0); - assert_eq!(round_half_toward_zero_f64(-1.5), -1.0); - assert_eq!(round_half_toward_zero_f64(2.5001), 3.0); - } - - #[gpui::test] - async fn test_with_timeout(cx: &mut TestAppContext) { - Task::ready(()) - .with_timeout(Duration::from_secs(1), &cx.executor()) - .await - .expect("Timeout should be noop"); - - let long_duration = Duration::from_secs(6000); - let short_duration = Duration::from_secs(1); - cx.executor() - .timer(long_duration) - .with_timeout(short_duration, &cx.executor()) - .await - .expect_err("timeout should have triggered"); - - let fut = cx - .executor() - .timer(long_duration) - .with_timeout(short_duration, &cx.executor()); - cx.executor().advance_clock(short_duration * 2); - futures::FutureExt::now_or_never(fut) - .unwrap_or_else(|| panic!("timeout should have triggered")) - .expect_err("timeout"); - } -} diff --git a/crates/gpui_pre/src/view.rs b/crates/gpui_pre/src/view.rs deleted file mode 100644 index d2628d35..00000000 --- a/crates/gpui_pre/src/view.rs +++ /dev/null @@ -1,507 +0,0 @@ -use crate::{ - AnyElement, AnyEntity, AnyWeakEntity, App, Bounds, Context, Element, ElementId, Entity, - EntityId, GlobalElementId, InspectorElementId, IntoElement, LayoutId, PaintIndex, Pixels, - PrepaintStateIndex, Render, RenderOnce, Style, StyleRefinement, TextStyle, WeakEntity, -}; -use crate::{Empty, Window}; -use anyhow::Result; -use collections::FxHashSet; -use refineable::Refineable; -use std::mem; -use std::{any::TypeId, fmt, ops::Range}; - -/// A dynamically-typed view handle that can be downcast to a specific `Entity`. -/// -/// This is the type-erased counterpart to [`ViewElement`]: it holds an entity plus -/// a function pointer to its render, and is itself a [`View`], so embedding it as an -/// element goes through the same [`ViewElement`] machinery as any other view. -#[derive(Clone, Debug)] -pub struct AnyView { - entity: AnyEntity, - render: fn(&AnyView, &mut Window, &mut App) -> AnyElement, -} - -impl From> for AnyView { - fn from(value: Entity) -> Self { - AnyView { - entity: value.into_any(), - render: any_view::render::, - } - } -} - -impl AnyView { - /// Embed this view as a cached [`ViewElement`] laid out at `style`. - /// - /// The rendered subtree is recycled from the previous frame unless - /// [Context::notify] was called on the backing entity since it was rendered - /// (or [Window::refresh] is called, which ignores caching). - pub fn cached(self, style: StyleRefinement) -> ViewElement { - ViewElement::new(self).cached(style) - } - - /// Convert this to a weak handle. - pub fn downgrade(&self) -> AnyWeakView { - AnyWeakView { - entity: self.entity.downgrade(), - render: self.render, - } - } - - /// Convert this to a [Entity] of a specific type. - /// If this handle does not contain a view of the specified type, returns itself in an `Err` variant. - pub fn downcast(self) -> Result, Self> { - match self.entity.downcast() { - Ok(entity) => Ok(entity), - Err(entity) => Err(Self { - entity, - render: self.render, - }), - } - } - - /// Gets the [TypeId] of the underlying view. - pub fn entity_type(&self) -> TypeId { - self.entity.entity_type - } - - /// The [`EntityId`] of this view. - pub fn entity_id(&self) -> EntityId { - self.entity.entity_id() - } -} - -impl PartialEq for AnyView { - fn eq(&self, other: &Self) -> bool { - self.entity == other.entity - } -} - -impl Eq for AnyView {} - -/// `AnyView` is the type-erased [`View`]: its `render` is a function pointer rather -/// than a concrete type, but it participates in the reactive graph exactly like any -/// other view via [`ViewElement`]. -impl View for AnyView { - fn entity_id(&self) -> Option { - Some(self.entity.entity_id()) - } - - fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { - (self.render)(&self, window, cx) - } -} - -impl IntoElement for Entity { - type Element = ViewElement>; - - fn into_element(self) -> Self::Element { - ViewElement::new(self) - } -} - -impl IntoElement for AnyView { - type Element = ViewElement; - - fn into_element(self) -> Self::Element { - ViewElement::new(self) - } -} - -/// A weak, dynamically-typed view handle. -pub struct AnyWeakView { - entity: AnyWeakEntity, - render: fn(&AnyView, &mut Window, &mut App) -> AnyElement, -} - -impl AnyWeakView { - /// Upgrade to a strong `AnyView` handle, if the view is still alive. - pub fn upgrade(&self) -> Option { - let entity = self.entity.upgrade()?; - Some(AnyView { - entity, - render: self.render, - }) - } -} - -impl From> for AnyWeakView { - fn from(view: WeakEntity) -> Self { - AnyWeakView { - entity: view.into(), - render: any_view::render::, - } - } -} - -impl PartialEq for AnyWeakView { - fn eq(&self, other: &Self) -> bool { - self.entity == other.entity - } -} - -impl std::fmt::Debug for AnyWeakView { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AnyWeakView") - .field("entity_id", &self.entity.entity_id) - .finish_non_exhaustive() - } -} - -mod any_view { - use crate::{AnyElement, AnyView, App, IntoElement, Render, Window}; - - pub(crate) fn render( - view: &AnyView, - window: &mut Window, - cx: &mut App, - ) -> AnyElement { - let view = view.clone().downcast::().unwrap(); - // Record the view's Render type name so the accessibility debug dump can - // attribute nodes to the view that produced them. - #[cfg(debug_assertions)] - window - .a11y - .view_type_names - .insert(view.entity_id(), std::any::type_name::()); - view.update(cx, |view, cx| view.render(window, cx).into_any_element()) - } -} - -/// A renderable that participates in GPUI's reactive graph — the unifying model -/// behind [`Render`] and [`RenderOnce`]. -/// -/// When `entity_id()` returns `Some`, that id becomes the view's identity: it gets -/// a unique element-id space (so internal `use_state` / `.id(..)` never collide -/// across siblings) and `cx.notify()` on that entity re-renders only this view's -/// subtree. `None` behaves like a stateless component. -/// -/// You rarely implement `View` directly. `Entity` and any `T: RenderOnce` -/// get a blanket impl below; implement it by hand only when a component needs both -/// parent-supplied props *and* a backing entity for identity. -pub trait View: 'static + Sized { - /// This view's identity, if it has one. A view typically holds the backing - /// entity as a field and returns its [`EntityId`] here. - /// - /// The id becomes this view's [`ElementId`], so two views keyed on the same - /// entity must not be rendered at the same position in the element tree - /// (e.g. as siblings under the same parent): their internal element state - /// (`use_state`, scroll offsets, etc.) would silently collide. Nesting is - /// fine — the id is scoped by the parent path. - fn entity_id(&self) -> Option; - - /// Render this view into an element tree, consuming `self`. - fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement; -} - -/// A stateless component (`RenderOnce`) is a `View` with no identity. -impl View for T { - fn entity_id(&self) -> Option { - None - } - - #[inline] - fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { - RenderOnce::render(self, window, cx) - } -} - -/// An entity that renders itself (`Render`) is a `View` keyed on its own id. -impl View for Entity { - fn entity_id(&self) -> Option { - Some(Entity::entity_id(self)) - } - - #[inline] - fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { - self.update(cx, |this, cx| { - Render::render(this, window, cx).into_any_element() - }) - } -} - -impl Entity { - /// Embed this entity as a cached [`ViewElement`] laid out at `style`. - /// - /// The rendered subtree is reused until the entity is notified (or the - /// cached bounds / text style change). Caching requires a definite size: - /// a cached view is laid out from `style` and is *not* measured from its - /// contents. Use [`ViewElement::new`] (or `.child(entity)`) for the - /// uncached case. - #[track_caller] - pub fn cached(self, style: StyleRefinement) -> ViewElement> { - ViewElement::new(self).cached(style) - } -} - -/// The element type for [`View`] implementations. Wraps a `View` and hooks it -/// into layout, prepaint, and paint. Constructed via [`ViewElement::new`]. -#[doc(hidden)] -pub struct ViewElement { - view: Option, - entity_id: Option, - cached_style: Option, - #[cfg(debug_assertions)] - source: &'static core::panic::Location<'static>, -} - -impl ViewElement { - /// Wrap a [`View`] as an element. - #[track_caller] - pub fn new(view: V) -> Self { - let entity_id = view.entity_id(); - ViewElement { - entity_id, - cached_style: None, - view: Some(view), - #[cfg(debug_assertions)] - source: core::panic::Location::caller(), - } - } - - /// Enable caching of this view's rendered subtree, laid out at `style`. - /// The composer supplies the layout style because caching skips rendering - /// the contents to measure them. - /// - /// Crate-private on purpose: caching is only sound for entity-backed views, - /// where [`Context::notify`] is the contract that busts the cache. A stateless - /// view has no such contract, so a frozen subtree could never be invalidated. - /// Reach this through [`Entity::cached`] or [`AnyView::cached`], which are - /// entity-backed by construction. - pub(crate) fn cached(mut self, style: StyleRefinement) -> Self { - self.cached_style = Some(style); - self - } -} - -impl IntoElement for ViewElement { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -struct ViewElementState { - prepaint_range: Range, - paint_range: Range, - cache_key: ViewElementCacheKey, - accessed_entities: FxHashSet, -} - -struct ViewElementCacheKey { - bounds: Bounds, - content_mask: crate::ClipRegion, - text_style: TextStyle, -} - -impl Element for ViewElement { - type RequestLayoutState = Option; - type PrepaintState = Option; - - fn id(&self) -> Option { - self.entity_id.map(ElementId::View) - } - - fn source_location(&self) -> Option<&'static core::panic::Location<'static>> { - #[cfg(debug_assertions)] - return Some(self.source); - - #[cfg(not(debug_assertions))] - return None; - } - - fn request_layout( - &mut self, - _id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - window: &mut Window, - cx: &mut App, - ) -> (LayoutId, Self::RequestLayoutState) { - if let Some(entity_id) = self.entity_id { - // Stateful path: create a reactive boundary. - window.with_rendered_view(entity_id, |window| { - let caching_disabled = window.is_inspector_picking(cx); - match self.cached_style.as_ref() { - Some(style) if !caching_disabled => { - let mut root_style = Style::default(); - root_style.refine(style); - let layout_id = window.request_layout(root_style, None, cx); - (layout_id, None) - } - _ => { - let mut element = self - .view - .take() - .unwrap() - .render(window, cx) - .into_any_element(); - let layout_id = element.request_layout(window, cx); - (layout_id, Some(element)) - } - } - }) - } else { - // Stateless path: isolate subtree via type name (no entity identity). - window.with_id( - ElementId::Name(std::any::type_name::().into()), - |window| { - let mut element = self - .view - .take() - .unwrap() - .render(window, cx) - .into_any_element(); - let layout_id = element.request_layout(window, cx); - (layout_id, Some(element)) - }, - ) - } - } - - fn prepaint( - &mut self, - global_id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - bounds: Bounds, - element: &mut Self::RequestLayoutState, - window: &mut Window, - cx: &mut App, - ) -> Option { - if let Some(entity_id) = self.entity_id { - // Stateful path. - window.set_view_id(entity_id); - window.with_rendered_view(entity_id, |window| { - if let Some(mut element) = element.take() { - element.prepaint(window, cx); - return Some(element); - } - - window.with_element_state::( - global_id.unwrap(), - |element_state, window| { - let content_mask = window.content_mask(); - let text_style = window.text_style(); - - if let Some(mut element_state) = element_state - && element_state.cache_key.bounds == bounds - && element_state.cache_key.content_mask == content_mask - && element_state.cache_key.text_style == text_style - && !window.dirty_views.contains(&entity_id) - && !window.refreshing - { - let prepaint_start = window.prepaint_index(); - window.reuse_prepaint(element_state.prepaint_range.clone()); - cx.entities - .extend_accessed(&element_state.accessed_entities); - let prepaint_end = window.prepaint_index(); - element_state.prepaint_range = prepaint_start..prepaint_end; - - return (None, element_state); - } - - let refreshing = mem::replace(&mut window.refreshing, true); - let prepaint_start = window.prepaint_index(); - let (mut element, accessed_entities) = cx.detect_accessed_entities(|cx| { - let mut element = self - .view - .take() - .unwrap() - .render(window, cx) - .into_any_element(); - element.layout_as_root(bounds.size.into(), window, cx); - element.prepaint_at(bounds.origin, window, cx); - element - }); - - let prepaint_end = window.prepaint_index(); - window.refreshing = refreshing; - - ( - Some(element), - ViewElementState { - accessed_entities, - prepaint_range: prepaint_start..prepaint_end, - paint_range: PaintIndex::default()..PaintIndex::default(), - cache_key: ViewElementCacheKey { - bounds, - content_mask, - text_style, - }, - }, - ) - }, - ) - }) - } else { - // Stateless path: just prepaint the element. - window.with_id( - ElementId::Name(std::any::type_name::().into()), - |window| { - element.as_mut().unwrap().prepaint(window, cx); - }, - ); - Some(element.take().unwrap()) - } - } - - fn paint( - &mut self, - global_id: Option<&GlobalElementId>, - _inspector_id: Option<&InspectorElementId>, - _bounds: Bounds, - _request_layout: &mut Self::RequestLayoutState, - element: &mut Self::PrepaintState, - window: &mut Window, - cx: &mut App, - ) { - if let Some(entity_id) = self.entity_id { - // Stateful path. - window.with_rendered_view(entity_id, |window| { - let caching_disabled = window.is_inspector_picking(cx); - if self.cached_style.is_some() && !caching_disabled { - window.with_element_state::( - global_id.unwrap(), - |element_state, window| { - let mut element_state = element_state.unwrap(); - - let paint_start = window.paint_index(); - - if let Some(element) = element { - let refreshing = mem::replace(&mut window.refreshing, true); - element.paint(window, cx); - window.refreshing = refreshing; - } else { - window.reuse_paint(element_state.paint_range.clone()); - } - - let paint_end = window.paint_index(); - element_state.paint_range = paint_start..paint_end; - - ((), element_state) - }, - ) - } else { - element.as_mut().unwrap().paint(window, cx); - } - }); - } else { - // Stateless path: just paint the element. - window.with_id( - ElementId::Name(std::any::type_name::().into()), - |window| { - element.as_mut().unwrap().paint(window, cx); - }, - ); - } - } -} - -/// A view that renders nothing -pub struct EmptyView; - -impl Render for EmptyView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - Empty - } -} diff --git a/crates/gpui_pre/src/window.rs b/crates/gpui_pre/src/window.rs deleted file mode 100644 index b4e23ddb..00000000 --- a/crates/gpui_pre/src/window.rs +++ /dev/null @@ -1,8383 +0,0 @@ -#[cfg(feature = "profiler")] -use crate::profiler; -#[cfg(feature = "profiler")] -use crate::DebugFrameOverlayMode; -#[cfg(any(feature = "inspector", debug_assertions))] -use crate::Inspector; -use crate::{ - point, prelude::*, px, rems, size, transparent_black, Action, AnyDrag, AnyElement, - AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset, AsyncWindowContext, - AtlasTile, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Capslock, Context, - Corners, CursorHideMode, CursorStyle, Decorations, DevicePixels, DispatchActionListener, - DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, - FileDropEvent, FontId, Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler, IsZero, - KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId, - LineLayoutIndex, Modifiers, ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, - MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, - PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, Priority, PromptButton, - PromptLevel, Quad, Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, - Replay, ResizeEdge, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style, - SubpixelSprite, SubscriberSet, Subscription, SystemWindowTab, SystemWindowTabController, - TabStopMap, TaffyLayoutEngine, Task, TextInputConfiguration, TextInputStateChange, - TextRenderingMode, TextStyle, TextStyleRefinement, ThermalState, TransformationMatrix, - Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance, WindowBounds, - WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem, - SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS_X, SUBPIXEL_VARIANTS_Y, -}; - -use crate::gestures::{GestureTuning, RecognizedTouchGesture, TouchGestureRecognizer}; -use crate::interactive::TouchEvent; -use anyhow::{anyhow, Context as _, Result}; -use collections::{FxHashMap, FxHashSet}; -#[cfg(target_os = "macos")] -use core_video::pixel_buffer::CVPixelBuffer; -use derive_more::{Deref, DerefMut}; -use futures::channel::oneshot; -use futures::FutureExt; -use gpui_util::post_inc; -use gpui_util::{measure, ResultExt}; -use itertools::FoldWhile::{Continue, Done}; -use itertools::Itertools; -use parking_lot::RwLock; -use raw_window_handle::{HandleError, HasDisplayHandle, HasWindowHandle}; -use refineable::Refineable; -use scheduler::Instant; -use slotmap::SlotMap; -use smallvec::SmallVec; -use std::{ - any::{Any, TypeId}, - borrow::Cow, - cell::{Cell, RefCell}, - cmp, - fmt::{Debug, Display}, - hash::{Hash, Hasher}, - marker::PhantomData, - mem, - ops::{DerefMut, Range}, - rc::Rc, - sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}, - Arc, Weak, - }, - time::Duration, -}; -use uuid::Uuid; - -pub(crate) mod a11y; -mod prompts; - -pub use a11y::A11ySubtreeBuilder; - -use self::a11y::A11y; -#[cfg(not(target_family = "wasm"))] -use self::a11y::ROOT_NODE_ID; -use crate::util::{ - atomic_incr_if_not_zero, ceil_to_device_pixel, floor_to_device_pixel, round_half_toward_zero, - round_half_toward_zero_f64, round_stroke_to_device_pixel, round_to_device_pixel, -}; -pub use prompts::*; - -/// Default window size used when no explicit size is provided. -pub const DEFAULT_WINDOW_SIZE: Size = size(px(1536.), px(1095.)); - -/// A 6:5 aspect ratio minimum window size to be used for functional, -/// additional-to-main-Zed windows, like the settings and rules library windows. -pub const DEFAULT_ADDITIONAL_WINDOW_SIZE: Size = Size { - width: Pixels(900.), - height: Pixels(750.), -}; - -/// Represents the two different phases when dispatching events. -#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] -pub enum DispatchPhase { - /// After the capture phase comes the bubble phase, in which mouse event listeners are - /// invoked front to back and keyboard event listeners are invoked from the focused element - /// to the root of the element tree. This is the phase you'll most commonly want to use when - /// registering event listeners. - #[default] - Bubble, - /// During the initial capture phase, mouse event listeners are invoked back to front, and keyboard - /// listeners are invoked from the root of the tree downward toward the focused element. This phase - /// is used for special purposes such as clearing the "pressed" state for click events. If - /// you stop event propagation during this phase, you need to know what you're doing. Handlers - /// outside of the immediate region may rely on detecting non-local events during this phase. - Capture, -} - -impl DispatchPhase { - /// Returns true if this represents the "bubble" phase. - #[inline] - pub fn bubble(self) -> bool { - self == DispatchPhase::Bubble - } - - /// Returns true if this represents the "capture" phase. - #[inline] - pub fn capture(self) -> bool { - self == DispatchPhase::Capture - } -} - -struct WindowInvalidatorInner { - #[cfg(feature = "profiler")] - pub window_id: WindowId, - pub dirty: bool, - pub draw_phase: DrawPhase, - pub dirty_views: FxHashSet, - pub update_count: usize, - #[cfg(feature = "profiler")] - pub frame_dirty: FrameDirtyAccumulator, - pub platform_waker: Option>, -} - -/// Per-frame invalidation bookkeeping, drained at draw time and emitted to the -/// frame profiler. Tracks when the current frame first became dirty and how -/// many invalidations were coalesced into it, whenever the profiler is -/// compiled in. Retention of the resulting per-frame records is what -/// `profiler::trace_enabled()` controls, not this measurement. -#[cfg(feature = "profiler")] -#[derive(Default)] -struct FrameDirtyAccumulator { - dirty_at: Option, - invalidations: u64, -} - -#[derive(Clone)] -pub(crate) struct WindowInvalidator { - inner: Rc>, -} - -impl WindowInvalidator { - pub fn new(#[allow(unused_variables)] window_id: WindowId) -> Self { - WindowInvalidator { - inner: Rc::new(RefCell::new(WindowInvalidatorInner { - #[cfg(feature = "profiler")] - window_id, - dirty: true, - draw_phase: DrawPhase::None, - dirty_views: FxHashSet::default(), - update_count: 0, - #[cfg(feature = "profiler")] - frame_dirty: FrameDirtyAccumulator::default(), - platform_waker: None, - })), - } - } - - pub fn invalidate_view(&self, entity: EntityId, cx: &mut App) -> bool { - let mut inner = self.inner.borrow_mut(); - inner.update_count += 1; - inner.dirty_views.insert(entity); - if inner.draw_phase == DrawPhase::None { - #[cfg(feature = "profiler")] - let dirty_at = Self::record_frame_dirty(&mut inner); - let became_dirty = !inner.dirty; - inner.dirty = true; - let waker = became_dirty.then(|| inner.platform_waker.clone()).flatten(); - #[cfg(feature = "profiler")] - let window_id = inner.window_id; - drop(inner); - #[cfg(feature = "profiler")] - if became_dirty { - profiler::journal::record_frame_pending(window_id, dirty_at); - } - cx.push_effect(Effect::Notify { emitter: entity }); - if let Some(waker) = waker { - waker(); - } - true - } else { - false - } - } - - pub fn is_dirty(&self) -> bool { - self.inner.borrow().dirty - } - - pub fn set_dirty(&self, dirty: bool) { - let mut inner = self.inner.borrow_mut(); - let became_dirty = dirty && !inner.dirty; - inner.dirty = dirty; - if dirty { - inner.update_count += 1; - } - #[cfg(feature = "profiler")] - let dirty_at = dirty.then(|| Self::record_frame_dirty(&mut inner)); - let waker = became_dirty.then(|| inner.platform_waker.clone()).flatten(); - #[cfg(feature = "profiler")] - let window_id = inner.window_id; - drop(inner); - #[cfg(feature = "profiler")] - if became_dirty && let Some(dirty_at) = dirty_at { - profiler::journal::record_frame_pending(window_id, dirty_at); - } - if let Some(waker) = waker { - waker(); - } - } - - pub fn set_platform_waker(&self, waker: Option>) { - let mut inner = self.inner.borrow_mut(); - inner.platform_waker = waker; - let waker = inner.dirty.then(|| inner.platform_waker.clone()).flatten(); - drop(inner); - if let Some(waker) = waker { - waker(); - } - } - - /// Wakes the platform's frame-request source so a frame request is - /// delivered even if the platform stops requesting frames for idle - /// windows. No-op on platforms without a frame waker. - pub fn wake_platform(&self) { - let waker = self.inner.borrow().platform_waker.clone(); - if let Some(waker) = waker { - waker(); - } - } - - pub fn set_phase(&self, phase: DrawPhase) { - self.inner.borrow_mut().draw_phase = phase - } - - pub fn update_count(&self) -> usize { - self.inner.borrow().update_count - } - - #[cfg(feature = "profiler")] - fn record_frame_dirty(inner: &mut WindowInvalidatorInner) -> Instant { - let dirty_at = *inner.frame_dirty.dirty_at.get_or_insert_with(Instant::now); - inner.frame_dirty.invalidations += 1; - dirty_at - } - - #[cfg(feature = "profiler")] - fn take_frame_dirty(&self) -> FrameDirtyAccumulator { - mem::take(&mut self.inner.borrow_mut().frame_dirty) - } - - pub fn take_views(&self) -> FxHashSet { - mem::take(&mut self.inner.borrow_mut().dirty_views) - } - - pub fn replace_views(&self, views: FxHashSet) { - self.inner.borrow_mut().dirty_views = views; - } - - pub fn not_drawing(&self) -> bool { - self.inner.borrow().draw_phase == DrawPhase::None - } - - #[track_caller] - pub fn debug_assert_paint(&self) { - debug_assert!( - matches!(self.inner.borrow().draw_phase, DrawPhase::Paint), - "this method can only be called during paint" - ); - } - - #[track_caller] - pub fn debug_assert_prepaint(&self) { - debug_assert!( - matches!(self.inner.borrow().draw_phase, DrawPhase::Prepaint), - "this method can only be called during request_layout, or prepaint" - ); - } - - #[track_caller] - pub fn debug_assert_paint_or_prepaint(&self) { - debug_assert!( - matches!( - self.inner.borrow().draw_phase, - DrawPhase::Paint | DrawPhase::Prepaint - ), - "this method can only be called during request_layout, prepaint, or paint" - ); - } -} - -type AnyObserver = Box bool + 'static>; - -pub(crate) type AnyWindowFocusListener = - Box bool + 'static>; - -pub(crate) struct WindowFocusEvent { - pub(crate) previous_focus_path: SmallVec<[FocusId; 8]>, - pub(crate) current_focus_path: SmallVec<[FocusId; 8]>, -} - -impl WindowFocusEvent { - pub fn is_focus_in(&self, focus_id: FocusId) -> bool { - !self.previous_focus_path.contains(&focus_id) && self.current_focus_path.contains(&focus_id) - } - - pub fn is_focus_out(&self, focus_id: FocusId) -> bool { - self.previous_focus_path.contains(&focus_id) && !self.current_focus_path.contains(&focus_id) - } -} - -/// This is provided when subscribing for `Context::on_focus_out` events. -pub struct FocusOutEvent { - /// A weak focus handle representing what was blurred. - pub blurred: WeakFocusHandle, -} - -slotmap::new_key_type! { - /// A globally unique identifier for a focusable element. - pub struct FocusId; -} - -thread_local! { - /// Fallback arena used when no app-specific arena is active. - /// In production, each window draw sets CURRENT_ELEMENT_ARENA to the app's arena. - pub(crate) static ELEMENT_ARENA: RefCell = RefCell::new(Arena::new(1024 * 1024)); - - /// Points to the current App's element arena during draw operations. - /// This allows multiple test Apps to have isolated arenas, preventing - /// cross-session corruption when the scheduler interleaves their tasks. - static CURRENT_ELEMENT_ARENA: Cell>> = const { Cell::new(None) }; -} - -/// Whether a window draw is currently in progress on this thread. -/// -/// This holds exactly while an `ElementArenaScope` is active: nested scopes -/// restore the previous (still set) arena pointer, so `CURRENT_ELEMENT_ARENA` -/// is `Some` from the outermost draw's start to its end. -/// -/// The `on_request_frame` callback uses this to defer draw requests that -/// arrive re-entrantly while a draw is already on the stack (e.g. via nested -/// message pumping in the Windows window procedure), instead of running a -/// nested draw or panicking on the already-borrowed App. -fn draw_in_progress() -> bool { - CURRENT_ELEMENT_ARENA.with(|current| current.get().is_some()) -} - -/// Allocates an element in the current arena. Uses the app-specific arena if one -/// is active (during draw), otherwise falls back to the thread-local ELEMENT_ARENA. -pub(crate) fn with_element_arena(f: impl FnOnce(&mut Arena) -> R) -> R { - CURRENT_ELEMENT_ARENA.with(|current| { - if let Some(arena_ptr) = current.get() { - // SAFETY: The pointer is valid for the duration of the draw operation - // that set it, and we're being called during that same draw. - let arena_cell = unsafe { &*arena_ptr }; - f(&mut arena_cell.borrow_mut()) - } else { - ELEMENT_ARENA.with_borrow_mut(f) - } - }) -} - -/// Scope guard that sets CURRENT_ELEMENT_ARENA for the duration of a draw -/// operation and tracks the arena's scope depth, so that a nested draw's -/// `ArenaClearNeeded::clear` is deferred rather than freeing memory the outer -/// draw still references (see `Arena::clear`). -/// -/// Call [`ElementArenaScope::exit`] with the same arena that was entered to -/// obtain the [`ArenaClearNeeded`] token the draw now owes; requiring `exit` -/// makes it impossible to request a clear before the scope has ended. The -/// scope's teardown — restoring the thread-local and balancing `begin_scope` -/// with `end_scope` — happens in `Drop`, so the arena's scope depth stays -/// balanced on every path, including when a panic unwinds a draw before `exit` -/// is reached. (If teardown lived only in `exit`, such a panic would leave the -/// scope depth permanently elevated and defer every future clear, leaking -/// memory unboundedly.) -pub(crate) struct ElementArenaScope { - /// The entered arena: compared against the argument in `exit`, and - /// dereferenced in `Drop` to end its scope (see the SAFETY note there). - entered: *const RefCell, - previous: Option<*const RefCell>, - exited: bool, -} - -impl ElementArenaScope { - /// Enter a scope where element allocations use the given arena. - pub(crate) fn enter(arena: &RefCell) -> Self { - arena.borrow_mut().begin_scope(); - let previous = CURRENT_ELEMENT_ARENA.with(|current| { - let prev = current.get(); - current.set(Some(arena as *const RefCell)); - prev - }); - Self { - entered: arena as *const RefCell, - previous, - exited: false, - } - } - - /// End the scope: restores the previously-current arena and ends the - /// arena's clear-deferral scope. Returns the token for the arena clear the - /// draw now owes; producing it here makes it impossible to request a clear - /// before the scope has ended (which would be silently deferred forever). - /// - /// Panics if passed a different arena than was entered: ending the scope - /// of the wrong arena would unbalance two arenas' scope depths, allowing - /// one of them to clear while a draw still references its memory. - pub(crate) fn exit(mut self, arena: &RefCell) -> ArenaClearNeeded { - assert!( - std::ptr::eq(self.entered, arena), - "ElementArenaScope::exit called with a different arena than was entered" - ); - self.exited = true; - // Teardown (restoring the thread-local and ending the arena's - // clear-deferral scope) runs in `Drop`, which fires both here — `self` - // is dropped as `exit` returns, before the token reaches the caller — - // and when a panic unwinds the draw before `exit` is reached. - ArenaClearNeeded::new(arena) - } -} - -impl Drop for ElementArenaScope { - fn drop(&mut self) { - // Teardown lives here (rather than in `exit`) so it runs exactly once on - // every path: `exit` consumes and drops the guard on the normal path, - // and unwinding drops it on the panic path. Balancing `begin_scope` here - // keeps the arena's scope depth correct even when a draw panics; if this - // only happened in `exit`, a panic between `enter` and `exit` would leave - // the depth elevated and defer every future clear. - CURRENT_ELEMENT_ARENA.with(|current| { - current.set(self.previous); - }); - // SAFETY: `entered` came from a `&RefCell` in `enter`, and the - // arena (owned by the `App` being drawn) outlives this guard on both the - // normal and unwinding paths, since the guard is a local of the draw. - unsafe { &*self.entered }.borrow_mut().end_scope(); - if !self.exited && !std::thread::panicking() { - debug_assert!(false, "ElementArenaScope dropped without calling exit()"); - log::error!( - "ElementArenaScope dropped without calling exit(); \ - the arena clear for this draw was never requested" - ); - } - } -} - -/// Returned when the element arena has been used and so must be cleared before the next draw. -#[must_use] -pub struct ArenaClearNeeded { - /// Identity of the arena that was drawn into. Only ever compared against - /// another pointer in `clear`; never dereferenced. - arena: *const RefCell, -} - -impl ArenaClearNeeded { - /// Create a new ArenaClearNeeded token for the App whose arena was drawn - /// into. Private: the only way to obtain one is [`ElementArenaScope::exit`]. - fn new(arena: &RefCell) -> Self { - Self { - arena: arena as *const RefCell, - } - } - - /// Clear the element arena of the App the draw ran against. If an enclosing - /// draw is still in progress (this draw was nested inside it), the clear is - /// deferred to the enclosing draw's own `ArenaClearNeeded` so that its live - /// allocations aren't freed. - /// - /// Panics if passed a different App than the draw ran against, since - /// clearing another App's arena could free memory its draws still - /// reference. - pub fn clear(self, cx: &mut App) { - assert!( - std::ptr::eq(self.arena, &cx.element_arena), - "ArenaClearNeeded::clear called with a different App than the draw ran against" - ); - cx.element_arena.borrow_mut().clear(); - } -} - -pub(crate) type FocusMap = RwLock>; -pub(crate) struct FocusRef { - pub(crate) ref_count: AtomicUsize, - pub(crate) tab_index: isize, - pub(crate) tab_stop: bool, -} - -impl FocusId { - /// Obtains whether the element associated with this handle is currently focused. - pub fn is_focused(&self, window: &Window) -> bool { - window.focus == Some(*self) - } - - /// Obtains whether the element associated with this handle contains the focused - /// element or is itself focused. - pub fn contains_focused(&self, window: &Window, cx: &App) -> bool { - window - .focused(cx) - .is_some_and(|focused| self.contains(focused.id, window)) - } - - /// Obtains whether the element associated with this handle is contained within the - /// focused element or is itself focused. - pub fn within_focused(&self, window: &Window, cx: &App) -> bool { - let focused = window.focused(cx); - focused.is_some_and(|focused| focused.id.contains(*self, window)) - } - - /// Obtains whether this handle contains the given handle in the most recently rendered frame. - pub(crate) fn contains(&self, other: Self, window: &Window) -> bool { - window - .rendered_frame - .dispatch_tree - .focus_contains(*self, other) - } -} - -/// A handle which can be used to track and manipulate the focused element in a window. -pub struct FocusHandle { - pub(crate) id: FocusId, - handles: Arc, - /// The index of this element in the tab order. - pub tab_index: isize, - /// Whether this element can be focused by tab navigation. - pub tab_stop: bool, -} - -impl std::fmt::Debug for FocusHandle { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("FocusHandle({:?})", self.id)) - } -} - -impl FocusHandle { - pub(crate) fn new(handles: &Arc) -> Self { - let id = handles.write().insert(FocusRef { - ref_count: AtomicUsize::new(1), - tab_index: 0, - tab_stop: false, - }); - - Self { - id, - tab_index: 0, - tab_stop: false, - handles: handles.clone(), - } - } - - pub(crate) fn for_id(id: FocusId, handles: &Arc) -> Option { - let lock = handles.read(); - let focus = lock.get(id)?; - if atomic_incr_if_not_zero(&focus.ref_count) == 0 { - return None; - } - Some(Self { - id, - tab_index: focus.tab_index, - tab_stop: focus.tab_stop, - handles: handles.clone(), - }) - } - - /// Sets the tab index of the element associated with this handle. - pub fn tab_index(mut self, index: isize) -> Self { - self.tab_index = index; - if let Some(focus) = self.handles.write().get_mut(self.id) { - focus.tab_index = index; - } - self - } - - /// Sets whether the element associated with this handle is a tab stop. - /// - /// When `false`, the element will not be included in the tab order. - pub fn tab_stop(mut self, tab_stop: bool) -> Self { - self.tab_stop = tab_stop; - if let Some(focus) = self.handles.write().get_mut(self.id) { - focus.tab_stop = tab_stop; - } - self - } - - /// Converts this focus handle into a weak variant, which does not prevent it from being released. - pub fn downgrade(&self) -> WeakFocusHandle { - WeakFocusHandle { - id: self.id, - handles: Arc::downgrade(&self.handles), - } - } - - /// Moves the focus to the element associated with this handle. - pub fn focus(&self, window: &mut Window, cx: &mut App) { - window.focus(self, cx) - } - - /// Obtains whether the element associated with this handle is currently focused. - pub fn is_focused(&self, window: &Window) -> bool { - self.id.is_focused(window) - } - - /// Obtains whether the element associated with this handle contains the focused - /// element or is itself focused. - pub fn contains_focused(&self, window: &Window, cx: &App) -> bool { - self.id.contains_focused(window, cx) - } - - /// Obtains whether the element associated with this handle is contained within the - /// focused element or is itself focused. - pub fn within_focused(&self, window: &Window, cx: &mut App) -> bool { - self.id.within_focused(window, cx) - } - - /// Obtains whether this handle contains the given handle in the most recently rendered frame. - pub fn contains(&self, other: &Self, window: &Window) -> bool { - self.id.contains(other.id, window) - } - - /// Dispatch an action on the element that rendered this focus handle - pub fn dispatch_action(&self, action: &dyn Action, window: &mut Window, cx: &mut App) { - if let Some(node_id) = window - .rendered_frame - .dispatch_tree - .focusable_node_id(self.id) - { - window.dispatch_action_on_node(node_id, action, cx) - } - } -} - -impl Clone for FocusHandle { - fn clone(&self) -> Self { - Self::for_id(self.id, &self.handles).unwrap() - } -} - -impl PartialEq for FocusHandle { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl Eq for FocusHandle {} - -impl Drop for FocusHandle { - fn drop(&mut self) { - self.handles - .read() - .get(self.id) - .unwrap() - .ref_count - .fetch_sub(1, SeqCst); - } -} - -/// A weak reference to a focus handle. -#[derive(Clone, Debug)] -pub struct WeakFocusHandle { - pub(crate) id: FocusId, - pub(crate) handles: Weak, -} - -impl WeakFocusHandle { - /// Attempts to upgrade the [WeakFocusHandle] to a [FocusHandle]. - pub fn upgrade(&self) -> Option { - let handles = self.handles.upgrade()?; - FocusHandle::for_id(self.id, &handles) - } -} - -impl PartialEq for WeakFocusHandle { - fn eq(&self, other: &WeakFocusHandle) -> bool { - self.id == other.id - } -} - -impl Eq for WeakFocusHandle {} - -impl PartialEq for WeakFocusHandle { - fn eq(&self, other: &FocusHandle) -> bool { - self.id == other.id - } -} - -impl PartialEq for FocusHandle { - fn eq(&self, other: &WeakFocusHandle) -> bool { - self.id == other.id - } -} - -/// Focusable allows users of your view to easily -/// focus it (using window.focus_view(cx, view)) -pub trait Focusable: 'static { - /// Returns the focus handle associated with this view. - fn focus_handle(&self, cx: &App) -> FocusHandle; -} - -impl Focusable for Entity { - fn focus_handle(&self, cx: &App) -> FocusHandle { - self.read(cx).focus_handle(cx) - } -} - -/// ManagedView is a view (like a Modal, Popover, Menu, etc.) -/// where the lifecycle of the view is handled by another view. -pub trait ManagedView: Focusable + EventEmitter + Render {} - -impl + Render> ManagedView for M {} - -/// Emitted by implementers of [`ManagedView`] to indicate the view should be dismissed, such as when a view is presented as a modal. -pub struct DismissEvent; - -type FrameCallback = Box; - -pub(crate) type AnyMouseListener = - Box; - -#[derive(Clone)] -pub(crate) struct CursorStyleRequest { - pub(crate) hitbox_id: Option, - pub(crate) style: CursorStyle, -} - -#[derive(Default, Eq, PartialEq)] -pub(crate) struct HitTest { - pub(crate) ids: SmallVec<[HitboxId; 8]>, - pub(crate) hover_hitbox_count: usize, -} - -/// A type of window control area that corresponds to the platform window. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum WindowControlArea { - /// An area that allows dragging of the platform window. - Drag, - /// An area that allows closing of the platform window. - Close, - /// An area that allows maximizing of the platform window. - Max, - /// An area that allows minimizing of the platform window. - Min, -} - -/// An identifier for a [Hitbox] which also includes [HitboxBehavior]. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub struct HitboxId(u64); - -#[cfg(feature = "test-support")] -impl HitboxId { - /// A placeholder HitboxId exclusively for integration testing API's that - /// need a hitbox but where the value of the hitbox does not matter. The - /// alternative is to make the Hitbox optional but that complicates the - /// implementation. - pub const fn placeholder() -> Self { - Self(0) - } -} - -impl HitboxId { - /// Checks if the hitbox with this ID is currently hovered. Returns `false` during keyboard - /// input modality so that keyboard navigation suppresses hover highlights. Except when handling - /// `ScrollWheelEvent`, this is typically what you want when determining whether to handle mouse - /// events or paint hover styles. - /// - /// See [`Hitbox::is_hovered`] for details. - pub fn is_hovered(self, window: &Window) -> bool { - // If this hitbox has captured the pointer, it's always considered hovered - if window.captured_hitbox == Some(self) { - return true; - } - if window.last_input_was_keyboard() { - return false; - } - self.hit_test(window) - } - - /// Checks if the hitbox with this ID is currently hovered, regardless of the last - /// input modality used. - /// - /// See [`HitboxId::is_hovered`] for more details. - pub(crate) fn is_hovered_ignoring_last_input(self, window: &Window) -> bool { - // If this hitbox has captured the pointer, it's always considered hovered - if window.captured_hitbox == Some(self) { - return true; - } - self.hit_test(window) - } - - fn hit_test(self, window: &Window) -> bool { - let hit_test = &window.mouse_hit_test; - for id in hit_test.ids.iter().take(hit_test.hover_hitbox_count) { - if self == *id { - return true; - } - } - false - } - - /// Checks if the hitbox with this ID contains the mouse and should handle scroll events. - /// Typically this should only be used when handling `ScrollWheelEvent`, and otherwise - /// `is_hovered` should be used. See the documentation of `Hitbox::is_hovered` for details about - /// this distinction. - pub fn should_handle_scroll(self, window: &Window) -> bool { - window.mouse_hit_test.ids.contains(&self) - } - - fn next(mut self) -> HitboxId { - HitboxId(self.0.wrapping_add(1)) - } -} - -/// A rectangular region that potentially blocks hitboxes inserted prior. -/// See [Window::insert_hitbox] for more details. -#[derive(Clone, Debug, Deref)] -pub struct Hitbox { - /// A unique identifier for the hitbox. - pub id: HitboxId, - /// The bounds of the hitbox. - #[deref] - pub bounds: Bounds, - /// The content mask when the hitbox was inserted. - pub content_mask: crate::ClipRegion, - /// Flags that specify hitbox behavior. - pub behavior: HitboxBehavior, -} - -impl Hitbox { - /// Checks if the hitbox is currently hovered. Returns `false` during keyboard input modality - /// so that keyboard navigation suppresses hover highlights. Except when handling - /// `ScrollWheelEvent`, this is typically what you want when determining whether to handle mouse - /// events or paint hover styles. - /// - /// This can return `false` even when the hitbox contains the mouse, if a hitbox in front of - /// this sets `HitboxBehavior::BlockMouse` (`InteractiveElement::occlude`) or - /// `HitboxBehavior::BlockMouseExceptScroll` (`InteractiveElement::block_mouse_except_scroll`), - /// or if the current input modality is keyboard (see [`Window::last_input_was_keyboard`]). - /// - /// Handling of `ScrollWheelEvent` should typically use `should_handle_scroll` instead. - /// Concretely, this is due to use-cases like overlays that cause the elements under to be - /// non-interactive while still allowing scrolling. More abstractly, this is because - /// `is_hovered` is about element interactions directly under the mouse - mouse moves, clicks, - /// hover styling, etc. In contrast, scrolling is about finding the current outer scrollable - /// container. - pub fn is_hovered(&self, window: &Window) -> bool { - self.id.is_hovered(window) - } - - /// Checks whether this hitbox would be hovered at `position`, regardless of the current input - /// modality or mouse position. - pub fn is_hovered_at(&self, position: Point, window: &Window) -> bool { - let hit_test = window.rendered_frame.hit_test(position); - hit_test - .ids - .iter() - .take(hit_test.hover_hitbox_count) - .any(|id| self.id == *id) - } - - /// Checks if the hitbox contains the mouse and should handle scroll events. Typically this - /// should only be used when handling `ScrollWheelEvent`, and otherwise `is_hovered` should be - /// used. See the documentation of `Hitbox::is_hovered` for details about this distinction. - /// - /// This can return `false` even when the hitbox contains the mouse, if a hitbox in front of - /// this sets `HitboxBehavior::BlockMouse` (`InteractiveElement::occlude`). - pub fn should_handle_scroll(&self, window: &Window) -> bool { - self.id.should_handle_scroll(window) - } -} - -/// How the hitbox affects mouse behavior. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub enum HitboxBehavior { - /// Normal hitbox mouse behavior, doesn't affect mouse handling for other hitboxes. - #[default] - Normal, - - /// All hitboxes behind this hitbox will be ignored and so will have `hitbox.is_hovered() == - /// false` and `hitbox.should_handle_scroll() == false`. Typically for elements this causes - /// skipping of all mouse events, hover styles, and tooltips. This flag is set by - /// [`InteractiveElement::occlude`]. - /// - /// For mouse handlers that check those hitboxes, this behaves the same as registering a - /// bubble-phase handler for every mouse event type: - /// - /// ```ignore - /// window.on_mouse_event(move |_: &EveryMouseEventTypeHere, phase, window, cx| { - /// if phase == DispatchPhase::Capture && hitbox.is_hovered(window) { - /// cx.stop_propagation(); - /// } - /// }) - /// ``` - /// - /// This has effects beyond event handling - any use of hitbox checking, such as hover - /// styles and tooltips. These other behaviors are the main point of this mechanism. An - /// alternative might be to not affect mouse event handling - but this would allow - /// inconsistent UI where clicks and moves interact with elements that are not considered to - /// be hovered. - BlockMouse, - - /// All hitboxes behind this hitbox will have `hitbox.is_hovered() == false`, even when - /// `hitbox.should_handle_scroll() == true`. Typically for elements this causes all mouse - /// interaction except scroll events to be ignored - see the documentation of - /// [`Hitbox::is_hovered`] for details. This flag is set by - /// [`InteractiveElement::block_mouse_except_scroll`]. - /// - /// For mouse handlers that check those hitboxes, this behaves the same as registering a - /// bubble-phase handler for every mouse event type **except** `ScrollWheelEvent`: - /// - /// ```ignore - /// window.on_mouse_event(move |_: &EveryMouseEventTypeExceptScroll, phase, window, cx| { - /// if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) { - /// cx.stop_propagation(); - /// } - /// }) - /// ``` - /// - /// See the documentation of [`Hitbox::is_hovered`] for details of why `ScrollWheelEvent` is - /// handled differently than other mouse events. If also blocking these scroll events is - /// desired, then a `cx.stop_propagation()` handler like the one above can be used. - /// - /// This has effects beyond event handling - this affects any use of `is_hovered`, such as - /// hover styles and tooltips. These other behaviors are the main point of this mechanism. - /// An alternative might be to not affect mouse event handling - but this would allow - /// inconsistent UI where clicks and moves interact with elements that are not considered to - /// be hovered. - BlockMouseExceptScroll, -} - -/// An identifier for a tooltip. -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] -pub struct TooltipId(usize); - -impl TooltipId { - /// Checks if the tooltip is currently hovered. - pub fn is_hovered(&self, window: &Window) -> bool { - window - .tooltip_bounds - .as_ref() - .is_some_and(|tooltip_bounds| { - tooltip_bounds.id == *self - && tooltip_bounds.bounds.contains(&window.mouse_position()) - }) - } -} - -pub(crate) struct TooltipBounds { - id: TooltipId, - bounds: Bounds, -} - -#[derive(Clone)] -pub(crate) struct TooltipRequest { - id: TooltipId, - tooltip: AnyTooltip, -} - -pub(crate) struct DeferredDraw { - current_view: EntityId, - priority: usize, - parent_node: DispatchNodeId, - element_id_stack: SmallVec<[ElementId; 32]>, - text_style_stack: Vec, - content_mask: Option, - rem_size: Pixels, - element: Option, - absolute_offset: Point, - prepaint_range: Range, - paint_range: Range, -} - -pub(crate) struct Frame { - pub(crate) focus: Option, - pub(crate) window_active: bool, - pub(crate) element_states: FxHashMap<(GlobalElementId, TypeId), ElementStateBox>, - accessed_element_states: Vec<(GlobalElementId, TypeId)>, - pub(crate) mouse_listeners: Vec>, - pub(crate) dispatch_tree: DispatchTree, - pub(crate) scene: Scene, - pub(crate) hitboxes: Vec, - pub(crate) window_control_hitboxes: Vec<(WindowControlArea, Hitbox)>, - pub(crate) deferred_draws: Vec, - pub(crate) input_handlers: Vec>, - pub(crate) tooltip_requests: Vec>, - pub(crate) cursor_styles: Vec, - #[cfg(any(test, feature = "test-support"))] - pub(crate) debug_bounds: FxHashMap>, - #[cfg(any(feature = "inspector", debug_assertions))] - pub(crate) next_inspector_instance_ids: FxHashMap, usize>, - #[cfg(any(feature = "inspector", debug_assertions))] - pub(crate) inspector_hitboxes: FxHashMap, - pub(crate) tab_stops: TabStopMap, -} - -#[derive(Clone, Default)] -pub(crate) struct PrepaintStateIndex { - hitboxes_index: usize, - tooltips_index: usize, - deferred_draws_index: usize, - dispatch_tree_index: usize, - accessed_element_states_index: usize, - line_layout_index: LineLayoutIndex, -} - -#[derive(Clone, Default)] -pub(crate) struct PaintIndex { - scene_index: usize, - mouse_listeners_index: usize, - input_handlers_index: usize, - cursor_styles_index: usize, - accessed_element_states_index: usize, - tab_handle_index: usize, - line_layout_index: LineLayoutIndex, -} - -impl Frame { - pub(crate) fn new(dispatch_tree: DispatchTree) -> Self { - Frame { - focus: None, - window_active: false, - element_states: FxHashMap::default(), - accessed_element_states: Vec::new(), - mouse_listeners: Vec::new(), - dispatch_tree, - scene: Scene::default(), - hitboxes: Vec::new(), - window_control_hitboxes: Vec::new(), - deferred_draws: Vec::new(), - input_handlers: Vec::new(), - tooltip_requests: Vec::new(), - cursor_styles: Vec::new(), - - #[cfg(any(test, feature = "test-support"))] - debug_bounds: FxHashMap::default(), - - #[cfg(any(feature = "inspector", debug_assertions))] - next_inspector_instance_ids: FxHashMap::default(), - - #[cfg(any(feature = "inspector", debug_assertions))] - inspector_hitboxes: FxHashMap::default(), - tab_stops: TabStopMap::default(), - } - } - - pub(crate) fn clear(&mut self) { - self.element_states.clear(); - self.accessed_element_states.clear(); - self.mouse_listeners.clear(); - self.dispatch_tree.clear(); - self.scene.clear(); - self.input_handlers.clear(); - self.tooltip_requests.clear(); - self.cursor_styles.clear(); - self.hitboxes.clear(); - self.window_control_hitboxes.clear(); - self.deferred_draws.clear(); - self.tab_stops.clear(); - self.focus = None; - - #[cfg(any(test, feature = "test-support"))] - { - self.debug_bounds.clear(); - } - - #[cfg(any(feature = "inspector", debug_assertions))] - { - self.next_inspector_instance_ids.clear(); - self.inspector_hitboxes.clear(); - } - } - - pub(crate) fn cursor_style(&self, window: &Window) -> Option { - self.cursor_styles - .iter() - .rev() - .fold_while(None, |style, request| match request.hitbox_id { - None => Done(Some(request.style)), - Some(hitbox_id) => Continue(style.or_else(|| { - hitbox_id - .is_hovered_ignoring_last_input(window) - .then_some(request.style) - })), - }) - .into_inner() - } - - pub(crate) fn hit_test(&self, position: Point) -> HitTest { - let mut set_hover_hitbox_count = false; - let mut hit_test = HitTest::default(); - for hitbox in self.hitboxes.iter().rev() { - let bounds = hitbox.bounds.intersect(&hitbox.content_mask.bounds); - if bounds.contains(&position) { - hit_test.ids.push(hitbox.id); - if !set_hover_hitbox_count - && hitbox.behavior == HitboxBehavior::BlockMouseExceptScroll - { - hit_test.hover_hitbox_count = hit_test.ids.len(); - set_hover_hitbox_count = true; - } - if hitbox.behavior == HitboxBehavior::BlockMouse { - break; - } - } - } - if !set_hover_hitbox_count { - hit_test.hover_hitbox_count = hit_test.ids.len(); - } - hit_test - } - - pub(crate) fn focus_path(&self) -> SmallVec<[FocusId; 8]> { - self.focus - .map(|focus_id| self.dispatch_tree.focus_path(focus_id)) - .unwrap_or_default() - } - - pub(crate) fn finish(&mut self, prev_frame: &mut Self) { - for element_state_key in &self.accessed_element_states { - if let Some((element_state_key, element_state)) = - prev_frame.element_states.remove_entry(element_state_key) - { - self.element_states.insert(element_state_key, element_state); - } - } - - self.scene.finish(); - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -enum InputModality { - Mouse, - Keyboard, - Touch, -} - -/// Holds the state for a specific window. -pub struct Window { - pub(crate) handle: AnyWindowHandle, - pub(crate) invalidator: WindowInvalidator, - pub(crate) removed: bool, - pub(crate) platform_window: Box, - display_id: Option, - is_resizable: bool, - is_minimizable: bool, - sprite_atlas: Arc, - text_system: Arc, - text_rendering_mode: Rc>, - rem_size: Pixels, - /// The stack of override values for the window's rem size. - /// - /// This is used by `with_rem_size` to allow rendering an element tree with - /// a given rem size. - rem_size_override_stack: SmallVec<[Pixels; 8]>, - pub(crate) viewport_size: Size, - layout_engine: Option, - pub(crate) root: Option, - pub(crate) element_id_stack: SmallVec<[ElementId; 32]>, - pub(crate) text_style_stack: Vec, - pub(crate) rendered_entity_stack: Vec, - pub(crate) element_offset_stack: Vec>, - pub(crate) element_opacity: f32, - pub(crate) content_mask_stack: Vec, - pub(crate) requested_autoscroll: Option>, - /// The [`TextInputConfiguration`] most recently forwarded to the platform - /// window, so that only actual changes are forwarded (reconfiguring a live - /// input session can restart the IME connection). - last_text_input_configuration: Option, - focused_text_input_active: bool, - pub(crate) image_cache_stack: Vec, - pub(crate) rendered_frame: Frame, - pub(crate) next_frame: Frame, - next_hitbox_id: HitboxId, - pub(crate) next_tooltip_id: TooltipId, - pub(crate) tooltip_bounds: Option, - pub(crate) next_frame_callbacks: Rc>>, - pub(crate) dirty_views: FxHashSet, - focus_listeners: SubscriberSet<(), AnyWindowFocusListener>, - pub(crate) focus_lost_listeners: SubscriberSet<(), AnyObserver>, - focus_lost_path: SmallVec<[FocusId; 8]>, - default_prevented: bool, - mouse_position: Point, - mouse_hit_test: HitTest, - modifiers: Modifiers, - capslock: Capslock, - scale_factor: f32, - pub(crate) bounds_observers: SubscriberSet<(), AnyObserver>, - appearance: WindowAppearance, - pub(crate) appearance_observers: SubscriberSet<(), AnyObserver>, - pub(crate) button_layout_observers: SubscriberSet<(), AnyObserver>, - active: Rc>, - hovered: Rc>, - pub(crate) needs_present: Rc>, - /// Tracks recent input event timestamps to determine if input is arriving at a high rate. - /// Used to selectively enable VRR optimization only when input rate exceeds 60fps. - pub(crate) input_rate_tracker: Rc>, - #[cfg(feature = "profiler")] - window_profiler: profiler::WindowProfiler, - last_input_modality: InputModality, - touch_gestures: TouchGestureRecognizer, - touch_prediction_enabled: bool, - long_press_timer: Option>, - long_press_capture: Option, - pub(crate) refreshing: bool, - pub(crate) activation_observers: SubscriberSet<(), AnyObserver>, - pub(crate) focus: Option, - focus_enabled: bool, - /// Incremented every time focus moves. Used to invalidate a - /// pending keyboard activation state when focus changes. - pub(crate) focus_generation: u64, - pending_input: Option, - pending_modifier: ModifierState, - pub(crate) pending_input_observers: SubscriberSet<(), AnyObserver>, - prompt: Option, - pub(crate) client_inset: Option, - /// The hitbox that has captured the pointer, if any. - /// While captured, mouse events route to this hitbox regardless of hit testing. - captured_hitbox: Option, - #[cfg(any(feature = "inspector", debug_assertions))] - inspector: Option>, - #[cfg(feature = "profiler")] - debug_frame_overlay: crate::debug_overlay::DebugFrameOverlay, - pub(crate) a11y: A11y, -} - -#[derive(Clone, Debug, Default)] -struct ModifierState { - modifiers: Modifiers, - saw_other_input: bool, -} - -/// Tracks input event timestamps to determine if input is arriving at a high rate. -/// Used for selective VRR (Variable Refresh Rate) optimization. -#[derive(Clone, Debug)] -pub(crate) struct InputRateTracker { - timestamps: Vec, - window: Duration, - inputs_per_second: u32, - sustain_until: Instant, - sustain_duration: Duration, -} - -impl Default for InputRateTracker { - fn default() -> Self { - Self { - timestamps: Vec::new(), - window: Duration::from_millis(100), - inputs_per_second: 60, - sustain_until: Instant::now(), - sustain_duration: Duration::from_secs(1), - } - } -} - -impl InputRateTracker { - pub fn record_input(&mut self) { - let now = Instant::now(); - self.timestamps.push(now); - self.prune_old_timestamps(now); - - let min_events = self.inputs_per_second as u128 * self.window.as_millis() / 1000; - if self.timestamps.len() as u128 >= min_events { - self.sustain_until = now + self.sustain_duration; - } - } - - pub fn is_high_rate(&self) -> bool { - Instant::now() < self.sustain_until - } - - fn prune_old_timestamps(&mut self, now: Instant) { - self.timestamps - .retain(|&t| now.duration_since(t) <= self.window); - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub(crate) enum DrawPhase { - None, - Prepaint, - Paint, - Focus, -} - -pub(crate) const PENDING_INPUT_TIMEOUT: Duration = Duration::from_secs(1); - -/// Pending input for a potential multi-stroke key binding. -pub struct PendingInputStatus<'a> { - keystrokes: &'a [Keystroke], - timeout: Option, -} - -impl<'a> PendingInputStatus<'a> { - /// Returns the keystrokes entered so far. - pub fn keystrokes(&self) -> &'a [Keystroke] { - self.keystrokes - } - - /// Returns the timeout state for flushing this input, if it needs a timeout. - pub fn timeout(&self) -> Option { - self.timeout - } -} - -/// The timeout state for pending input. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct PendingInputTimeoutStatus { - duration: Duration, - remaining: Duration, - started_at: Option, - paused: bool, -} - -impl PendingInputTimeoutStatus { - /// Returns the full timeout duration. - pub fn duration(&self) -> Duration { - self.duration - } - - /// Returns the duration remaining before pending input is flushed. - pub fn remaining(&self, cx: &App) -> Duration { - self.started_at - .map(|started_at| { - self.remaining - .saturating_sub(cx.background_executor().now() - started_at) - }) - .unwrap_or(self.remaining) - } - - /// Returns whether the timeout is paused. - pub fn is_paused(&self) -> bool { - self.paused - } -} - -#[derive(Debug)] -struct PendingInputTimeout { - duration: Duration, - remaining: Duration, - state: PendingInputTimeoutState, -} - -#[derive(Debug)] -enum PendingInputTimeoutState { - Running { started_at: Instant, task: Task<()> }, - Paused { pause: PendingInputTimeoutPause }, -} - -#[derive(Debug)] -struct PendingInputTimeoutPause { - owner_id: EntityId, - _release_subscription: Subscription, -} - -impl PendingInputTimeout { - fn is_paused(&self) -> bool { - matches!(&self.state, PendingInputTimeoutState::Paused { .. }) - } - - fn pause(&mut self, pause: PendingInputTimeoutPause, now: Instant) -> bool { - match std::mem::replace(&mut self.state, PendingInputTimeoutState::Paused { pause }) { - PendingInputTimeoutState::Running { started_at, task } => { - self.remaining = self.remaining.saturating_sub(now - started_at); - drop(task); - true - } - previous_state @ PendingInputTimeoutState::Paused { .. } => { - self.state = previous_state; - false - } - } - } - - fn pause_owner_id(&self) -> Option { - match &self.state { - PendingInputTimeoutState::Running { .. } => None, - PendingInputTimeoutState::Paused { pause } => Some(pause.owner_id), - } - } - - fn resume(&mut self, owner_id: EntityId, started_at: Instant, task: Task<()>) -> bool { - match std::mem::replace( - &mut self.state, - PendingInputTimeoutState::Running { started_at, task }, - ) { - PendingInputTimeoutState::Paused { pause } if pause.owner_id == owner_id => true, - previous_state => { - self.state = previous_state; - false - } - } - } - - fn reset_duration(&mut self, duration: Duration) { - self.duration = duration; - self.remaining = duration; - } - - fn status(&self) -> PendingInputTimeoutStatus { - let (started_at, paused) = match &self.state { - PendingInputTimeoutState::Running { started_at, .. } => (Some(*started_at), false), - PendingInputTimeoutState::Paused { .. } => (None, true), - }; - PendingInputTimeoutStatus { - duration: self.duration, - remaining: self.remaining, - started_at, - paused, - } - } -} - -#[derive(Default, Debug)] -struct PendingInput { - keystrokes: SmallVec<[Keystroke; 1]>, - focus: Option, - timeout: Option, -} - -pub(crate) struct ElementStateBox { - pub(crate) inner: Box, - #[cfg(debug_assertions)] - pub(crate) type_name: &'static str, -} - -fn default_bounds(display_id: Option, cx: &mut App) -> WindowBounds { - // TODO, BUG: if you open a window with the currently active window - // on the stack, this will erroneously fallback to `None` - // - // TODO these should be the initial window bounds not considering maximized/fullscreen - let active_window_bounds = cx - .active_window() - .and_then(|w| w.update(cx, |_, window, _| window.window_bounds()).ok()); - - const CASCADE_OFFSET: f32 = 25.0; - - let display = display_id - .map(|id| cx.find_display(id)) - .unwrap_or_else(|| cx.primary_display()); - - let default_placement = || Bounds::new(point(px(0.), px(0.)), DEFAULT_WINDOW_SIZE); - - // Use visible_bounds to exclude taskbar/dock areas - let display_bounds = display - .as_ref() - .map(|d| d.visible_bounds()) - .unwrap_or_else(default_placement); - - let ( - Bounds { - origin: base_origin, - size: base_size, - }, - window_bounds_ctor, - ): (_, fn(Bounds) -> WindowBounds) = match active_window_bounds { - Some(bounds) => match bounds { - WindowBounds::Windowed(bounds) => (bounds, WindowBounds::Windowed), - WindowBounds::Maximized(bounds) => (bounds, WindowBounds::Maximized), - WindowBounds::Fullscreen(bounds) => (bounds, WindowBounds::Fullscreen), - }, - None => ( - display - .as_ref() - .map(|d| d.default_bounds()) - .unwrap_or_else(default_placement), - WindowBounds::Windowed, - ), - }; - - let cascade_offset = point(px(CASCADE_OFFSET), px(CASCADE_OFFSET)); - let proposed_origin = base_origin + cascade_offset; - let proposed_bounds = Bounds::new(proposed_origin, base_size); - - let display_right = display_bounds.origin.x + display_bounds.size.width; - let display_bottom = display_bounds.origin.y + display_bounds.size.height; - let window_right = proposed_bounds.origin.x + proposed_bounds.size.width; - let window_bottom = proposed_bounds.origin.y + proposed_bounds.size.height; - - let fits_horizontally = window_right <= display_right; - let fits_vertically = window_bottom <= display_bottom; - - let final_origin = match (fits_horizontally, fits_vertically) { - (true, true) => proposed_origin, - (false, true) => point(display_bounds.origin.x, base_origin.y), - (true, false) => point(base_origin.x, display_bounds.origin.y), - (false, false) => display_bounds.origin, - }; - window_bounds_ctor(Bounds::new(final_origin, base_size)) -} - -impl Window { - pub(crate) fn new( - handle: AnyWindowHandle, - options: WindowOptions, - cx: &mut App, - ) -> Result { - let WindowOptions { - window_bounds, - titlebar, - focus, - show, - kind, - is_movable, - app_owns_titlebar_drag, - inactive_frame_interval, - is_resizable, - is_minimizable, - display_id, - window_background, - app_id, - window_min_size, - window_decorations, - #[cfg_attr( - not(any(target_os = "linux", target_os = "freebsd")), - allow(unused_variables) - )] - icon, - #[cfg_attr(not(target_os = "macos"), allow(unused_variables))] - tabbing_identifier, - } = options; - - let initial_window_title = titlebar - .as_ref() - .and_then(|titlebar| titlebar.title.clone()); - - let window_bounds = window_bounds.unwrap_or_else(|| default_bounds(display_id, cx)); - let mut platform_window = cx.platform.open_window( - handle, - WindowParams { - bounds: window_bounds.get_bounds(), - titlebar, - kind, - is_movable, - app_owns_titlebar_drag, - is_resizable, - is_minimizable, - focus, - show, - display_id, - window_min_size, - app_id: app_id.clone(), - icon, - #[cfg(target_os = "macos")] - tabbing_identifier, - }, - )?; - - let tab_bar_visible = platform_window.tab_bar_visible(); - SystemWindowTabController::init_visible(cx, tab_bar_visible); - if let Some(tabs) = platform_window.tabbed_windows() { - SystemWindowTabController::add_tab(cx, handle.window_id(), tabs); - } - - let display_id = platform_window.display().map(|display| display.id()); - let sprite_atlas = platform_window.sprite_atlas(); - let mouse_position = platform_window.mouse_position(); - let modifiers = platform_window.modifiers(); - let capslock = platform_window.capslock(); - let content_size = platform_window.content_size(); - let scale_factor = platform_window.scale_factor(); - let appearance = platform_window.appearance(); - let text_system = Arc::new(WindowTextSystem::new(cx.text_system().clone())); - let invalidator = WindowInvalidator::new(handle.window_id()); - let active = Rc::new(Cell::new(platform_window.is_active())); - let hovered = Rc::new(Cell::new(platform_window.is_hovered())); - let needs_present = Rc::new(Cell::new(false)); - let next_frame_callbacks: Rc>> = Default::default(); - let input_rate_tracker = Rc::new(RefCell::new(InputRateTracker::default())); - let last_frame_time = Rc::new(Cell::new(None)); - - platform_window - .request_decorations(window_decorations.unwrap_or(WindowDecorations::Server)); - platform_window.set_background_appearance(window_background); - - match window_bounds { - WindowBounds::Fullscreen(_) => platform_window.toggle_fullscreen(), - WindowBounds::Maximized(_) => platform_window.zoom(), - WindowBounds::Windowed(_) => {} - } - - let accessibility_force_disabled = cx.accessibility_force_disabled; - let a11y_active_flag = Arc::new(AtomicBool::new(false)); - - #[cfg(not(target_family = "wasm"))] - if !accessibility_force_disabled { - let mut initial_root_node = accesskit::Node::new(accesskit::Role::Window); - if let Some(title) = &initial_window_title { - initial_root_node.set_label(title.to_string()); - } - let initial_tree = accesskit::TreeUpdate { - nodes: vec![(ROOT_NODE_ID, initial_root_node)], - tree: Some(accesskit::Tree::new(ROOT_NODE_ID)), - tree_id: accesskit::TreeId::ROOT, - focus: ROOT_NODE_ID, - }; - let (activation_sender, activation_receiver) = async_channel::unbounded::<()>(); - let (deactivation_sender, deactivation_receiver) = async_channel::unbounded::<()>(); - let (action_sender, action_receiver) = - async_channel::unbounded::(); - - platform_window.a11y_init(crate::A11yCallbacks { - activation: { - let active_flag = a11y_active_flag.clone(); - Box::new(move || { - log::info!("Accessibility activated"); - active_flag.store(true, SeqCst); - activation_sender.send_blocking(()).log_err(); - Some(initial_tree.clone()) - }) - }, - action: Box::new(move |request| { - action_sender.send_blocking(request).log_err(); - }), - deactivation: { - let active_flag = a11y_active_flag.clone(); - Box::new(move || { - log::info!("Accessibility deactivated"); - active_flag.store(false, SeqCst); - deactivation_sender.send_blocking(()).log_err(); - }) - }, - }); - - // A11y can be activated at any time, and so we cannot compute a - // correct `TreeUpdate` on-demand. When this happens, we return a - // default empty `TreeUpdate`. - // - // So we force a new frame, which will then send a correct `TreeUpdate`. - let mut async_cx = cx.to_async(); - cx.foreground_executor() - .spawn(async move { - while activation_receiver.recv().await.is_ok() { - handle - .update(&mut async_cx, |_, window, _| window.refresh()) - .log_err(); - } - }) - .detach(); - - let mut async_cx = cx.to_async(); - cx.foreground_executor() - .spawn(async move { - while deactivation_receiver.recv().await.is_ok() { - handle - .update(&mut async_cx, |_, window, _| window.refresh()) - .log_err(); - } - }) - .detach(); - - let mut async_cx = cx.to_async(); - cx.foreground_executor() - .spawn(async move { - while let Ok(request) = action_receiver.recv().await { - handle - .update(&mut async_cx, |_, window, cx| { - window.handle_a11y_action(request, cx); - }) - .log_err(); - } - }) - .detach(); - } - - platform_window.on_close(Box::new({ - let window_id = handle.window_id(); - let mut cx = cx.to_async(); - move || { - let _ = handle.update(&mut cx, |_, window, _| window.remove_window()); - let _ = cx.update(|cx| { - SystemWindowTabController::remove_tab(cx, window_id); - }); - } - })); - platform_window.on_request_frame(Box::new({ - let mut cx = cx.to_async(); - let invalidator = invalidator.clone(); - let active = active.clone(); - let needs_present = needs_present.clone(); - let next_frame_callbacks = next_frame_callbacks.clone(); - let input_rate_tracker = input_rate_tracker.clone(); - let mut deferred_force_render = false; - move |request_frame_options| { - #[cfg(feature = "profiler")] - let _foreground_turn = profiler::journal::foreground_turn(); - // This must be checked before anything else: if this request - // arrived re-entrantly while a draw is on this thread's stack - // (e.g. via a nested message pump in the Windows window - // procedure), drawing would nest draws, and even touching the - // App would panic on its already-mutable borrow. Skip instead; - // the platform leaves the window invalidated (or re-invalidates - // it), so a fresh request arrives once the in-progress draw - // unwinds. Remember force_render so the deferred frame still - // bypasses the view cache. - // - // Returning here skips `complete_frame`, which on Wayland would - // stall the window's frame callbacks (no `surface.commit()`) — - // but calling it would hit the App borrow panic above, and this - // branch is unreachable there in practice: only Windows pumps - // platform events (and thus requests frames) mid-draw. - if draw_in_progress() { - log::debug!("deferring re-entrant window draw request"); - deferred_force_render |= request_frame_options.force_render; - return; - } - // Take the deferred flag first: `||` short-circuits, and leaving - // the flag set when this request already forces a render would - // force a second, redundant render on the next frame. - let force_render = - mem::take(&mut deferred_force_render) || request_frame_options.force_render; - - let thermal_state = handle - .update(&mut cx, |_, _, cx| cx.thermal_state()) - .log_err(); - - // Throttle frame rate based on conditions: - // - Thermal pressure (Serious/Critical): cap to ~60fps - // - Inactive window (not focused): cap to ~30fps to save energy - let min_frame_interval = if request_frame_options.require_presentation - || (!request_frame_options.force_render - && next_frame_callbacks.borrow().is_empty()) - { - None - } else if !active.get() && !input_rate_tracker.borrow_mut().is_high_rate() { - inactive_frame_interval - } else if let Some(ThermalState::Critical | ThermalState::Serious) = thermal_state { - Some(Duration::from_micros(16667)) - } else { - None - }; - - let now = Instant::now(); - if let Some(min_interval) = min_frame_interval { - if let Some(last_frame) = last_frame_time.get() - && now.duration_since(last_frame) < min_interval - { - // Don't lose a pending forced render to throttling. - deferred_force_render |= force_render; - // Deferred by throttling: ask demand-driven platforms to retry. - handle - .update(&mut cx, |_, window, _| { - window.platform_window.schedule_frame(); - }) - .log_err(); - // The demand that entered this branch (a deferred forced - // render or pending next-frame callbacks) is still - // unserved; platforms that stop requesting frames for - // idle windows need a wakeup to deliver the retry. - invalidator.wake_platform(); - return; - } - } - last_frame_time.set(Some(now)); - - let pending_next_frame_callbacks = next_frame_callbacks.take(); - if !pending_next_frame_callbacks.is_empty() { - handle - .update(&mut cx, |_, window, cx| { - for callback in pending_next_frame_callbacks { - callback(window, cx); - } - }) - .log_err(); - } - - // Keep presenting if input was recently arriving at a high rate (>= 60fps). - // Once high-rate input is detected, we sustain presentation for 1 second - // to prevent display underclocking during active input. - let needs_present = request_frame_options.require_presentation - || needs_present.get() - || input_rate_tracker.borrow_mut().is_high_rate(); - - if invalidator.is_dirty() || force_render { - measure("frame duration", || { - handle - .update(&mut cx, |_, window, cx| { - if force_render { - // Bypass cached view reuse so we don't replay stale - // atlas tile references after a GPU device recovery. - window.refresh(); - } - let arena_clear_needed = window.draw(cx); - window.present(); - arena_clear_needed.clear(cx); - }) - .log_err(); - }) - } else if needs_present { - handle - .update(&mut cx, |_, window, _| window.present()) - .log_err(); - } - - handle - .update(&mut cx, |_, window, _| { - if window.invalidator.is_dirty() - || !window.next_frame_callbacks.borrow().is_empty() - { - window.platform_window.schedule_frame(); - } - }) - .log_err(); - - // Platforms that stop requesting frames for idle windows only - // deliver another request after a wakeup. If demand remains - // after this frame (the window was re-invalidated mid-draw, or - // animations scheduled next-frame callbacks), re-arm the frame - // source explicitly. - if invalidator.is_dirty() || !next_frame_callbacks.borrow().is_empty() { - invalidator.wake_platform(); - } - } - })); - invalidator.set_platform_waker(platform_window.frame_waker()); - platform_window.on_resize(Box::new({ - let mut cx = cx.to_async(); - move |_, _| { - handle - .update(&mut cx, |_, window, cx| window.bounds_changed(cx)) - .log_err(); - } - })); - platform_window.on_moved(Box::new({ - let mut cx = cx.to_async(); - move || { - handle - .update(&mut cx, |_, window, cx| window.bounds_changed(cx)) - .log_err(); - } - })); - platform_window.on_appearance_changed(Box::new({ - let cx = cx.to_async(); - let foreground_executor = cx.foreground_executor().clone(); - move || { - let mut cx = cx.clone(); - // Defer the update because changing the AppKit appearance may - // synchronously invoke this callback while App is already borrowed. - foreground_executor - .spawn(async move { - handle - .update(&mut cx, |_, window, cx| window.appearance_changed(cx)) - .log_err(); - }) - .detach(); - } - })); - platform_window.on_button_layout_changed(Box::new({ - let mut cx = cx.to_async(); - move || { - handle - .update(&mut cx, |_, window, cx| window.button_layout_changed(cx)) - .log_err(); - } - })); - platform_window.on_active_status_change(Box::new({ - let mut cx = cx.to_async(); - move |active| { - handle - .update(&mut cx, |_, window, cx| { - window.active.set(active); - window.modifiers = window.platform_window.modifiers(); - window.capslock = window.platform_window.capslock(); - window - .activation_observers - .clone() - .retain(&(), |callback| callback(window, cx)); - - window.bounds_changed(cx); - window.refresh(); - - SystemWindowTabController::update_last_active(cx, window.handle.id); - }) - .log_err(); - } - })); - platform_window.on_hover_status_change(Box::new({ - let mut cx = cx.to_async(); - move |active| { - handle - .update(&mut cx, |_, window, _| { - window.hovered.set(active); - window.refresh(); - }) - .log_err(); - } - })); - platform_window.on_input({ - let mut cx = cx.to_async(); - Box::new(move |event| { - handle - .update(&mut cx, |_, window, cx| window.dispatch_event(event, cx)) - .log_err() - .unwrap_or(DispatchEventResult::default()) - }) - }); - platform_window.on_hit_test_window_control({ - let mut cx = cx.to_async(); - Box::new(move || { - handle - .update(&mut cx, |_, window, _cx| { - for (area, hitbox) in &window.rendered_frame.window_control_hitboxes { - if window.mouse_hit_test.ids.contains(&hitbox.id) { - return Some(*area); - } - } - None - }) - .log_err() - .unwrap_or(None) - }) - }); - platform_window.on_move_tab_to_new_window({ - let mut cx = cx.to_async(); - Box::new(move || { - handle - .update(&mut cx, |_, _window, cx| { - SystemWindowTabController::move_tab_to_new_window(cx, handle.window_id()); - }) - .log_err(); - }) - }); - platform_window.on_merge_all_windows({ - let mut cx = cx.to_async(); - Box::new(move || { - handle - .update(&mut cx, |_, _window, cx| { - SystemWindowTabController::merge_all_windows(cx, handle.window_id()); - }) - .log_err(); - }) - }); - platform_window.on_select_next_tab({ - let mut cx = cx.to_async(); - Box::new(move || { - handle - .update(&mut cx, |_, _window, cx| { - SystemWindowTabController::select_next_tab(cx, handle.window_id()); - }) - .log_err(); - }) - }); - platform_window.on_select_previous_tab({ - let mut cx = cx.to_async(); - Box::new(move || { - handle - .update(&mut cx, |_, _window, cx| { - SystemWindowTabController::select_previous_tab(cx, handle.window_id()) - }) - .log_err(); - }) - }); - platform_window.on_toggle_tab_bar({ - let mut cx = cx.to_async(); - Box::new(move || { - handle - .update(&mut cx, |_, window, cx| { - let tab_bar_visible = window.platform_window.tab_bar_visible(); - SystemWindowTabController::set_visible(cx, tab_bar_visible); - }) - .log_err(); - }) - }); - - if let Some(app_id) = app_id { - platform_window.set_app_id(&app_id); - } - - platform_window.map_window().unwrap(); - - Ok(Window { - handle, - invalidator, - removed: false, - platform_window, - display_id, - is_resizable, - is_minimizable, - sprite_atlas, - text_system, - text_rendering_mode: cx.text_rendering_mode.clone(), - rem_size: px(16.), - rem_size_override_stack: SmallVec::new(), - viewport_size: content_size, - layout_engine: Some(TaffyLayoutEngine::new()), - root: None, - element_id_stack: SmallVec::default(), - text_style_stack: Vec::new(), - rendered_entity_stack: Vec::new(), - element_offset_stack: Vec::new(), - content_mask_stack: Vec::new(), - element_opacity: 1.0, - requested_autoscroll: None, - last_text_input_configuration: None, - focused_text_input_active: false, - rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), - next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), - next_frame_callbacks, - next_hitbox_id: HitboxId(0), - next_tooltip_id: TooltipId::default(), - tooltip_bounds: None, - dirty_views: FxHashSet::default(), - focus_listeners: SubscriberSet::new(), - focus_lost_listeners: SubscriberSet::new(), - focus_lost_path: SmallVec::new(), - default_prevented: true, - mouse_position, - mouse_hit_test: HitTest::default(), - modifiers, - capslock, - scale_factor, - bounds_observers: SubscriberSet::new(), - appearance, - appearance_observers: SubscriberSet::new(), - button_layout_observers: SubscriberSet::new(), - active, - hovered, - needs_present, - input_rate_tracker, - #[cfg(feature = "profiler")] - window_profiler: profiler::WindowProfiler::new(handle.window_id())?, - last_input_modality: InputModality::Mouse, - touch_gestures: TouchGestureRecognizer::new( - cx.platform - .gestures() - .map_or_else(GestureTuning::default, |gestures| gestures.tuning()), - ), - touch_prediction_enabled: true, - long_press_timer: None, - long_press_capture: None, - refreshing: false, - activation_observers: SubscriberSet::new(), - focus: None, - focus_enabled: true, - focus_generation: 0, - pending_input: None, - pending_modifier: ModifierState::default(), - pending_input_observers: SubscriberSet::new(), - prompt: None, - client_inset: None, - image_cache_stack: Vec::new(), - captured_hitbox: None, - #[cfg(any(feature = "inspector", debug_assertions))] - inspector: None, - #[cfg(feature = "profiler")] - debug_frame_overlay: crate::debug_overlay::DebugFrameOverlay::new(), - a11y: A11y::new( - a11y_active_flag, - accessibility_force_disabled, - initial_window_title, - ), - }) - } - - pub(crate) fn new_focus_listener( - &self, - value: AnyWindowFocusListener, - ) -> (Subscription, impl FnOnce() + use<>) { - self.focus_listeners.insert((), value) - } -} - -#[derive(Clone, Debug, Default, PartialEq, Eq)] -#[expect(missing_docs)] -pub struct DispatchEventResult { - pub propagate: bool, - pub default_prevented: bool, -} - -/// Indicates which region of the window is visible. Content falling outside of -/// this mask will not be rendered. A mask carries both its rectangular cull -/// bounds and optional rounded corners; the bounds keep scene culling cheap, -/// while the renderer applies the corner shape to every primitive. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct ContentMask { - /// The bounds - pub bounds: Bounds

, - /// The corner radii of the visible region. - pub corner_radii: Corners

, - /// Horizontal corner radii. - pub radii_x: Corners

, - /// Vertical corner radii. - pub radii_y: Corners

, - /// One-based index of the next clip in the scene; zero ends the chain. - pub parent: u32, - /// Explicit GPU record padding. - pub padding: u32, -} - -impl RoundedClip { - /// Convert logical clip geometry to device coordinates. - pub fn scale(&self, scale: f32) -> RoundedClip { - RoundedClip { - bounds: self.bounds.scale(scale), - radii_x: self.radii_x.scale(scale), - radii_y: self.radii_y.scale(scale), - parent: 0, - padding: 0, - } - } - - /// Exact point membership in the rounded rectangle. - pub fn contains(&self, point: Point) -> bool { - if self.bounds.is_empty() || !self.bounds.contains(&point) { - return false; - } - let x = f32::from(point.x - self.bounds.left()); - let y = f32::from(point.y - self.bounds.top()); - let right = f32::from(self.bounds.right() - point.x); - let bottom = f32::from(self.bounds.bottom() - point.y); - [ - (x, y, self.radii_x.top_left, self.radii_y.top_left), - (right, y, self.radii_x.top_right, self.radii_y.top_right), - ( - right, - bottom, - self.radii_x.bottom_right, - self.radii_y.bottom_right, - ), - ( - x, - bottom, - self.radii_x.bottom_left, - self.radii_y.bottom_left, - ), - ] - .into_iter() - .all(|(x, y, rx, ry)| { - let (rx, ry) = (f32::from(rx), f32::from(ry)); - if rx <= 0.0 || ry <= 0.0 || x >= rx || y >= ry { - return true; - } - let dx = (x - rx) / rx; - let dy = (y - ry) / ry; - dx * dx + dy * dy <= 1.0 - }) - } -} - -/// The exact intersection of rectangular and rounded ancestor clips. -/// -/// `bounds` is only a culling rectangle. Rounded curves keep the bounds and -/// radii of their owning element, even when a narrow descendant intersects them. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct ClipRegion { - /// Rectangular intersection used for culling and layout visibility. - pub bounds: Bounds, - /// Rounded constraints still relevant within `bounds`. - pub rounded_clips: SmallVec<[RoundedClip; 1]>, -} - -impl From> for ClipRegion { - fn from(mask: ContentMask) -> Self { - let radii = mask - .corner_radii - .clamp_radii_for_quad_size(mask.bounds.size); - Self::rounded(mask.bounds, radii, radii) - } -} - -impl ClipRegion { - /// Construct a clip with elliptical corners. The caller normalizes radii - /// against the original element before deriving any border inset. - pub fn rounded( - bounds: Bounds, - radii_x: Corners, - radii_y: Corners, - ) -> Self { - let mut region = Self { - bounds, - rounded_clips: SmallVec::new(), - }; - if radii_x.max() > px(0.) && radii_y.max() > px(0.) && !bounds.is_empty() { - region.rounded_clips.push(RoundedClip { - bounds, - radii_x, - radii_y, - parent: 0, - padding: 0, - }); - } - region - } - - /// Intersect without approximating or relocating either region's curves. - pub fn intersect(&self, other: &Self) -> Self { - let bounds = self.bounds.intersect(&other.bounds); - let mut result = Self { - bounds, - rounded_clips: SmallVec::new(), - }; - if bounds.is_empty() { - return result; - } - for clip in self.rounded_clips.iter().chain(&other.rounded_clips) { - // Logical containment cannot prune a curve: the GPU's conservative - // culling rectangle can expand across its antialiased edge. - if result.rounded_clips.contains(clip) { - continue; - } - result.rounded_clips.push(*clip); - } - result - } - - /// Exact logical point membership, independent of rasterization scale. - pub fn contains(&self, point: Point) -> bool { - !self.bounds.is_empty() - && self.bounds.contains(&point) - && self.rounded_clips.iter().all(|clip| clip.contains(point)) - } -} - -/// Concrete clip record exported to the Metal shader bindings. -#[allow(non_camel_case_types)] -pub type RoundedClip_ScaledPixels = RoundedClip; - -#[cfg(test)] -mod tests { - use super::*; - use crate::point; - - #[test] - fn nested_clips_preserve_both_original_shapes() { - let outer: ClipRegion = ContentMask { - bounds: Bounds::from_corners(point(px(0.), px(0.)), point(px(100.), px(100.))), - corner_radii: Corners::all(px(16.)), - ..Default::default() - } - .into(); - for (left, top, right, bottom) in - [(4., 4., 96., 96.), (0., 0., 10., 100.), (7., 1., 94., 80.)] - { - let inner: ClipRegion = ContentMask { - bounds: Bounds::from_corners( - point(px(left), px(top)), - point(px(right), px(bottom)), - ), - ..Default::default() - } - .into(); - let intersection = outer.intersect(&inner); - for x in 0..100 { - for y in 0..100 { - let point = point(px(x as f32 + 0.5), px(y as f32 + 0.5)); - assert_eq!( - intersection.contains(point), - outer.contains(point) && inner.contains(point) - ); - } - } - } - } -} diff --git a/crates/gpui_pre_apple/vendor/gpui/src/color.rs b/crates/gpui_pre_apple/vendor/gpui/src/color.rs deleted file mode 100644 index 3bd893f9..00000000 --- a/crates/gpui_pre_apple/vendor/gpui/src/color.rs +++ /dev/null @@ -1,1070 +0,0 @@ -use anyhow::{Context as _, bail}; -use schemars::{JsonSchema, json_schema}; -use serde::{ - Deserialize, Deserializer, Serialize, Serializer, - de::{self, Visitor}, -}; -use std::borrow::Cow; -use std::{ - fmt::{self, Display, Formatter}, - hash::{Hash, Hasher}, -}; - -/// Convert an RGB hex color code number to a color type -pub fn rgb(hex: u32) -> Rgba { - let [_, r, g, b] = hex.to_be_bytes().map(|b| (b as f32) / 255.0); - Rgba { r, g, b, a: 1.0 } -} - -/// Convert an RGBA hex color code number to [`Rgba`] -pub fn rgba(hex: u32) -> Rgba { - let [r, g, b, a] = hex.to_be_bytes().map(|b| (b as f32) / 255.0); - Rgba { r, g, b, a } -} - -/// Swap from RGBA with premultiplied alpha to BGRA -pub fn swap_rgba_pa_to_bgra(color: &mut [u8]) { - color.swap(0, 2); - if color[3] > 0 { - let a = color[3] as f32 / 255.; - color[0] = (color[0] as f32 / a) as u8; - color[1] = (color[1] as f32 / a) as u8; - color[2] = (color[2] as f32 / a) as u8; - } -} - -/// An RGBA color -#[derive(PartialEq, Clone, Copy, Default)] -#[repr(C)] -pub struct Rgba { - /// The red component of the color, in the range 0.0 to 1.0 - pub r: f32, - /// The green component of the color, in the range 0.0 to 1.0 - pub g: f32, - /// The blue component of the color, in the range 0.0 to 1.0 - pub b: f32, - /// The alpha component of the color, in the range 0.0 to 1.0 - 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 { - /// Create a new [`Rgba`] color by blending this and another color together - pub fn blend(&self, other: Rgba) -> Self { - if other.a >= 1.0 { - other - } else if other.a <= 0.0 { - *self - } else { - 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, - } - } - } - - /// Returns a new RGBA color with the same red, green and blue channels, but - /// with a new alpha value. - /// - /// Example: - /// ``` - /// use gpui::rgba; - /// let color = rgba(0xFF0000FF); - /// let faded = color.alpha(0.25); - /// assert_eq!(faded.a, 0.25); - /// ``` - /// - /// This will return a red color with 25% opacity. - /// - /// Example: - /// ``` - /// use gpui::rgba; - /// let color = rgba(0x3399FFCC); - /// let transparent = color.alpha(0.0); - /// assert_eq!(transparent.a, 0.0); - /// ``` - /// - /// This will return the same blue color, fully transparent. - pub fn alpha(&self, a: f32) -> Self { - Rgba { - r: self.r, - g: self.g, - b: self.b, - a: a.clamp(0., 1.), - } - } - - /// Returns a new RGBA color with the same red, green, and blue channels, - /// but with the alpha channel multiplied by the given factor. - /// - /// Example: - /// ``` - /// use gpui::rgba; - /// let color = rgba(0xFF0000FF); // Fully opaque red - /// let faded = color.opacity(0.5); - /// assert_eq!(faded.a, 0.5); - /// ``` - /// - /// This will return a red color with 50% opacity. - /// - /// Example: - /// ``` - /// use gpui::rgba; - /// let color = rgba(0x3399FFCC); // A light blue with 80% opacity - /// let faded = color.opacity(0.5); - /// assert!((faded.a - 0.4).abs() < 1e-6); - /// ``` - /// - /// This will return the same blue color scaled down to 40% opacity. - pub fn opacity(&self, factor: f32) -> Self { - Rgba { - r: self.r, - g: self.g, - b: self.b, - a: self.a * factor.clamp(0., 1.), - } - } -} - -impl From 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 Visitor<'_> 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(self, value: &str) -> Result { - Rgba::try_from(value).map_err(E::custom) - } -} - -impl JsonSchema for Rgba { - fn schema_name() -> Cow<'static, str> { - "Rgba".into() - } - - fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema { - json_schema!({ - "type": "string", - "pattern": "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$" - }) - } -} - -impl<'de> Deserialize<'de> for Rgba { - fn deserialize>(deserializer: D) -> Result { - deserializer.deserialize_str(RgbaVisitor) - } -} - -impl Serialize for Rgba { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let r = (self.r * 255.0).round() as u8; - let g = (self.g * 255.0).round() as u8; - let b = (self.b * 255.0).round() as u8; - let a = (self.a * 255.0).round() as u8; - - let s = format!("#{r:02x}{g:02x}{b:02x}{a:02x}"); - serializer.serialize_str(&s) - } -} - -impl From 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: r.clamp(0., 1.), - g: g.clamp(0., 1.), - b: b.clamp(0., 1.), - a: color.a, - } - } -} - -impl TryFrom<&'_ str> for Rgba { - type Error = anyhow::Error; - - fn try_from(value: &'_ str) -> Result { - 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"; - const INVALID_UNICODE: &str = "invalid unicode characters in color"; - - 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.get(0..1).with_context(|| { - format!("{INVALID_UNICODE}: r component of #rgb/#rgba for value: '{value}'") - })?, - 16, - )?; - let g = u8::from_str_radix( - hex.get(1..2).with_context(|| { - format!("{INVALID_UNICODE}: g component of #rgb/#rgba for value: '{value}'") - })?, - 16, - )?; - let b = u8::from_str_radix( - hex.get(2..3).with_context(|| { - format!("{INVALID_UNICODE}: b component of #rgb/#rgba for value: '{value}'") - })?, - 16, - )?; - let a = if hex.len() == RGBA { - u8::from_str_radix( - hex.get(3..4).with_context(|| { - format!("{INVALID_UNICODE}: a component of #rgba for value: '{value}'") - })?, - 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.get(0..2).with_context(|| { - format!( - "{}: r component of #rrggbb/#rrggbbaa for value: '{}'", - INVALID_UNICODE, value - ) - })?, - 16, - )?; - let g = u8::from_str_radix( - hex.get(2..4).with_context(|| { - format!( - "{INVALID_UNICODE}: g component of #rrggbb/#rrggbbaa for value: '{value}'" - ) - })?, - 16, - )?; - let b = u8::from_str_radix( - hex.get(4..6).with_context(|| { - format!( - "{INVALID_UNICODE}: b component of #rrggbb/#rrggbbaa for value: '{value}'" - ) - })?, - 16, - )?; - let a = if hex.len() == RRGGBBAA { - u8::from_str_radix( - hex.get(6..8).with_context(|| { - format!( - "{INVALID_UNICODE}: a component of #rrggbbaa for value: '{value}'" - ) - })?, - 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., - }) - } -} - -/// An HSLA color -#[derive(Default, Copy, Clone, Debug)] -#[repr(C)] -pub struct Hsla { - /// Hue, in a range from 0 to 1 - pub h: f32, - - /// Saturation, in a range from 0 to 1 - pub s: f32, - - /// Lightness, in a range from 0 to 1 - pub l: f32, - - /// Alpha, in a range from 0 to 1 - pub a: f32, -} - -#[cfg(feature = "proptest")] -mod property { - use super::Hsla; - use proptest::prelude::*; - - impl Hsla { - /// Proptest [`Strategy`] that produces opaque colors (i.e. alpha = 1). - /// - /// For truly arbitrary colors, use the [`Arbitrary`] implementation. - pub fn opaque_strategy() -> impl Strategy { - (0.0f32..=1.0, 0.0f32..=1.0, 0.0f32..=1.0).prop_map(|(h, s, l)| Hsla { h, s, l, a: 1. }) - } - } - - impl Arbitrary for Hsla { - type Strategy = BoxedStrategy; - type Parameters = (); - - fn arbitrary_with((): Self::Parameters) -> Self::Strategy { - (0.0f32..=1.0, 0.0f32..=1.0, 0.0f32..=1.0, 0.0f32..=1.0) - .prop_map(|(h, s, l, a)| Hsla { h, s, l, a }) - .boxed() - } - } -} - -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 { - Some(self.cmp(other)) - } -} - -impl Ord for Hsla { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - 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 Eq for Hsla {} - -impl Hash for Hsla { - fn hash(&self, state: &mut H) { - state.write_u32(u32::from_be_bytes(self.h.to_be_bytes())); - state.write_u32(u32::from_be_bytes(self.s.to_be_bytes())); - state.write_u32(u32::from_be_bytes(self.l.to_be_bytes())); - state.write_u32(u32::from_be_bytes(self.a.to_be_bytes())); - } -} - -impl Display for Hsla { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "hsla({:.2}, {:.2}%, {:.2}%, {:.2})", - self.h * 360., - self.s * 100., - self.l * 100., - self.a - ) - } -} - -/// Construct an [`Hsla`] object from plain values -pub const 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.), - } -} - -/// Pure black in [`Hsla`] -pub const fn black() -> Hsla { - Hsla { - h: 0., - s: 0., - l: 0., - a: 1., - } -} - -/// Transparent black in [`Hsla`] -pub const fn transparent_black() -> Hsla { - Hsla { - h: 0., - s: 0., - l: 0., - a: 0., - } -} - -/// Transparent white in [`Hsla`] -pub const fn transparent_white() -> Hsla { - Hsla { - h: 0., - s: 0., - l: 1., - a: 0., - } -} - -/// Opaque grey in [`Hsla`], values will be clamped to the range [0, 1] -pub const fn opaque_grey(lightness: f32, opacity: f32) -> Hsla { - Hsla { - h: 0., - s: 0., - l: lightness.clamp(0., 1.), - a: opacity.clamp(0., 1.), - } -} - -/// Pure white in [`Hsla`] -pub const fn white() -> Hsla { - Hsla { - h: 0., - s: 0., - l: 1., - a: 1., - } -} - -/// The color red in [`Hsla`] -pub const fn red() -> Hsla { - Hsla { - h: 0., - s: 1., - l: 0.5, - a: 1., - } -} - -/// The color blue in [`Hsla`] -pub const fn blue() -> Hsla { - Hsla { - h: 0.6666666667, - s: 1., - l: 0.5, - a: 1., - } -} - -/// The color green in [`Hsla`] -pub const fn green() -> Hsla { - Hsla { - h: 0.3333333333, - s: 1., - l: 0.25, - a: 1., - } -} - -/// The color yellow in [`Hsla`] -pub const fn yellow() -> Hsla { - Hsla { - h: 0.1666666667, - s: 1., - l: 0.5, - a: 1., - } -} - -impl Hsla { - /// Converts this HSLA color to an RGBA color. - pub fn to_rgb(self) -> Rgba { - self.into() - } - - /// The color red - pub const fn red() -> Self { - red() - } - - /// The color green - pub const fn green() -> Self { - green() - } - - /// The color blue - pub const fn blue() -> Self { - blue() - } - - /// The color black - pub const fn black() -> Self { - black() - } - - /// The color white - pub const fn white() -> Self { - white() - } - - /// The color transparent black - pub const fn transparent_black() -> Self { - transparent_black() - } - - /// Returns true if the HSLA color is fully transparent, false otherwise. - pub fn is_transparent(&self) -> bool { - self.a == 0.0 - } - - /// Returns true if the HSLA color is fully opaque, false otherwise. - pub fn is_opaque(&self) -> bool { - self.a == 1.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 its 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 { - self - } else { - let converted_self = Rgba::from(self); - let converted_other = Rgba::from(other); - let blended_rgb = converted_self.blend(converted_other); - Hsla::from(blended_rgb) - } - } - - /// Returns a new HSLA color with the same hue, and lightness, but with no saturation. - pub fn grayscale(&self) -> Self { - Hsla { - h: self.h, - s: 0., - l: self.l, - a: self.a, - } - } - - /// 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.); - } - - /// Multiplies the alpha value of the color by a given factor - /// and returns a new HSLA color. - /// - /// Useful for transforming colors with dynamic opacity, - /// like a color from an external source. - /// - /// Example: - /// ``` - /// let color = gpui::red(); - /// let faded_color = color.opacity(0.5); - /// assert_eq!(faded_color.a, 0.5); - /// ``` - /// - /// This will return a red color with half the opacity. - /// - /// Example: - /// ``` - /// use gpui::hsla; - /// let color = hsla(0.7, 1.0, 0.5, 0.7); // A saturated blue - /// let faded_color = color.opacity(0.16); - /// assert!((faded_color.a - 0.112).abs() < 1e-6); - /// ``` - /// - /// This will return a blue color with around ~10% opacity, - /// suitable for an element's hover or selected state. - /// - pub fn opacity(&self, factor: f32) -> Self { - Hsla { - h: self.h, - s: self.s, - l: self.l, - a: self.a * factor.clamp(0., 1.), - } - } - - /// Returns a new HSLA color with the same hue, saturation, - /// and lightness, but with a new alpha value. - /// - /// Example: - /// ``` - /// let color = gpui::red(); - /// let red_color = color.alpha(0.25); - /// assert_eq!(red_color.a, 0.25); - /// ``` - /// - /// This will return a red color with 25% opacity. - /// - /// Example: - /// ``` - /// use gpui::hsla; - /// let color = hsla(0.7, 1.0, 0.5, 0.7); // A saturated blue - /// let faded_color = color.alpha(0.25); - /// assert_eq!(faded_color.a, 0.25); - /// ``` - /// - /// This will return a blue color with 25% opacity. - pub fn alpha(&self, a: f32) -> Self { - Hsla { - h: self.h, - s: self.s, - l: self.l, - a: a.clamp(0., 1.), - } - } -} - -impl From 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 JsonSchema for Hsla { - fn schema_name() -> Cow<'static, str> { - Rgba::schema_name() - } - - fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema { - Rgba::json_schema(generator) - } -} - -impl Serialize for Hsla { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - Rgba::from(*self).serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for Hsla { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Ok(Rgba::deserialize(deserializer)?.into()) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub(crate) enum BackgroundTag { - Solid = 0, - LinearGradient = 1, - PatternSlash = 2, - Checkerboard = 3, -} - -/// A color space for color interpolation. -/// -/// References: -/// - -/// - -#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub enum ColorSpace { - #[default] - /// The sRGB color space. - Srgb = 0, - /// The Oklab color space. - Oklab = 1, -} - -impl Display for ColorSpace { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - ColorSpace::Srgb => write!(f, "sRGB"), - ColorSpace::Oklab => write!(f, "Oklab"), - } - } -} - -/// A background color, which can be either a solid color or a linear gradient. -#[derive(Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub struct Background { - pub(crate) tag: BackgroundTag, - pub(crate) color_space: ColorSpace, - pub(crate) solid: Hsla, - pub(crate) gradient_angle_or_pattern_height: f32, - pub(crate) colors: [LinearColorStop; 2], - /// Padding for alignment for repr(C) layout. - pad: u32, -} - -impl std::fmt::Debug for Background { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self.tag { - BackgroundTag::Solid => write!(f, "Solid({:?})", self.solid), - BackgroundTag::LinearGradient => write!( - f, - "LinearGradient({}, {:?}, {:?})", - self.gradient_angle_or_pattern_height, self.colors[0], self.colors[1] - ), - BackgroundTag::PatternSlash => write!( - f, - "PatternSlash({:?}, {})", - self.solid, self.gradient_angle_or_pattern_height - ), - BackgroundTag::Checkerboard => write!( - f, - "Checkerboard({:?}, {})", - self.solid, self.gradient_angle_or_pattern_height - ), - } - } -} - -impl Eq for Background {} -impl Default for Background { - fn default() -> Self { - Self { - tag: BackgroundTag::Solid, - solid: Hsla::default(), - color_space: ColorSpace::default(), - gradient_angle_or_pattern_height: 0.0, - colors: [LinearColorStop::default(), LinearColorStop::default()], - pad: 0, - } - } -} - -/// Creates a hash pattern background -pub fn pattern_slash(color: impl Into, width: f32, interval: f32) -> Background { - let width_scaled = (width * 255.0) as u32; - let interval_scaled = (interval * 255.0) as u32; - let height = ((width_scaled * 0xFFFF) + interval_scaled) as f32; - - Background { - tag: BackgroundTag::PatternSlash, - solid: color.into(), - gradient_angle_or_pattern_height: height, - ..Default::default() - } -} - -/// Creates a checkerboard pattern background -pub fn checkerboard(color: impl Into, size: f32) -> Background { - Background { - tag: BackgroundTag::Checkerboard, - solid: color.into(), - gradient_angle_or_pattern_height: size, - ..Default::default() - } -} - -/// Creates a solid background color. -pub fn solid_background(color: impl Into) -> Background { - Background { - solid: color.into(), - ..Default::default() - } -} - -/// Creates a LinearGradient background color. -/// -/// The gradient line's angle of direction. A value of `0.` is equivalent to top; increasing values rotate clockwise from there. -/// -/// The `angle` is in degrees value in the range 0.0 to 360.0. -/// -/// -pub fn linear_gradient( - angle: f32, - from: impl Into, - to: impl Into, -) -> Background { - Background { - tag: BackgroundTag::LinearGradient, - gradient_angle_or_pattern_height: angle, - colors: [from.into(), to.into()], - ..Default::default() - } -} - -/// A color stop in a linear gradient. -/// -/// -#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub struct LinearColorStop { - /// The color of the color stop. - pub color: Hsla, - /// The percentage of the gradient, in the range 0.0 to 1.0. - pub percentage: f32, -} - -/// Creates a new linear color stop. -/// -/// The percentage of the gradient, in the range 0.0 to 1.0. -pub fn linear_color_stop(color: impl Into, percentage: f32) -> LinearColorStop { - LinearColorStop { - color: color.into(), - percentage, - } -} - -impl LinearColorStop { - /// Returns a new color stop with the same color, but with a modified alpha value. - pub fn opacity(&self, factor: f32) -> Self { - Self { - percentage: self.percentage, - color: self.color.opacity(factor), - } - } -} - -impl Background { - /// Returns the solid color if this is a solid background, None otherwise. - pub fn as_solid(&self) -> Option { - if self.tag == BackgroundTag::Solid { - Some(self.solid) - } else { - None - } - } - - /// Use specified color space for color interpolation. - /// - /// - pub fn color_space(mut self, color_space: ColorSpace) -> Self { - self.color_space = color_space; - self - } - - /// Returns a new background color with the same hue, saturation, and lightness, but with a modified alpha value. - pub fn opacity(&self, factor: f32) -> Self { - let mut background = *self; - background.solid = background.solid.opacity(factor); - background.colors = [ - self.colors[0].opacity(factor), - self.colors[1].opacity(factor), - ]; - background - } - - /// Returns whether the background color is transparent. - pub fn is_transparent(&self) -> bool { - match self.tag { - BackgroundTag::Solid => self.solid.is_transparent(), - BackgroundTag::LinearGradient => self.colors.iter().all(|c| c.color.is_transparent()), - BackgroundTag::PatternSlash => self.solid.is_transparent(), - BackgroundTag::Checkerboard => self.solid.is_transparent(), - } - } -} - -impl From for Background { - fn from(value: Hsla) -> Self { - Background { - tag: BackgroundTag::Solid, - solid: value, - ..Default::default() - } - } -} - -impl From for Background { - fn from(value: Rgba) -> Self { - Background { - tag: BackgroundTag::Solid, - solid: Hsla::from(value), - ..Default::default() - } - } -} - -#[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)) - } - - #[test] - fn test_background_solid() { - let color = Hsla::from(rgba(0xff0099ff)); - let mut background = Background::from(color); - assert_eq!(background.tag, BackgroundTag::Solid); - assert_eq!(background.solid, color); - - assert_eq!(background.opacity(0.5).solid, color.opacity(0.5)); - assert!(!background.is_transparent()); - background.solid = hsla(0.0, 0.0, 0.0, 0.0); - assert!(background.is_transparent()); - } - - #[test] - fn test_background_linear_gradient() { - let from = linear_color_stop(rgba(0xff0099ff), 0.0); - let to = linear_color_stop(rgba(0x00ff99ff), 1.0); - let background = linear_gradient(90.0, from, to); - assert_eq!(background.tag, BackgroundTag::LinearGradient); - assert_eq!(background.colors[0], from); - assert_eq!(background.colors[1], to); - - assert_eq!(background.opacity(0.5).colors[0], from.opacity(0.5)); - assert_eq!(background.opacity(0.5).colors[1], to.opacity(0.5)); - assert!(!background.is_transparent()); - assert!(background.opacity(0.0).is_transparent()); - } - - #[test] - fn test_rgba_alpha() { - let color = Rgba { - r: 0.2, - g: 0.6, - b: 1.0, - a: 0.8, - }; - - assert_eq!(color.alpha(0.25).a, 0.25); - assert_eq!(color.alpha(1.5).a, 1.0); - } - - #[test] - fn test_rgba_opacity() { - let color = Rgba { - r: 0.2, - g: 0.6, - b: 1.0, - a: 0.8, - }; - assert!((color.opacity(0.5).a - 0.4).abs() < 1e-6); - assert_eq!(color.opacity(2.0).a, 0.8); - } -} diff --git a/crates/gpui_pre_apple/vendor/gpui/src/geometry.rs b/crates/gpui_pre_apple/vendor/gpui/src/geometry.rs deleted file mode 100644 index 05dafbf9..00000000 --- a/crates/gpui_pre_apple/vendor/gpui/src/geometry.rs +++ /dev/null @@ -1,4008 +0,0 @@ -//! The GPUI geometry module is a collection of types and traits that -//! can be used to describe common units, concepts, and the relationships -//! between them. - -use anyhow::{Context as _, anyhow}; -use core::fmt::Debug; -use derive_more::{Add, AddAssign, Div, DivAssign, Mul, Neg, Sub, SubAssign}; -use refineable::Refineable; -use schemars::{JsonSchema, json_schema}; -use serde::{Deserialize, Deserializer, Serialize, Serializer, de}; -use std::borrow::Cow; -use std::ops::{AddAssign, Range}; -use std::{ - cmp::{self, PartialOrd}, - fmt::{self, Display}, - hash::Hash, - ops::{Add, Div, Mul, MulAssign, Neg, Sub}, -}; -use taffy::prelude::{TaffyGridLine, TaffyGridSpan}; - -use crate::{App, DisplayId}; - -/// Axis in a 2D cartesian space. -#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] -pub enum Axis { - /// The y axis, or up and down - Vertical, - /// The x axis, or left and right - Horizontal, -} - -impl Axis { - /// Swap this axis to the opposite axis. - pub fn invert(self) -> Self { - match self { - Axis::Vertical => Axis::Horizontal, - Axis::Horizontal => Axis::Vertical, - } - } -} - -/// A trait for accessing the given unit along a certain axis. -pub trait Along { - /// The unit associated with this type - type Unit; - - /// Returns the unit along the given axis. - fn along(&self, axis: Axis) -> Self::Unit; - - /// Applies the given function to the unit along the given axis and returns a new value. - fn apply_along(&self, axis: Axis, f: impl FnOnce(Self::Unit) -> Self::Unit) -> Self; -} - -/// Describes a location in a 2D cartesian 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 gpui::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, - Serialize, - Deserialize, - JsonSchema, - Hash, - Neg, -)] -#[refineable(Debug, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub struct Point { - /// The x coordinate of the point. - pub x: T, - /// The y coordinate of the point. - pub y: T, -} - -/// Constructs a new `Point` 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` with the specified coordinates. -/// -/// # Examples -/// -/// ``` -/// use gpui::point; -/// let p = point(10, 20); -/// assert_eq!(p.x, 10); -/// assert_eq!(p.y, 20); -/// ``` -pub const fn point(x: T, y: T) -> Point { - Point { x, y } -} - -impl Point { - /// 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 - /// - /// ``` - /// use gpui::Point; - /// 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` by applying the given function to both coordinates. - /// - /// This method allows for converting a `Point` to a `Point` 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 gpui::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 }); - /// ``` - #[must_use] - pub fn map(&self, f: impl Fn(T) -> U) -> Point { - Point { - x: f(self.x.clone()), - y: f(self.y.clone()), - } - } -} - -impl Along for Point { - 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 { - 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 { - /// 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 gpui::{Point, Pixels, ScaledPixels}; - /// let p = Point { x: Pixels::from(10.0), y: Pixels::from(20.0) }; - /// let scaled_p = p.scale(1.5); - /// assert_eq!(scaled_p, Point { x: ScaledPixels::from(15.0), y: ScaledPixels::from(30.0) }); - /// ``` - pub fn scale(&self, factor: f32) -> Point { - 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 gpui::{Pixels, Point}; - /// let p = Point { x: Pixels::from(3.0), y: Pixels::from(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 Point -where - T: Sub + Clone + Debug + Default + PartialEq, -{ - /// Get the position of this point, relative to the given origin - pub fn relative_to(&self, origin: &Point) -> Point { - point( - self.x.clone() - origin.x.clone(), - self.y.clone() - origin.y.clone(), - ) - } -} - -impl Mul for Point -where - T: Mul + Clone + Debug + Default + PartialEq, - Rhs: Clone + Debug, -{ - type Output = Point; - - fn mul(self, rhs: Rhs) -> Self::Output { - Point { - x: self.x * rhs.clone(), - y: self.y * rhs, - } - } -} - -impl MulAssign for Point -where - T: Mul + Clone + Debug + Default + PartialEq, - S: Clone, -{ - fn mul_assign(&mut self, rhs: S) { - self.x = self.x.clone() * rhs.clone(); - self.y = self.y.clone() * rhs; - } -} - -impl Div for Point -where - T: Div + Clone + Debug + Default + PartialEq, - S: Clone, -{ - type Output = Self; - - fn div(self, rhs: S) -> Self::Output { - Self { - x: self.x / rhs.clone(), - y: self.y / rhs, - } - } -} - -impl Point -where - T: PartialOrd + Clone + Debug + Default + PartialEq, -{ - /// 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 gpui::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 gpui::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 gpui::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 Clone for Point { - fn clone(&self) -> Self { - Self { - x: self.x.clone(), - y: self.y.clone(), - } - } -} - -impl Display for Point { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({}, {})", self.x, self.y) - } -} - -/// 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( - Add, Clone, Copy, Default, Deserialize, Div, Hash, Neg, PartialEq, Refineable, Serialize, Sub, -)] -#[refineable(Debug, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub struct Size { - /// The width component of the size. - pub width: T, - /// The height component of the size. - pub height: T, -} - -impl Size { - /// Create a new Size, a synonym for [`size`] - pub fn new(width: T, height: T) -> Self { - size(width, height) - } -} - -/// Constructs a new `Size` with the provided width and height. -/// -/// # Arguments -/// -/// * `width` - The width component of the `Size`. -/// * `height` - The height component of the `Size`. -/// -/// # Examples -/// -/// ``` -/// use gpui::size; -/// let my_size = size(10, 20); -/// assert_eq!(my_size.width, 10); -/// assert_eq!(my_size.height, 20); -/// ``` -pub const fn size(width: T, height: T) -> Size -where - T: Clone + Debug + Default + PartialEq, -{ - Size { width, height } -} - -impl Size -where - T: Clone + Debug + Default + PartialEq, -{ - /// Applies a function to the width and height of the size, producing a new `Size`. - /// - /// This method allows for converting a `Size` to a `Size` 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 gpui::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(&self, f: impl Fn(T) -> U) -> Size - where - U: Clone + Debug + Default + PartialEq, - { - Size { - width: f(self.width.clone()), - height: f(self.height.clone()), - } - } -} - -impl Size -where - T: Clone + Debug + Default + PartialEq + Half, -{ - /// Compute the center point of the size.g - pub fn center(&self) -> Point { - Point { - x: self.width.half(), - y: self.height.half(), - } - } -} - -impl Size { - /// 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` that is proportionally larger or smaller - /// depending on the factor. - /// - /// # Arguments - /// - /// * `factor` - The scaling factor to apply to the width and height. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Size, Pixels, ScaledPixels}; - /// let size = Size { width: Pixels::from(100.0), height: Pixels::from(50.0) }; - /// let scaled_size = size.scale(2.0); - /// assert_eq!(scaled_size, Size { width: ScaledPixels::from(200.0), height: ScaledPixels::from(100.0) }); - /// ``` - pub fn scale(&self, factor: f32) -> Size { - Size { - width: self.width.scale(factor), - height: self.height.scale(factor), - } - } -} - -impl Along for Size -where - T: Clone + Debug + Default + PartialEq, -{ - 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 Size -where - T: PartialOrd + Clone + Debug + Default + PartialEq, -{ - /// 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 gpui::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() - }, - } - } - - /// Returns a new `Size` with the minimum width and height from `self` and `other`. - /// - /// # Arguments - /// - /// * `other` - A reference to another `Size` to compare with `self`. - /// - /// # Examples - /// - /// ``` - /// # use gpui::Size; - /// let size1 = Size { width: 30, height: 40 }; - /// let size2 = Size { width: 50, height: 20 }; - /// let min_size = size1.min(&size2); - /// assert_eq!(min_size, Size { width: 30, height: 20 }); - /// ``` - pub fn min(&self, other: &Self) -> Self { - Size { - width: if self.width >= other.width { - other.width.clone() - } else { - self.width.clone() - }, - height: if self.height >= other.height { - other.height.clone() - } else { - self.height.clone() - }, - } - } -} - -impl Mul for Size -where - T: Mul + Clone + Debug + Default + PartialEq, - Rhs: Clone + Debug + Default + PartialEq, -{ - type Output = Size; - - fn mul(self, rhs: Rhs) -> Self::Output { - Size { - width: self.width * rhs.clone(), - height: self.height * rhs, - } - } -} - -impl MulAssign for Size -where - T: Mul + Clone + Debug + Default + PartialEq, - S: Clone, -{ - fn mul_assign(&mut self, rhs: S) { - self.width = self.width.clone() * rhs.clone(); - self.height = self.height.clone() * rhs; - } -} - -impl Eq for Size where T: Eq + Clone + Debug + Default + PartialEq {} - -impl Debug for Size -where - T: Clone + Debug + Default + PartialEq, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Size {{ {:?} × {:?} }}", self.width, self.height) - } -} - -impl Display for Size { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} × {}", self.width, self.height) - } -} - -impl From> for Size { - fn from(point: Point) -> Self { - Self { - width: point.x, - height: point.y, - } - } -} - -impl From> for Size { - fn from(size: Size) -> Self { - Size { - width: size.width.into(), - height: size.height.into(), - } - } -} - -impl From> for Size { - fn from(size: Size) -> Self { - Size { - width: size.width.into(), - height: size.height.into(), - } - } -} - -impl Size { - /// 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` 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 { - /// 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` 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` which defines the top left corner of the rectangle, -/// and the size is represented as a `Size` which defines the width and height of the rectangle. -/// -/// # Examples -/// -/// ``` -/// # use gpui::{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, Copy, Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] -#[refineable(Debug)] -#[repr(C)] -pub struct Bounds { - /// The origin point of this area. - pub origin: Point, - /// The size of the rectangle. - pub size: Size, -} - -/// Create a bounds with the given origin and size -pub fn bounds( - origin: Point, - size: Size, -) -> Bounds { - Bounds { origin, size } -} - -impl Bounds { - /// Generate a centered bounds for the given display or primary display if none is provided - pub fn centered(display_id: Option, size: Size, cx: &App) -> Self { - let display = display_id - .and_then(|id| cx.find_display(id)) - .or_else(|| cx.primary_display()); - - display - .map(|display| Bounds::centered_at(display.bounds().center(), size)) - .unwrap_or_else(|| Bounds { - origin: point(px(0.), px(0.)), - size, - }) - } - - /// Generate maximized bounds for the given display or primary display if none is provided - pub fn maximized(display_id: Option, cx: &App) -> Self { - let display = display_id - .and_then(|id| cx.find_display(id)) - .or_else(|| cx.primary_display()); - - display - .map(|display| display.bounds()) - .unwrap_or_else(|| Bounds { - origin: point(px(0.), px(0.)), - size: size(px(1024.), px(768.)), - }) - } -} - -impl Bounds -where - T: Clone + Debug + Default + PartialEq, -{ - /// Creates a new `Bounds` with the specified origin and size. - /// - /// # Arguments - /// - /// * `origin` - A `Point` representing the origin of the bounds. - /// * `size` - A `Size` representing the size of the bounds. - /// - /// # Returns - /// - /// Returns a `Bounds` that has the given origin and size. - pub fn new(origin: Point, size: Size) -> Self { - Bounds { origin, size } - } -} - -impl Bounds -where - T: Sub + Clone + Debug + Default + PartialEq, -{ - /// Constructs a `Bounds` from two corner points: the top left and bottom right corners. - /// - /// This function calculates the origin and size of the `Bounds` based on the provided corner points. - /// The origin is set to the top left corner, and the size is determined by the difference between - /// the x and y coordinates of the bottom right and top left points. - /// - /// # Arguments - /// - /// * `top_left` - A `Point` representing the top left corner of the rectangle. - /// * `bottom_right` - A `Point` representing the bottom right corner of the rectangle. - /// - /// # Returns - /// - /// Returns a `Bounds` that encompasses the area defined by the two corner points. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point}; - /// let top_left = Point { x: 0, y: 0 }; - /// let bottom_right = Point { x: 10, y: 10 }; - /// let bounds = Bounds::from_corners(top_left, bottom_right); - /// - /// assert_eq!(bounds.origin, top_left); - /// assert_eq!(bounds.size.width, 10); - /// assert_eq!(bounds.size.height, 10); - /// ``` - pub fn from_corners(top_left: Point, bottom_right: Point) -> Self { - let origin = Point { - x: top_left.x.clone(), - y: top_left.y.clone(), - }; - let size = Size { - width: bottom_right.x - top_left.x, - height: bottom_right.y - top_left.y, - }; - Bounds { origin, size } - } -} - -impl Bounds -where - T: Sub + Half + Clone + Debug + Default + PartialEq, -{ - /// Constructs a `Bounds` from a corner point and size. The specified corner will be placed at - /// the specified origin. - pub fn from_anchor_and_size(corner: Anchor, origin: Point, size: Size) -> Bounds { - let origin = match corner { - Anchor::TopLeft => origin, - Anchor::TopRight => Point { - x: origin.x - size.width.clone(), - y: origin.y, - }, - Anchor::BottomLeft => Point { - x: origin.x, - y: origin.y - size.height.clone(), - }, - Anchor::BottomRight => Point { - x: origin.x - size.width.clone(), - y: origin.y - size.height.clone(), - }, - Anchor::TopCenter => Point { - x: origin.x - size.width.half(), - y: origin.y, - }, - Anchor::BottomCenter => Point { - x: origin.x - size.width.half(), - y: origin.y - size.height.clone(), - }, - Anchor::LeftCenter => Point { - x: origin.x, - y: origin.y - size.height.half(), - }, - Anchor::RightCenter => Point { - x: origin.x - size.width.clone(), - y: origin.y - size.height.half(), - }, - }; - - Bounds { origin, size } - } -} - -impl Bounds -where - T: Sub + Half + Clone + Debug + Default + PartialEq, -{ - /// Creates a new bounds centered at the given point. - pub fn centered_at(center: Point, size: Size) -> Self { - let origin = Point { - x: center.x - size.width.half(), - y: center.y - size.height.half(), - }; - Self::new(origin, size) - } -} - -impl Bounds -where - T: Add + Half + Clone + Debug + Default + PartialEq, -{ - /// Returns the top center point of the bounds. - pub fn top_center(&self) -> Point { - Point { - x: self.origin.x.clone() + self.size.width.half(), - y: self.origin.y.clone(), - } - } - - /// Returns the bottom center point of the bounds. - pub fn bottom_center(&self) -> Point { - Point { - x: self.origin.x.clone() + self.size.width.half(), - y: self.origin.y.clone() + self.size.height.clone(), - } - } - - /// Returns the left center point of the bounds. - pub fn left_center(&self) -> Point { - Point { - x: self.origin.x.clone(), - y: self.origin.y.clone() + self.size.height.half(), - } - } - - /// Returns the right center point of the bounds. - pub fn right_center(&self) -> Point { - Point { - x: self.origin.x.clone() + self.size.width.clone(), - y: self.origin.y.clone() + self.size.height.half(), - } - } -} - -impl Bounds -where - T: PartialOrd + Add + Clone + Debug + Default + PartialEq, -{ - /// 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 gpui::{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) -> bool { - let my_lower_right = self.bottom_right(); - let their_lower_right = other.bottom_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 - } -} - -impl Bounds -where - T: Add + Half + Clone + Debug + Default + PartialEq, -{ - /// 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` where `T` is the type of the - /// coordinate system. - /// - /// # Returns - /// - /// A `Point` representing the center of the bounds. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{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 { - Point { - x: self.origin.x.clone() + self.size.width.clone().half(), - y: self.origin.y.clone() + self.size.height.clone().half(), - } - } -} - -impl Bounds -where - T: Add + Clone + Debug + Default + PartialEq, -{ - /// Calculates the half perimeter of a rectangle defined by the bounds. - /// - /// The half perimeter is calculated as the sum of the width and the height of the rectangle. - /// This method is generic over the type `T` which must implement the `Sub` trait to allow - /// calculation of the width and height from the bounds' origin and size, as well as the `Add` trait - /// to sum the width and height for the half perimeter. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point, Size}; - /// let bounds = Bounds { - /// origin: Point { x: 0, y: 0 }, - /// size: Size { width: 10, height: 20 }, - /// }; - /// let half_perimeter = bounds.half_perimeter(); - /// assert_eq!(half_perimeter, 30); - /// ``` - pub fn half_perimeter(&self) -> T { - self.size.width.clone() + self.size.height.clone() - } -} - -impl Bounds -where - T: Add + Sub + Clone + Debug + Default + PartialEq, -{ - /// 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 gpui::{Bounds, Point, Size}; - /// let mut bounds = Bounds { - /// origin: Point { x: 10, y: 10 }, - /// size: Size { width: 10, height: 10 }, - /// }; - /// let expanded_bounds = bounds.dilate(5); - /// assert_eq!(expanded_bounds, Bounds { - /// origin: Point { x: 5, y: 5 }, - /// size: Size { width: 20, height: 20 }, - /// }); - /// ``` - #[must_use] - pub fn dilate(&self, amount: T) -> Bounds { - let double_amount = amount.clone() + amount.clone(); - Bounds { - origin: self.origin.clone() - point(amount.clone(), amount), - size: self.size.clone() + size(double_amount.clone(), double_amount), - } - } - - /// Extends the bounds different amounts in each direction. - #[must_use] - pub fn extend(&self, amount: Edges) -> Bounds { - Bounds { - origin: self.origin.clone() - point(amount.left.clone(), amount.top.clone()), - size: self.size.clone() - + size( - amount.left.clone() + amount.right.clone(), - amount.top.clone() + amount.bottom, - ), - } - } -} - -impl Bounds -where - T: Add - + Sub - + Neg - + Clone - + Debug - + Default - + PartialEq, -{ - /// Inset the bounds by a specified amount. Equivalent to `dilate` with the amount negated. - /// - /// Note that this may panic if T does not support negative values. - pub fn inset(&self, amount: T) -> Self { - self.dilate(-amount) - } -} - -impl + Sub + Clone + Debug + Default + PartialEq> - Bounds -{ - /// 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 gpui::{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 bottom_right = self - .bottom_right() - .min(&other.bottom_right()) - .max(&upper_left); - Self::from_corners(upper_left, bottom_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 gpui::{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.bottom_right().max(&other.bottom_right()); - Bounds::from_corners(top_left, bottom_right) - } -} - -impl Bounds -where - T: Add + Sub + Clone + Debug + Default + PartialEq, -{ - /// Computes the space available within outer bounds. - pub fn space_within(&self, outer: &Self) -> Edges { - Edges { - top: self.top() - outer.top(), - right: outer.right() - self.right(), - bottom: outer.bottom() - self.bottom(), - left: self.left() - outer.left(), - } - } -} - -impl Mul for Bounds -where - T: Mul + Clone + Debug + Default + PartialEq, - Point: Mul>, - Rhs: Clone + Debug + Default + PartialEq, -{ - type Output = Bounds; - - fn mul(self, rhs: Rhs) -> Self::Output { - Bounds { - origin: self.origin * rhs.clone(), - size: self.size * rhs, - } - } -} - -impl MulAssign for Bounds -where - T: Mul + Clone + Debug + Default + PartialEq, - S: Clone, -{ - fn mul_assign(&mut self, rhs: S) { - self.origin *= rhs.clone(); - self.size *= rhs; - } -} - -impl Div for Bounds -where - Size: Div>, - T: Div + Clone + Debug + Default + PartialEq, - S: Clone, -{ - type Output = Self; - - fn div(self, rhs: S) -> Self { - Self { - origin: self.origin / rhs.clone(), - size: self.size / rhs, - } - } -} - -impl Add> for Bounds -where - T: Add + Clone + Debug + Default + PartialEq, -{ - type Output = Self; - - fn add(self, rhs: Point) -> Self { - Self { - origin: self.origin + rhs, - size: self.size, - } - } -} - -impl Sub> for Bounds -where - T: Sub + Clone + Debug + Default + PartialEq, -{ - type Output = Self; - - fn sub(self, rhs: Point) -> Self { - Self { - origin: self.origin - rhs, - size: self.size, - } - } -} - -impl From> for Point { - fn from(size: Size) -> Self { - Self { - x: size.width, - y: size.height, - } - } -} - -impl Bounds -where - T: Add + Clone + Debug + Default + PartialEq, -{ - /// 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 top right corner point of the bounds. - /// - /// # Returns - /// - /// A `Point` representing the top right corner of the bounds. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point, Size}; - /// let bounds = Bounds { - /// origin: Point { x: 0, y: 0 }, - /// size: Size { width: 10, height: 20 }, - /// }; - /// let top_right = bounds.top_right(); - /// assert_eq!(top_right, Point { x: 10, y: 0 }); - /// ``` - pub fn top_right(&self) -> Point { - Point { - x: self.origin.x.clone() + self.size.width.clone(), - y: self.origin.y.clone(), - } - } - - /// Returns the bottom right corner point of the bounds. - /// - /// # Returns - /// - /// A `Point` representing the bottom right corner of the bounds. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point, Size}; - /// let bounds = Bounds { - /// origin: Point { x: 0, y: 0 }, - /// size: Size { width: 10, height: 20 }, - /// }; - /// let bottom_right = bounds.bottom_right(); - /// assert_eq!(bottom_right, Point { x: 10, y: 20 }); - /// ``` - pub fn bottom_right(&self) -> Point { - Point { - x: self.origin.x.clone() + self.size.width.clone(), - y: self.origin.y.clone() + self.size.height.clone(), - } - } - - /// Returns the bottom left corner point of the bounds. - /// - /// # Returns - /// - /// A `Point` representing the bottom left corner of the bounds. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point, Size}; - /// let bounds = Bounds { - /// origin: Point { x: 0, y: 0 }, - /// size: Size { width: 10, height: 20 }, - /// }; - /// let bottom_left = bounds.bottom_left(); - /// assert_eq!(bottom_left, Point { x: 0, y: 20 }); - /// ``` - pub fn bottom_left(&self) -> Point { - Point { - x: self.origin.x.clone(), - y: self.origin.y.clone() + self.size.height.clone(), - } - } -} - -impl Bounds -where - T: Add + Half + Clone + Debug + Default + PartialEq, -{ - /// Returns the requested corner point of the bounds. - /// - /// # Returns - /// - /// A `Point` representing the corner of the bounds requested by the parameter. - /// - /// # Examples - /// - /// ``` - /// use gpui::{Bounds, Anchor, Point, Size}; - /// let bounds = Bounds { - /// origin: Point { x: 0, y: 0 }, - /// size: Size { width: 10, height: 20 }, - /// }; - /// let bottom_left = bounds.corner(Anchor::BottomLeft); - /// assert_eq!(bottom_left, Point { x: 0, y: 20 }); - /// ``` - pub fn corner(&self, corner: Anchor) -> Point { - match corner { - Anchor::TopLeft => self.origin.clone(), - Anchor::TopRight => self.top_right(), - Anchor::BottomLeft => self.bottom_left(), - Anchor::BottomRight => self.bottom_right(), - Anchor::TopCenter => self.top_center(), - Anchor::BottomCenter => self.bottom_center(), - Anchor::LeftCenter => self.left_center(), - Anchor::RightCenter => self.right_center(), - } - } -} - -impl Bounds -where - T: Add + PartialOrd + Clone + Debug + Default + PartialEq, -{ - /// 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` that represents the point to check. - /// - /// # Returns - /// - /// Returns `true` if the point is within the bounds, `false` otherwise. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Point, Bounds, Size}; - /// 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(&inside_point)); - /// assert!(!bounds.contains(&outside_point)); - /// ``` - pub fn contains(&self, point: &Point) -> 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() - } - - /// Checks if this bounds is completely contained within another bounds. - /// - /// This method determines whether the current bounds is entirely enclosed by the given bounds. - /// A bounds is considered to be contained within another if its origin (top-left corner) and - /// its bottom-right corner are both contained within the other bounds. - /// - /// # Arguments - /// - /// * `other` - A reference to another `Bounds` that might contain this bounds. - /// - /// # Returns - /// - /// Returns `true` if this bounds is completely inside the other bounds, `false` otherwise. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point, Size}; - /// let outer_bounds = Bounds { - /// origin: Point { x: 0, y: 0 }, - /// size: Size { width: 20, height: 20 }, - /// }; - /// let inner_bounds = Bounds { - /// origin: Point { x: 5, y: 5 }, - /// size: Size { width: 10, height: 10 }, - /// }; - /// let overlapping_bounds = Bounds { - /// origin: Point { x: 15, y: 15 }, - /// size: Size { width: 10, height: 10 }, - /// }; - /// - /// assert!(inner_bounds.is_contained_within(&outer_bounds)); - /// assert!(!overlapping_bounds.is_contained_within(&outer_bounds)); - /// ``` - pub fn is_contained_within(&self, other: &Self) -> bool { - other.contains(&self.origin) && other.contains(&self.bottom_right()) - } - - /// Applies a function to the origin and size of the bounds, producing a new `Bounds`. - /// - /// This method allows for converting a `Bounds` to a `Bounds` 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` with the origin and size mapped by the provided function. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{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(&self, f: impl Fn(T) -> U) -> Bounds - where - U: Clone + Debug + Default + PartialEq, - { - Bounds { - origin: self.origin.map(&f), - size: self.size.map(f), - } - } - - /// Applies a function to the origin of the bounds, producing a new `Bounds` with the new origin - /// - /// # Examples - /// - /// ``` - /// # use gpui::{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_origin(|value| value * 1.5); - /// - /// assert_eq!(new_bounds, Bounds { - /// origin: Point { x: 15.0, y: 15.0 }, - /// size: Size { width: 10.0, height: 20.0 }, - /// }); - /// ``` - pub fn map_origin(self, f: impl Fn(T) -> T) -> Bounds { - Bounds { - origin: self.origin.map(f), - size: self.size, - } - } - - /// Applies a function to the origin of the bounds, producing a new `Bounds` with the new origin - /// - /// # Examples - /// - /// ``` - /// # use gpui::{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_size(|value| value * 1.5); - /// - /// assert_eq!(new_bounds, Bounds { - /// origin: Point { x: 10.0, y: 10.0 }, - /// size: Size { width: 15.0, height: 30.0 }, - /// }); - /// ``` - pub fn map_size(self, f: impl Fn(T) -> T) -> Bounds { - Bounds { - origin: self.origin, - size: self.size.map(f), - } - } -} - -impl Bounds -where - T: Add + Sub + PartialOrd + Clone + Debug + Default + PartialEq, -{ - /// Convert a point to the coordinate space defined by this Bounds - pub fn localize(&self, point: &Point) -> Option> { - self.contains(point) - .then(|| point.relative_to(&self.origin)) - } -} - -/// Checks if the bounds represent an empty area. -/// -/// # Returns -/// -/// Returns `true` if either the width or the height of the bounds is less than or equal to zero, indicating an empty area. -impl Bounds { - /// Checks if the bounds represent an empty area. - /// - /// # Returns - /// - /// Returns `true` if either the width or the height of the bounds is less than or equal to zero, indicating an empty area. - #[must_use] - pub fn is_empty(&self) -> bool { - self.size.width <= T::default() || self.size.height <= T::default() - } -} - -impl> Display for Bounds { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{} - {} (size {})", - self.origin, - self.bottom_right(), - self.size - ) - } -} - -impl Size { - /// Converts the size from physical to logical pixels. - pub fn to_pixels(self, scale_factor: f32) -> Size { - size( - px(self.width.0 as f32 / scale_factor), - px(self.height.0 as f32 / scale_factor), - ) - } -} - -impl Size { - /// Converts the size from logical to physical pixels. - pub fn to_device_pixels(self, scale_factor: f32) -> Size { - size( - DevicePixels((self.width.0 * scale_factor).round() as i32), - DevicePixels((self.height.0 * scale_factor).round() as i32), - ) - } -} - -impl Bounds { - /// 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` 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` that represents the scaled bounds. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Bounds, Point, Size, Pixels, ScaledPixels, DevicePixels}; - /// let bounds = Bounds { - /// origin: Point { x: Pixels::from(10.0), y: Pixels::from(20.0) }, - /// size: Size { width: Pixels::from(30.0), height: Pixels::from(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::from(20.0), - /// y: ScaledPixels::from(40.0), - /// }, - /// size: Size { - /// width: ScaledPixels::from(60.0), - /// height: ScaledPixels::from(80.0) - /// }, - /// }); - /// ``` - pub fn scale(&self, factor: f32) -> Bounds { - Bounds { - origin: self.origin.scale(factor), - size: self.size.scale(factor), - } - } - - /// Convert the bounds from logical pixels to physical pixels - pub fn to_device_pixels(self, factor: f32) -> Bounds { - Bounds { - origin: point( - DevicePixels((self.origin.x.0 * factor).round() as i32), - DevicePixels((self.origin.y.0 * factor).round() as i32), - ), - size: self.size.to_device_pixels(factor), - } - } -} - -impl Bounds { - /// Convert the bounds from physical pixels to logical pixels - pub fn to_pixels(self, scale_factor: f32) -> Bounds { - Bounds { - origin: point( - px(self.origin.x.0 as f32 / scale_factor), - px(self.origin.y.0 as f32 / scale_factor), - ), - size: self.size.to_pixels(scale_factor), - } - } -} - -/// 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 gpui::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, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub struct Edges { - /// The size of the top edge. - pub top: T, - /// The size of the right edge. - pub right: T, - /// The size of the bottom edge. - pub bottom: T, - /// The size of the left edge. - pub left: T, -} - -impl Mul for Edges -where - T: Mul + Clone + Debug + Default + PartialEq, -{ - 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 * rhs.left, - } - } -} - -impl MulAssign for Edges -where - T: Mul + Clone + Debug + Default + PartialEq, - 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 Copy for Edges {} - -impl Edges { - /// 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 gpui::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`. - /// - /// This method allows for converting an `Edges` to an `Edges` 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` with each field mapped by the provided function. - /// - /// # Examples - /// - /// ``` - /// # use gpui::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(&self, f: impl Fn(&T) -> U) -> Edges - where - U: Clone + Debug + Default + PartialEq, - { - 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 gpui::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 bool>(&self, predicate: F) -> bool { - predicate(&self.top) - || predicate(&self.right) - || predicate(&self.bottom) - || predicate(&self.left) - } -} - -impl Edges { - /// 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` with all edges set to `Length::Auto`. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Edges, Length}; - /// 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` with all edges set to zero length. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{DefiniteLength, Edges, Length, Pixels}; - /// let no_edges = Edges::::zero(); - /// assert_eq!(no_edges.top, Length::Definite(DefiniteLength::from(Pixels::ZERO))); - /// assert_eq!(no_edges.right, Length::Definite(DefiniteLength::from(Pixels::ZERO))); - /// assert_eq!(no_edges.bottom, Length::Definite(DefiniteLength::from(Pixels::ZERO))); - /// assert_eq!(no_edges.left, Length::Definite(DefiniteLength::from(Pixels::ZERO))); - /// ``` - pub fn zero() -> Self { - Self { - top: px(0.).into(), - right: px(0.).into(), - bottom: px(0.).into(), - left: px(0.).into(), - } - } -} - -impl Edges { - /// 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` with all edges set to zero length. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{px, DefiniteLength, Edges}; - /// let no_edges = Edges::::zero(); - /// assert_eq!(no_edges.top, DefiniteLength::from(px(0.))); - /// assert_eq!(no_edges.right, DefiniteLength::from(px(0.))); - /// assert_eq!(no_edges.bottom, DefiniteLength::from(px(0.))); - /// assert_eq!(no_edges.left, DefiniteLength::from(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` representing the size of the parent element. - /// * `rem_size` - `Pixels` representing the size of one REM unit. - /// - /// # Returns - /// - /// Returns an `Edges` representing the edges with lengths converted to pixels. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Edges, DefiniteLength, px, AbsoluteLength, rems, 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, rem_size: Pixels) -> Edges { - 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 { - /// 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` with all edges set to zero length. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{AbsoluteLength, Edges, Pixels}; - /// let no_edges = Edges::::zero(); - /// assert_eq!(no_edges.top, AbsoluteLength::Pixels(Pixels::ZERO)); - /// assert_eq!(no_edges.right, AbsoluteLength::Pixels(Pixels::ZERO)); - /// assert_eq!(no_edges.bottom, AbsoluteLength::Pixels(Pixels::ZERO)); - /// assert_eq!(no_edges.left, AbsoluteLength::Pixels(Pixels::ZERO)); - /// ``` - 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` representing the edges with lengths converted to pixels. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Edges, AbsoluteLength, Pixels, px, rems}; - /// 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 { - 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 { - /// Scales the `Edges` by a given factor, returning `Edges`. - /// - /// 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` where each edge is the result of scaling the original edge by the given factor. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Edges, Pixels, ScaledPixels}; - /// let edges = Edges { - /// top: Pixels::from(10.0), - /// right: Pixels::from(20.0), - /// bottom: Pixels::from(30.0), - /// left: Pixels::from(40.0), - /// }; - /// let scaled_edges = edges.scale(2.0); - /// assert_eq!(scaled_edges.top, ScaledPixels::from(20.0)); - /// assert_eq!(scaled_edges.right, ScaledPixels::from(40.0)); - /// assert_eq!(scaled_edges.bottom, ScaledPixels::from(60.0)); - /// assert_eq!(scaled_edges.left, ScaledPixels::from(80.0)); - /// ``` - pub fn scale(&self, factor: f32) -> Edges { - 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 for Edges { - fn from(val: f32) -> Self { - let val: Pixels = val.into(); - val.into() - } -} - -impl From for Edges { - fn from(val: Pixels) -> Self { - Edges { - top: val, - right: val, - bottom: val, - left: val, - } - } -} - -/// Identifies a reference point on a 2D box, used to anchor positioned elements. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Anchor { - /// The top left corner - TopLeft, - /// The top right corner - TopRight, - /// The bottom left corner - BottomLeft, - /// The bottom right corner - BottomRight, - /// The top center position - TopCenter, - /// The bottom center position - BottomCenter, - /// The left center position - LeftCenter, - /// The right center position - RightCenter, -} - -impl Anchor { - /// Returns the directly opposite corner. - /// - /// # Examples - /// - /// ``` - /// # use gpui::Anchor; - /// assert_eq!(Anchor::TopLeft.opposite(), Anchor::BottomRight); - /// ``` - #[must_use] - pub fn opposite(self) -> Self { - match self { - Anchor::TopLeft => Anchor::BottomRight, - Anchor::TopRight => Anchor::BottomLeft, - Anchor::BottomLeft => Anchor::TopRight, - Anchor::BottomRight => Anchor::TopLeft, - Anchor::TopCenter => Anchor::BottomCenter, - Anchor::BottomCenter => Anchor::TopCenter, - Anchor::LeftCenter => Anchor::RightCenter, - Anchor::RightCenter => Anchor::LeftCenter, - } - } - - /// Returns the corner across from this corner, moving along the specified axis. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Axis, Anchor}; - /// let result = Anchor::TopLeft.other_side_along(Axis::Horizontal); - /// assert_eq!(result, Anchor::TopRight); - /// ``` - #[must_use] - pub fn other_side_along(self, axis: Axis) -> Self { - match axis { - Axis::Vertical => match self { - Anchor::TopLeft => Anchor::BottomLeft, - Anchor::TopRight => Anchor::BottomRight, - Anchor::BottomLeft => Anchor::TopLeft, - Anchor::BottomRight => Anchor::TopRight, - Anchor::TopCenter => Anchor::BottomCenter, - Anchor::BottomCenter => Anchor::TopCenter, - Anchor::LeftCenter => Anchor::LeftCenter, - Anchor::RightCenter => Anchor::RightCenter, - }, - Axis::Horizontal => match self { - Anchor::TopLeft => Anchor::TopRight, - Anchor::TopRight => Anchor::TopLeft, - Anchor::BottomLeft => Anchor::BottomRight, - Anchor::BottomRight => Anchor::BottomLeft, - Anchor::TopCenter => Anchor::TopCenter, - Anchor::BottomCenter => Anchor::BottomCenter, - Anchor::LeftCenter => Anchor::RightCenter, - Anchor::RightCenter => Anchor::LeftCenter, - }, - } - } - - /// Returns true if at the center. - #[inline] - pub fn is_center(&self) -> bool { - matches!( - self, - Self::TopCenter | Self::BottomCenter | Self::LeftCenter | Self::RightCenter - ) - } -} - -/// 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, PartialEq, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub struct Corners { - /// The value associated with the top left corner. - pub top_left: T, - /// The value associated with the top right corner. - pub top_right: T, - /// The value associated with the bottom right corner. - pub bottom_right: T, - /// The value associated with the bottom left corner. - pub bottom_left: T, -} - -impl Corners -where - T: Add + Half + Clone + Debug + Default + PartialEq, -{ - /// 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 gpui::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, - } - } - - /// Returns the requested corner value, supporting all eight corner positions. - /// - /// For the four basic corners (TopLeft, TopRight, BottomLeft, BottomRight), - /// this returns the corresponding field value directly. - /// - /// For the center positions (TopCenter, BottomCenter, LeftCenter, RightCenter), - /// this calculates the average of the two adjacent corners. - /// - /// # Returns - /// - /// A value of type `T` representing the corner requested by the parameter. - /// - /// # Examples - /// - /// Basic corner positions: - /// - /// ``` - /// # use gpui::{Anchor, Corners}; - /// let corners = Corners { - /// top_left: 10, - /// top_right: 20, - /// bottom_left: 30, - /// bottom_right: 40 - /// }; - /// assert_eq!(corners.corner(Anchor::TopLeft), 10); - /// assert_eq!(corners.corner(Anchor::BottomRight), 40); - /// ``` - /// - /// Center positions (calculated as average of adjacent corners): - /// - /// ``` - /// # use gpui::{Anchor, Corners}; - /// let corners = Corners { - /// top_left: 10, - /// top_right: 20, - /// bottom_left: 30, - /// bottom_right: 40 - /// }; - /// assert_eq!(corners.corner(Anchor::TopCenter), 15); - /// assert_eq!(corners.corner(Anchor::BottomCenter), 35); - /// assert_eq!(corners.corner(Anchor::LeftCenter), 20); - /// assert_eq!(corners.corner(Anchor::RightCenter), 30); - /// ``` - #[must_use] - pub fn corner(&self, corner: Anchor) -> T { - match corner { - Anchor::TopLeft => self.top_left.clone(), - Anchor::TopRight => self.top_right.clone(), - Anchor::BottomLeft => self.bottom_left.clone(), - Anchor::BottomRight => self.bottom_right.clone(), - Anchor::TopCenter => (self.top_left.clone() + self.top_right.clone()).half(), - Anchor::BottomCenter => (self.bottom_left.clone() + self.bottom_right.clone()).half(), - Anchor::LeftCenter => (self.top_left.clone() + self.bottom_left.clone()).half(), - Anchor::RightCenter => (self.top_right.clone() + self.bottom_right.clone()).half(), - } - } -} - -impl Corners { - /// Converts the `AbsoluteLength` to `Pixels` based on the provided rem size. - /// - /// # Arguments - /// - /// * `rem_size` - The size of one REM unit in pixels, used for conversion if the `AbsoluteLength` is in REMs. - /// - /// # Returns - /// - /// Returns a `Corners` instance with each corner's length converted to pixels. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Corners, AbsoluteLength, Pixels, Rems, Size}; - /// let corners = Corners { - /// top_left: AbsoluteLength::Pixels(Pixels::from(15.0)), - /// top_right: AbsoluteLength::Rems(Rems(1.0)), - /// bottom_right: AbsoluteLength::Pixels(Pixels::from(30.0)), - /// bottom_left: AbsoluteLength::Rems(Rems(2.0)), - /// }; - /// let rem_size = Pixels::from(16.0); - /// let corners_in_pixels = corners.to_pixels(rem_size); - /// - /// assert_eq!(corners_in_pixels.top_left, Pixels::from(15.0)); - /// assert_eq!(corners_in_pixels.top_right, Pixels::from(16.0)); // 1 rem converted to pixels - /// assert_eq!(corners_in_pixels.bottom_right, Pixels::from(30.0)); - /// assert_eq!(corners_in_pixels.bottom_left, Pixels::from(32.0)); // 2 rems converted to pixels - /// ``` - pub fn to_pixels(self, rem_size: Pixels) -> Corners { - Corners { - top_left: self.top_left.to_pixels(rem_size), - top_right: self.top_right.to_pixels(rem_size), - bottom_right: self.bottom_right.to_pixels(rem_size), - bottom_left: self.bottom_left.to_pixels(rem_size), - } - } -} - -impl Corners { - /// Scales the `Corners` by a given factor, returning `Corners`. - /// - /// 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` where each corner is the result of scaling the original corner by the given factor. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Corners, Pixels, ScaledPixels}; - /// let corners = Corners { - /// top_left: Pixels::from(10.0), - /// top_right: Pixels::from(20.0), - /// bottom_right: Pixels::from(30.0), - /// bottom_left: Pixels::from(40.0), - /// }; - /// let scaled_corners = corners.scale(2.0); - /// assert_eq!(scaled_corners.top_left, ScaledPixels::from(20.0)); - /// assert_eq!(scaled_corners.top_right, ScaledPixels::from(40.0)); - /// assert_eq!(scaled_corners.bottom_right, ScaledPixels::from(60.0)); - /// assert_eq!(scaled_corners.bottom_left, ScaledPixels::from(80.0)); - /// ``` - #[must_use] - pub fn scale(&self, factor: f32) -> Corners { - 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. - #[must_use] - pub fn max(&self) -> Pixels { - self.top_left - .max(self.top_right) - .max(self.bottom_right) - .max(self.bottom_left) - } -} - -impl + Ord + Clone + Debug + Default + PartialEq> Corners { - /// Clamps corner radii to be less than or equal to half the shortest side of a quad. - /// - /// # Arguments - /// - /// * `size` - The size of the quad which limits the size of the corner radii. - /// - /// # Returns - /// - /// Anchor radii values clamped to fit. - #[must_use] - pub fn clamp_radii_for_quad_size(self, size: Size) -> Corners { - let max = cmp::min(size.width, size.height) / 2.; - Corners { - top_left: cmp::min(self.top_left, max.clone()), - top_right: cmp::min(self.top_right, max.clone()), - bottom_right: cmp::min(self.bottom_right, max.clone()), - bottom_left: cmp::min(self.bottom_left, max), - } - } -} - -impl Corners { - /// Applies a function to each field of the `Corners`, producing a new `Corners`. - /// - /// This method allows for converting a `Corners` to a `Corners` 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` with each field mapped by the provided function. - /// - /// # Examples - /// - /// ``` - /// # use gpui::{Corners, Pixels, Rems}; - /// let corners = Corners { - /// top_left: Pixels::from(10.0), - /// top_right: Pixels::from(20.0), - /// bottom_right: Pixels::from(30.0), - /// bottom_left: Pixels::from(40.0), - /// }; - /// let corners_in_rems = corners.map(|&px| Rems(f32::from(px) / 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), - /// }); - /// ``` - #[must_use] - pub fn map(&self, f: impl Fn(&T) -> U) -> Corners - where - U: Clone + Debug + Default + PartialEq, - { - 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 Mul for Corners -where - T: Mul + Clone + Debug + Default + PartialEq, -{ - 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 * rhs.bottom_left, - } - } -} - -impl MulAssign for Corners -where - T: Mul + Clone + Debug + Default + PartialEq, - 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 Copy for Corners where T: Copy + Clone + Debug + Default + PartialEq {} - -impl From for Corners { - fn from(val: f32) -> Self { - Corners { - top_left: val.into(), - top_right: val.into(), - bottom_right: val.into(), - bottom_left: val.into(), - } - } -} - -impl From for Corners { - fn from(val: Pixels) -> Self { - Corners { - top_left: val, - top_right: val, - bottom_right: val, - bottom_left: val, - } - } -} - -/// Represents an angle in Radians -#[derive( - Clone, - Copy, - Default, - Add, - AddAssign, - Sub, - SubAssign, - Neg, - Div, - DivAssign, - PartialEq, - Serialize, - Deserialize, - Debug, -)] -#[repr(transparent)] -pub struct Radians(pub f32); - -/// Create a `Radian` from a raw value -pub fn radians(value: f32) -> Radians { - Radians(value) -} - -/// A type representing a percentage value. -#[derive( - Clone, - Copy, - Default, - Add, - AddAssign, - Sub, - SubAssign, - Neg, - Div, - DivAssign, - PartialEq, - Serialize, - Deserialize, - Debug, -)] -#[repr(transparent)] -pub struct Percentage(pub f32); - -/// Generate a `Radian` from a percentage of a full circle. -pub fn percentage(value: f32) -> Percentage { - debug_assert!( - (0.0..=1.0).contains(&value), - "Percentage must be between 0 and 1" - ); - Percentage(value) -} - -impl From for Radians { - fn from(value: Percentage) -> Self { - radians(value.0 * std::f32::consts::PI * 2.0) - } -} - -/// 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 gpui::{Pixels, ScaledPixels}; -/// -/// // Define a length of 10 pixels -/// let length = Pixels::from(10.0); -/// -/// // Define a length and scale it by a factor of 2 -/// let scaled_length = length.scale(2.0); -/// assert_eq!(scaled_length, ScaledPixels::from(20.0)); -/// ``` -#[derive( - Clone, - Copy, - Default, - Add, - AddAssign, - Sub, - SubAssign, - Neg, - Div, - DivAssign, - PartialEq, - Serialize, - Deserialize, - JsonSchema, -)] -#[repr(transparent)] -pub struct Pixels(pub(crate) f32); - -impl 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 for Pixels { - type Output = Self; - - fn mul(self, rhs: f32) -> Self { - Self(self.0 * rhs) - } -} - -impl Mul for f32 { - type Output = Pixels; - - fn mul(self, rhs: Pixels) -> Self::Output { - rhs * self - } -} - -impl Mul for Pixels { - type Output = Self; - - fn mul(self, rhs: usize) -> Self { - self * (rhs as f32) - } -} - -impl Mul for usize { - type Output = Pixels; - - fn mul(self, rhs: Pixels) -> Pixels { - rhs * self - } -} - -impl MulAssign for Pixels { - fn mul_assign(&mut self, rhs: f32) { - self.0 *= rhs; - } -} - -impl Display for Pixels { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}px", self.0) - } -} - -impl Debug for Pixels { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(self, f) - } -} - -impl std::iter::Sum for Pixels { - fn sum>(iter: I) -> Self { - iter.fold(Self::ZERO, |a, b| a + b) - } -} - -impl<'a> std::iter::Sum<&'a Pixels> for Pixels { - fn sum>(iter: I) -> Self { - iter.fold(Self::ZERO, |a, b| a + *b) - } -} - -impl TryFrom<&'_ str> for Pixels { - type Error = anyhow::Error; - - fn try_from(value: &'_ str) -> Result { - value - .strip_suffix("px") - .context("expected 'px' suffix") - .and_then(|number| Ok(number.parse()?)) - .map(Self) - } -} - -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); - /// The minimum value that can be represented by `Pixels`. - pub const MIN: Pixels = Pixels(f32::MIN); - - /// Returns the raw `f32` value of this `Pixels`. - pub fn as_f32(self) -> f32 { - self.0 - } - - /// 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. - #[must_use] - 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()) - } - - /// Returns the sign of the `Pixels` value. - /// - /// # Returns - /// - /// Returns: - /// * `1.0` if the value is positive - /// * `-1.0` if the value is negative - pub fn signum(&self) -> f32 { - self.0.signum() - } - - /// Returns the f64 value of `Pixels`. - /// - /// # Returns - /// - /// A f64 value of the `Pixels`. - pub fn to_f64(self) -> f64 { - self.0 as f64 - } -} - -impl Eq for Pixels {} - -impl PartialOrd for Pixels { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Pixels { - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.0.total_cmp(&other.0) - } -} - -impl std::hash::Hash for Pixels { - fn hash(&self, state: &mut H) { - self.0.to_bits().hash(state); - } -} - -impl From for Pixels { - fn from(pixels: f64) -> Self { - Pixels(pixels as f32) - } -} - -impl From for Pixels { - fn from(pixels: f32) -> Self { - Pixels(pixels) - } -} - -impl From for f32 { - fn from(pixels: Pixels) -> Self { - pixels.0 - } -} - -impl From<&Pixels> for f32 { - fn from(pixels: &Pixels) -> Self { - pixels.0 - } -} - -impl From for f64 { - fn from(pixels: Pixels) -> Self { - pixels.0 as f64 - } -} - -impl From for u32 { - fn from(pixels: Pixels) -> Self { - pixels.0 as u32 - } -} - -impl From<&Pixels> for u32 { - fn from(pixels: &Pixels) -> Self { - pixels.0 as u32 - } -} - -impl From for Pixels { - fn from(pixels: u32) -> Self { - Pixels(pixels as f32) - } -} - -impl From for usize { - fn from(pixels: Pixels) -> Self { - pixels.0 as usize - } -} - -impl From 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, - Serialize, - Deserialize, -)] -#[repr(transparent)] -pub struct DevicePixels(pub 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 gpui::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 for i32 { - fn from(device_pixels: DevicePixels) -> Self { - device_pixels.0 - } -} - -impl From for DevicePixels { - fn from(device_pixels: i32) -> Self { - DevicePixels(device_pixels) - } -} - -impl From for DevicePixels { - fn from(device_pixels: u32) -> Self { - DevicePixels(device_pixels as i32) - } -} - -impl From for u32 { - fn from(device_pixels: DevicePixels) -> Self { - device_pixels.0 as u32 - } -} - -impl From for u64 { - fn from(device_pixels: DevicePixels) -> Self { - device_pixels.0 as u64 - } -} - -impl From for DevicePixels { - fn from(device_pixels: u64) -> Self { - DevicePixels(device_pixels as i32) - } -} - -impl From for usize { - fn from(device_pixels: DevicePixels) -> Self { - device_pixels.0 as usize - } -} - -impl From 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, DivAssign, PartialEq)] -#[repr(transparent)] -pub struct ScaledPixels(pub f32); - -impl ScaledPixels { - /// Returns the raw `f32` value of this `ScaledPixels`. - pub fn as_f32(self) -> f32 { - self.0 - } - - /// 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 round(&self) -> Self { - Self(self.0.round()) - } - - /// Ceils the `ScaledPixels` value to the nearest whole number. - /// - /// # Returns - /// - /// Returns a new `ScaledPixels` instance with the ceiled value. - pub fn ceil(&self) -> Self { - Self(self.0.ceil()) - } -} - -impl Eq for ScaledPixels {} - -impl PartialOrd for ScaledPixels { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for ScaledPixels { - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.0.total_cmp(&other.0) - } -} - -impl Debug for ScaledPixels { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}px (scaled)", self.0) - } -} - -impl From for DevicePixels { - fn from(scaled: ScaledPixels) -> Self { - DevicePixels(scaled.0.ceil() as i32) - } -} - -impl From for ScaledPixels { - fn from(device: DevicePixels) -> Self { - ScaledPixels(device.0 as f32) - } -} - -impl From for f64 { - fn from(scaled_pixels: ScaledPixels) -> Self { - scaled_pixels.0 as f64 - } -} - -impl From for u32 { - fn from(pixels: ScaledPixels) -> Self { - pixels.0 as u32 - } -} - -impl From for ScaledPixels { - fn from(pixels: f32) -> Self { - Self(pixels) - } -} - -impl Div for ScaledPixels { - type Output = f32; - - fn div(self, rhs: Self) -> Self::Output { - self.0 / rhs.0 - } -} - -impl std::ops::DivAssign for ScaledPixels { - fn div_assign(&mut self, rhs: Self) { - *self = Self(self.0 / rhs.0); - } -} - -impl std::ops::RemAssign for ScaledPixels { - fn rem_assign(&mut self, rhs: Self) { - self.0 %= rhs.0; - } -} - -impl std::ops::Rem for ScaledPixels { - type Output = Self; - - fn rem(self, rhs: Self) -> Self { - Self(self.0 % rhs.0) - } -} - -impl Mul for ScaledPixels { - type Output = Self; - - fn mul(self, rhs: f32) -> Self { - Self(self.0 * rhs) - } -} - -impl Mul for f32 { - type Output = ScaledPixels; - - fn mul(self, rhs: ScaledPixels) -> Self::Output { - rhs * self - } -} - -impl Mul for ScaledPixels { - type Output = Self; - - fn mul(self, rhs: usize) -> Self { - self * (rhs as f32) - } -} - -impl Mul for usize { - type Output = ScaledPixels; - - fn mul(self, rhs: ScaledPixels) -> ScaledPixels { - rhs * self - } -} - -impl MulAssign for ScaledPixels { - fn mul_assign(&mut self, rhs: f32) { - self.0 *= rhs; - } -} - -/// Represents a length in rems, a unit based on the font-size of the window, which can be assigned with [`Window::set_rem_size`][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 `` 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`. -/// -/// [set_rem_size]: crate::Window::set_rem_size -#[derive(Clone, Copy, Default, Add, Sub, Mul, Div, Neg, PartialEq)] -pub struct Rems(pub f32); - -impl Rems { - /// A length of zero. - pub const ZERO: Self = Self(0.0); - /// Convert this Rem value to pixels. - pub fn to_pixels(self, rem_size: Pixels) -> Pixels { - self * rem_size - } - /// Convert from pixels to Rem - pub fn from_pixels(length: Pixels, window: &gpui::Window) -> Self { - Self(length / window.rem_size()) - } -} - -impl Mul for Rems { - type Output = Pixels; - - fn mul(self, other: Pixels) -> Pixels { - Pixels(self.0 * other.0) - } -} - -impl AddAssign for Rems { - fn add_assign(&mut self, rhs: Rems) { - self.0 += rhs.0 - } -} - -impl Display for Rems { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}rem", self.0) - } -} - -impl Debug for Rems { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(self, f) - } -} - -impl TryFrom<&'_ str> for Rems { - type Error = anyhow::Error; - - fn try_from(value: &'_ str) -> Result { - value - .strip_suffix("rem") - .context("expected 'rem' suffix") - .and_then(|number| Ok(number.parse()?)) - .map(Self) - } -} - -/// 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, Neg, PartialEq)] -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 for AbsoluteLength { - fn from(pixels: Pixels) -> Self { - AbsoluteLength::Pixels(pixels) - } -} - -impl From 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 gpui::{AbsoluteLength, Pixels, Rems}; - /// let length_in_pixels = AbsoluteLength::Pixels(Pixels::from(42.0)); - /// let length_in_rems = AbsoluteLength::Rems(Rems(2.0)); - /// let rem_size = Pixels::from(16.0); - /// - /// assert_eq!(length_in_pixels.to_pixels(rem_size), Pixels::from(42.0)); - /// assert_eq!(length_in_rems.to_pixels(rem_size), Pixels::from(32.0)); - /// ``` - pub fn to_pixels(self, rem_size: Pixels) -> Pixels { - match self { - AbsoluteLength::Pixels(pixels) => pixels, - AbsoluteLength::Rems(rems) => rems.to_pixels(rem_size), - } - } - - /// Converts an `AbsoluteLength` to `Rems` based on a given `rem_size`. - /// - /// # Arguments - /// - /// * `rem_size` - The size of one rem in pixels. - /// - /// # Returns - /// - /// Returns the `AbsoluteLength` as `Pixels`. - pub fn to_rems(self, rem_size: Pixels) -> Rems { - match self { - AbsoluteLength::Pixels(pixels) => Rems(pixels.0 / rem_size.0), - AbsoluteLength::Rems(rems) => rems, - } - } -} - -impl Default for AbsoluteLength { - fn default() -> Self { - px(0.).into() - } -} - -impl Display for AbsoluteLength { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Pixels(pixels) => write!(f, "{pixels}"), - Self::Rems(rems) => write!(f, "{rems}"), - } - } -} - -impl Debug for AbsoluteLength { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(self, f) - } -} - -const EXPECTED_ABSOLUTE_LENGTH: &str = "number with 'px' or 'rem' suffix"; - -impl TryFrom<&'_ str> for AbsoluteLength { - type Error = anyhow::Error; - - fn try_from(value: &'_ str) -> Result { - if let Ok(pixels) = value.try_into() { - Ok(Self::Pixels(pixels)) - } else if let Ok(rems) = value.try_into() { - Ok(Self::Rems(rems)) - } else { - Err(anyhow!( - "invalid AbsoluteLength '{value}', expected {EXPECTED_ABSOLUTE_LENGTH}" - )) - } - } -} - -impl JsonSchema for AbsoluteLength { - fn schema_name() -> Cow<'static, str> { - "AbsoluteLength".into() - } - - fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema { - json_schema!({ - "type": "string", - "pattern": r"^-?\d+(\.\d+)?(px|rem)$" - }) - } -} - -impl<'de> Deserialize<'de> for AbsoluteLength { - fn deserialize>(deserializer: D) -> Result { - struct StringVisitor; - - impl de::Visitor<'_> for StringVisitor { - type Value = AbsoluteLength; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{EXPECTED_ABSOLUTE_LENGTH}") - } - - fn visit_str(self, value: &str) -> Result { - AbsoluteLength::try_from(value).map_err(E::custom) - } - } - - deserializer.deserialize_str(StringVisitor) - } -} - -impl Serialize for AbsoluteLength { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{self}")) - } -} - -/// 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, PartialEq)] -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 gpui::{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::from(42.0)); - /// assert_eq!(length_in_rems.to_pixels(base_size, rem_size), Pixels::from(32.0)); - /// assert_eq!(length_as_fraction.to_pixels(base_size, rem_size), Pixels::from(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 { - Display::fmt(self, f) - } -} - -impl Display for DefiniteLength { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - DefiniteLength::Absolute(length) => write!(f, "{length}"), - DefiniteLength::Fraction(fraction) => write!(f, "{}%", (fraction * 100.0) as i32), - } - } -} - -const EXPECTED_DEFINITE_LENGTH: &str = "expected number with 'px', 'rem', or '%' suffix"; - -impl TryFrom<&'_ str> for DefiniteLength { - type Error = anyhow::Error; - - fn try_from(value: &'_ str) -> Result { - if let Some(percentage) = value.strip_suffix('%') { - let fraction: f32 = percentage.parse::().with_context(|| { - format!("invalid DefiniteLength '{value}', expected {EXPECTED_DEFINITE_LENGTH}") - })?; - Ok(DefiniteLength::Fraction(fraction / 100.0)) - } else if let Ok(absolute_length) = value.try_into() { - Ok(DefiniteLength::Absolute(absolute_length)) - } else { - Err(anyhow!( - "invalid DefiniteLength '{value}', expected {EXPECTED_DEFINITE_LENGTH}" - )) - } - } -} - -impl JsonSchema for DefiniteLength { - fn schema_name() -> Cow<'static, str> { - "DefiniteLength".into() - } - - fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema { - json_schema!({ - "type": "string", - "pattern": r"^-?\d+(\.\d+)?(px|rem|%)$" - }) - } -} - -impl<'de> Deserialize<'de> for DefiniteLength { - fn deserialize>(deserializer: D) -> Result { - struct StringVisitor; - - impl de::Visitor<'_> for StringVisitor { - type Value = DefiniteLength; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{EXPECTED_DEFINITE_LENGTH}") - } - - fn visit_str(self, value: &str) -> Result { - DefiniteLength::try_from(value).map_err(E::custom) - } - } - - deserializer.deserialize_str(StringVisitor) - } -} - -impl Serialize for DefiniteLength { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{self}")) - } -} - -impl From for DefiniteLength { - fn from(pixels: Pixels) -> Self { - Self::Absolute(pixels.into()) - } -} - -impl From for DefiniteLength { - fn from(rems: Rems) -> Self { - Self::Absolute(rems.into()) - } -} - -impl From 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, PartialEq)] -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 { - Display::fmt(self, f) - } -} - -impl Display 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"), - } - } -} - -const EXPECTED_LENGTH: &str = "expected 'auto' or number with 'px', 'rem', or '%' suffix"; - -impl TryFrom<&'_ str> for Length { - type Error = anyhow::Error; - - fn try_from(value: &'_ str) -> Result { - if value == "auto" { - Ok(Length::Auto) - } else if let Ok(definite_length) = value.try_into() { - Ok(Length::Definite(definite_length)) - } else { - Err(anyhow!( - "invalid Length '{value}', expected {EXPECTED_LENGTH}" - )) - } - } -} - -impl JsonSchema for Length { - fn schema_name() -> Cow<'static, str> { - "Length".into() - } - - fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema { - json_schema!({ - "type": "string", - "pattern": r"^(auto|-?\d+(\.\d+)?(px|rem|%))$" - }) - } -} - -impl<'de> Deserialize<'de> for Length { - fn deserialize>(deserializer: D) -> Result { - struct StringVisitor; - - impl de::Visitor<'_> for StringVisitor { - type Value = Length; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{EXPECTED_LENGTH}") - } - - fn visit_str(self, value: &str) -> Result { - Length::try_from(value).map_err(E::custom) - } - } - - deserializer.deserialize_str(StringVisitor) - } -} - -impl Serialize for Length { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{self}")) - } -} - -/// 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 const fn relative(fraction: f32) -> DefiniteLength { - DefiniteLength::Fraction(fraction) -} - -/// Returns the Golden Ratio, i.e. `~(1.0 + sqrt(5.0)) / 2.0`. -pub const 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 const 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 const fn auto() -> Length { - Length::Auto -} - -impl From for Length { - fn from(pixels: Pixels) -> Self { - Self::Definite(pixels.into()) - } -} - -impl From for Length { - fn from(rems: Rems) -> Self { - Self::Definite(rems.into()) - } -} - -impl From for Length { - fn from(length: DefiniteLength) -> Self { - Self::Definite(length) - } -} - -impl From 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()) - } -} - -/// A location in a grid layout. -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, JsonSchema, Default)] -pub struct GridLocation { - /// The rows this item uses within the grid. - pub row: Range, - /// The columns this item uses within the grid. - pub column: Range, -} - -/// The placement of an item within a grid layout's column or row. -#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize, JsonSchema, Default)] -pub enum GridPlacement { - /// The grid line index to place this item. - Line(i16), - /// The number of grid lines to span. - Span(u16), - /// Automatically determine the placement, equivalent to Span(1) - #[default] - Auto, -} - -impl From for taffy::GridPlacement { - fn from(placement: GridPlacement) -> Self { - match placement { - GridPlacement::Line(index) => taffy::GridPlacement::from_line_index(index), - GridPlacement::Span(span) => taffy::GridPlacement::from_span(span), - GridPlacement::Auto => taffy::GridPlacement::Auto, - } - } -} - -/// 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 i32 { - fn half(&self) -> Self { - self / 2 - } -} - -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.) - } -} - -/// 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 IsZero for Point { - fn is_zero(&self) -> bool { - self.x.is_zero() && self.y.is_zero() - } -} - -impl IsZero for Size -where - T: IsZero + Clone + Debug + Default + PartialEq, -{ - fn is_zero(&self) -> bool { - self.width.is_zero() || self.height.is_zero() - } -} - -impl IsZero for Bounds { - fn is_zero(&self) -> bool { - self.size.is_zero() - } -} - -impl IsZero for Corners -where - T: IsZero + Clone + Debug + Default + PartialEq, -{ - 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!(bounds1.intersects(&bounds2)); - - // Test Case 2: Non-Intersecting bounds - assert!(!bounds1.intersects(&bounds3)); - - // Test Case 3: Bounds intersecting with themselves - assert!(bounds1.intersects(&bounds1)); - } -} diff --git a/crates/gpui_pre_apple/vendor/gpui/src/platform.rs b/crates/gpui_pre_apple/vendor/gpui/src/platform.rs deleted file mode 100644 index eeba999d..00000000 --- a/crates/gpui_pre_apple/vendor/gpui/src/platform.rs +++ /dev/null @@ -1,3113 +0,0 @@ -mod app_menu; -mod keyboard; -mod keystroke; - -#[cfg(all(target_os = "linux", feature = "wayland"))] -#[expect(missing_docs)] -pub mod layer_shell; - -/// Types for configuring parent-anchored popup windows such as menus, dropdowns and tooltips. -pub mod popup; - -#[cfg(any(test, feature = "test-support", feature = "bench-support"))] -mod threaded_dispatcher; - -#[cfg(any(test, feature = "test-support", feature = "bench-support"))] -mod test; - -#[cfg(all(target_os = "macos", any(test, feature = "test-support")))] -mod visual_test; - -#[cfg(all( - feature = "screen-capture", - any(target_os = "windows", target_os = "linux", target_os = "freebsd",) -))] -pub mod scap_screen_capture; - -#[cfg(all( - any(target_os = "windows", target_os = "linux"), - feature = "screen-capture" -))] -pub(crate) type PlatformScreenCaptureFrame = scap::frame::Frame; -#[cfg(not(feature = "screen-capture"))] -pub(crate) type PlatformScreenCaptureFrame = (); -#[cfg(all(target_os = "macos", feature = "screen-capture"))] -pub(crate) type PlatformScreenCaptureFrame = core_video::image_buffer::CVImageBuffer; - -use crate::{ - Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds, - DEFAULT_WINDOW_SIZE, DevicePixels, DispatchEventResult, Edges, ExternalDragPayload, Font, - FontId, FontMetrics, FontRun, ForegroundExecutor, GlyphId, GpuSpecs, Hsla, ImageSource, Keymap, - LineLayout, Pixels, PlatformGestures, PlatformInput, Point, Priority, RenderGlyphParams, - RenderImage, RenderImageParams, RenderSvgParams, Scene, ShapedGlyph, ShapedRun, SharedString, - Size, SvgRenderer, SystemWindowTab, Task, Window, WindowControlArea, hash, point, px, size, -}; -#[cfg(any(target_os = "linux", target_os = "freebsd"))] -use anyhow::bail; -use anyhow::{Context as _, Result}; -use async_task::Runnable; -use futures::channel::oneshot; -#[cfg(any(test, feature = "test-support", feature = "bench-support"))] -use image::RgbaImage; -use image::codecs::gif::GifDecoder; -use image::{AnimationDecoder as _, DynamicImage, Frame}; -use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; -use scheduler::Instant; -pub use scheduler::RunnableMeta; -use schemars::JsonSchema; -use seahash::SeaHasher; -use serde::{Deserialize, Serialize}; -use smallvec::SmallVec; -use std::borrow::Cow; -use std::hash::{Hash, Hasher}; -use std::io::Cursor; -use std::ops; -use std::time::Duration; -use std::{ - ffi::OsString, - fmt::{self, Debug}, - ops::Range, - path::{Path, PathBuf}, - rc::Rc, - sync::Arc, -}; -use strum::EnumIter; -use uuid::Uuid; - -pub use app_menu::*; -pub use keyboard::*; -pub use keystroke::*; - -#[cfg(any(test, feature = "test-support", feature = "bench-support"))] -pub(crate) use test::*; - -#[cfg(any(test, feature = "test-support"))] -pub use test::{TestDispatcher, TestScreenCaptureSource, TestScreenCaptureStream}; - -#[cfg(any(test, feature = "test-support", feature = "bench-support"))] -pub use threaded_dispatcher::ThreadedDispatcher; - -#[cfg(all(target_os = "macos", any(test, feature = "test-support")))] -pub use visual_test::VisualTestPlatform; - -// TODO(jk): return an enum instead of a string -/// Return which compositor we're guessing we'll use. -/// Does not attempt to connect to the given compositor. -#[cfg(any(target_os = "linux", target_os = "freebsd"))] -#[inline] -pub fn guess_compositor() -> &'static str { - if std::env::var_os("ZED_HEADLESS").is_some() { - return "Headless"; - } - - #[cfg(feature = "wayland")] - let wayland_display = std::env::var_os("WAYLAND_DISPLAY"); - #[cfg(not(feature = "wayland"))] - let wayland_display: Option = None; - - #[cfg(feature = "x11")] - let x11_display = std::env::var_os("DISPLAY"); - #[cfg(not(feature = "x11"))] - let x11_display: Option = None; - - let use_wayland = wayland_display.is_some_and(|display| !display.is_empty()); - let use_x11 = x11_display.is_some_and(|display| !display.is_empty()); - - if use_wayland { - "Wayland" - } else if use_x11 { - "X11" - } else { - "Headless" - } -} - -#[expect(missing_docs)] -pub trait Platform: 'static { - fn background_executor(&self) -> BackgroundExecutor; - fn foreground_executor(&self) -> ForegroundExecutor; - fn text_system(&self) -> Arc; - - fn run(&self, on_finish_launching: Box); - fn quit(&self); - fn restart(&self, binary_path: Option, arguments: Vec); - fn activate(&self, ignoring_other_apps: bool); - fn hide(&self); - fn hide_other_apps(&self); - fn unhide_other_apps(&self); - - fn displays(&self) -> Vec>; - fn primary_display(&self) -> Option>; - fn active_window(&self) -> Option; - fn window_stack(&self) -> Option> { - None - } - - fn is_screen_capture_supported(&self) -> bool { - false - } - - fn screen_capture_sources( - &self, - ) -> oneshot::Receiver>>> { - let (sources_tx, sources_rx) = oneshot::channel(); - sources_tx - .send(Err(anyhow::anyhow!( - "gpui was compiled without the screen-capture feature" - ))) - .ok(); - sources_rx - } - - fn open_window( - &self, - handle: AnyWindowHandle, - options: WindowParams, - ) -> anyhow::Result>; - - /// Returns the appearance of the application's windows. - fn window_appearance(&self) -> WindowAppearance; - - /// Overrides the appearance (light/dark) applied to the app's windows, independent - /// of the OS-wide setting. Pass `None` to clear the override and follow the system - /// again. The override is reflected by [`Platform::window_appearance`]. - /// - /// Currently only implemented on macOS, where it sets `NSApplication.appearance` so - /// the native window chrome (the window border and titlebar) of every window matches - /// a dark app theme even when the system is in light mode (or vice versa). A no-op on - /// other platforms. - fn set_window_appearance(&self, _appearance: Option) {} - - /// Returns the window button layout configuration when supported. - fn button_layout(&self) -> Option { - None - } - - fn open_url(&self, url: &str); - fn on_open_urls(&self, callback: Box)>); - fn register_url_scheme(&self, url: &str) -> Task>; - - fn prompt_for_paths( - &self, - options: PathPromptOptions, - ) -> oneshot::Receiver>>>; - fn prompt_for_new_path( - &self, - directory: &Path, - suggested_name: Option<&str>, - ) -> oneshot::Receiver>>; - fn can_select_mixed_files_and_dirs(&self) -> bool; - fn reveal_path(&self, path: &Path); - fn open_with_system(&self, path: &Path); - - fn on_quit(&self, callback: Box bool>); - fn on_reopen(&self, callback: Box); - fn on_system_wake(&self, callback: Box); - - // Mobile platform methods. On mobile the OS owns the application - // lifecycle: apps are backgrounded, foregrounded, and killed at the - // system's discretion, and must react rather than decide. - - /// Registers a callback invoked whenever the application's lifecycle - /// phase changes. See [`AppLifecyclePhase`] for the phase vocabulary and - /// its mapping onto iOS and Android. - /// - /// Desktop platforms never invoke this. - fn on_app_lifecycle(&self, _callback: Box) {} - - /// Registers a callback invoked when the OS signals memory pressure - /// (iOS `didReceiveMemoryWarning`, Android `onTrimMemory`). - /// - /// Desktop platforms never invoke this. - fn on_memory_warning(&self, _callback: Box) {} - - /// The platform's gesture recognition services, if it provides any - /// beyond gpui's portable recognizers. See - /// [`PlatformGestures`](crate::PlatformGestures). - fn gestures(&self) -> Option> { - None - } - - fn set_menus(&self, menus: Vec

, keymap: &Keymap); - fn get_menus(&self) -> Option> { - None - } - - fn set_dock_menu(&self, menu: Vec, keymap: &Keymap); - fn perform_dock_menu_action(&self, _action: usize) {} - fn add_recent_document(&self, _path: &Path) {} - fn update_jump_list( - &self, - _menus: Vec, - _entries: Vec>, - ) -> Task>> { - Task::ready(Vec::new()) - } - fn on_app_menu_action(&self, callback: Box); - fn on_will_open_app_menu(&self, callback: Box); - fn on_validate_app_menu_command(&self, callback: Box bool>); - - fn thermal_state(&self) -> ThermalState; - fn on_thermal_state_change(&self, callback: Box); - - /// Sets the application's process-wide identity and user-visible name. - /// - /// The identifier is used for platform identity mechanisms such as the - /// Windows AppUserModelID. The name is used wherever the operating system - /// presents the application to the user. Call this once, early in startup, - /// before opening windows or posting notifications. - fn set_app_identity(&self, identifier: &str, name: &str) { - _ = (identifier, name); - } - - /// Posts a notification to the operating system's notification center. - /// - /// Posting a notification whose [`SystemNotification::tag`] matches an - /// earlier one replaces that notification where the platform supports it. - /// No-op on platforms without notification support, or when delivery is - /// unavailable (e.g. authorization was denied). - fn show_system_notification(&self, notification: SystemNotification) { - _ = notification; - } - - /// Removes the delivered or pending notification with this tag. - /// - /// Best-effort: some platforms cannot retract a notification once shown, - /// in which case it ages out of the notification center on its own. - fn dismiss_system_notification(&self, tag: &str) { - _ = tag; - } - - /// Registers the callback invoked when the user activates a system - /// notification, either by clicking its body or one of its action - /// buttons. - /// - /// Implementations must invoke the callback on the main thread. - fn on_system_notification_response( - &self, - callback: Box, - ) { - _ = callback; - } - - fn compositor_name(&self) -> &'static str { - "" - } - fn app_path(&self) -> Result; - fn path_for_auxiliary_executable(&self, name: &str) -> Result; - - fn set_cursor_style(&self, style: CursorStyle); - - /// Hides the mouse cursor until the user moves the mouse over one of - /// this application's windows. - fn hide_cursor_until_mouse_moves(&self); - - /// Returns whether the mouse cursor is currently visible. - fn is_cursor_visible(&self) -> bool; - - fn should_auto_hide_scrollbars(&self) -> bool; - - fn read_from_clipboard(&self) -> Option; - fn write_to_clipboard(&self, item: ClipboardItem); - - /// Reads the clipboard, resolving once its contents are available. - /// - /// Most platforms read synchronously and return a ready task. Platforms - /// whose clipboard access is inherently asynchronous and permission-gated - /// (e.g. the browser's async clipboard API) override this method; on those - /// platforms [`Platform::read_from_clipboard`] cannot return the clipboard - /// contents, so callers that can await should prefer this method. - fn read_from_clipboard_async(&self) -> Task, ClipboardReadError>> { - Task::ready(Ok(self.read_from_clipboard())) - } - - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - fn read_from_primary(&self) -> Option; - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - fn write_to_primary(&self, item: ClipboardItem); - - #[cfg(target_os = "macos")] - fn read_from_find_pasteboard(&self) -> Option; - #[cfg(target_os = "macos")] - fn write_to_find_pasteboard(&self, item: ClipboardItem); - - fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task>; - fn read_credentials(&self, url: &str) -> Task)>>>; - fn delete_credentials(&self, url: &str) -> Task>; - - fn keyboard_layout(&self) -> Box; - fn keyboard_mapper(&self) -> Rc; - fn on_keyboard_layout_change(&self, callback: Box); -} - -/// A handle to a platform's display, e.g. a monitor or laptop screen. -pub trait PlatformDisplay: Debug { - /// Get the ID for this display - fn id(&self) -> DisplayId; - - /// Returns a stable identifier for this display that can be persisted and used - /// across system restarts. - fn uuid(&self) -> Result; - - /// Get the bounds for this display - fn bounds(&self) -> Bounds; - - /// Get the visible bounds for this display, excluding taskbar/dock areas. - /// This is the usable area where windows can be placed without being obscured. - /// Defaults to the full display bounds if not overridden. - fn visible_bounds(&self) -> Bounds { - self.bounds() - } - - /// Get the default bounds for this display to place a window - fn default_bounds(&self) -> Bounds { - let bounds = self.bounds(); - let center = bounds.center(); - let clipped_window_size = DEFAULT_WINDOW_SIZE.min(&bounds.size); - - let offset = clipped_window_size / 2.0; - let origin = point(center.x - offset.width, center.y - offset.height); - Bounds::new(origin, clipped_window_size) - } -} - -/// A notification posted to the operating system's notification center, -/// rather than rendered as in-app UI. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct SystemNotification { - /// Stable identity for the notification. Posting a new notification with - /// the same tag replaces the previous one where the platform supports it, - /// and responses carry the tag back to the application. - pub tag: SharedString, - /// The notification's headline. - pub title: SharedString, - /// Additional text displayed below the title. - pub body: SharedString, - /// Buttons offered on the notification. Platforms that cannot display - /// action buttons show the notification without them. - pub actions: Vec, -} - -/// A button offered on a [`SystemNotification`]. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct SystemNotificationAction { - /// Identifies the action in [`SystemNotificationResponse::action_id`] - /// when the user presses this button. - pub id: SharedString, - /// The button's user-visible label. - pub label: SharedString, -} - -/// The user's activation of a [`SystemNotification`]. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct SystemNotificationResponse { - /// The [`SystemNotification::tag`] of the activated notification. - pub tag: SharedString, - /// The pressed action button's [`SystemNotificationAction::id`], or - /// `None` when the user activated the notification body itself. - pub action_id: Option, -} - -/// Thermal state of the system -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ThermalState { - /// System has no thermal constraints - Nominal, - /// System is slightly constrained, reduce discretionary work - Fair, - /// System is moderately constrained, reduce CPU/GPU intensive work - Serious, - /// System is critically constrained, minimize all resource usage - Critical, -} - -/// Metadata for a given [ScreenCaptureSource] -#[derive(Clone)] -pub struct SourceMetadata { - /// Opaque identifier of this screen. - pub id: u64, - /// Human-readable label for this source. - pub label: Option, - /// Whether this source is the main display. - pub is_main: Option, - /// Video resolution of this source. - pub resolution: Size, -} - -/// A source of on-screen video content that can be captured. -pub trait ScreenCaptureSource { - /// Returns metadata for this source. - fn metadata(&self) -> Result; - - /// Start capture video from this source, invoking the given callback - /// with each frame. - fn stream( - &self, - foreground_executor: &ForegroundExecutor, - frame_callback: Box, - ) -> oneshot::Receiver>>; -} - -/// A video stream captured from a screen. -pub trait ScreenCaptureStream { - /// Returns metadata for this source. - fn metadata(&self) -> Result; -} - -/// A frame of video captured from a screen. -pub struct ScreenCaptureFrame(pub PlatformScreenCaptureFrame); - -/// An opaque identifier for a hardware display -#[derive(PartialEq, Eq, Hash, Copy, Clone)] -pub struct DisplayId(pub(crate) u64); - -impl DisplayId { - /// Create a new `DisplayId` from a raw platform display identifier. - pub fn new(id: u64) -> Self { - Self(id) - } -} - -impl From for DisplayId { - fn from(id: u64) -> Self { - Self(id) - } -} - -impl From for u64 { - fn from(id: DisplayId) -> Self { - id.0 - } -} - -impl Debug for DisplayId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "DisplayId({})", self.0) - } -} - -/// Which part of the window to resize -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ResizeEdge { - /// The top edge - Top, - /// The top right corner - TopRight, - /// The right edge - Right, - /// The bottom right corner - BottomRight, - /// The bottom edge - Bottom, - /// The bottom left corner - BottomLeft, - /// The left edge - Left, - /// The top left corner - TopLeft, -} - -/// A type to describe the appearance of a window -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)] -pub enum WindowDecorations { - #[default] - /// Server side decorations - Server, - /// Client side decorations - Client, -} - -/// A type to describe how this window is currently configured -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)] -pub enum Decorations { - /// The window is configured to use server side decorations - #[default] - Server, - /// The window is configured to use client side decorations - Client { - /// The edge tiling state - tiling: Tiling, - }, -} - -/// What window controls this platform supports -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct WindowControls { - /// Whether this platform supports fullscreen - pub fullscreen: bool, - /// Whether this platform supports maximize - pub maximize: bool, - /// Whether this platform supports minimize - pub minimize: bool, - /// Whether this platform supports a window menu - pub window_menu: bool, -} - -impl Default for WindowControls { - fn default() -> Self { - // Assume that we can do anything, unless told otherwise - Self { - fullscreen: true, - maximize: true, - minimize: true, - window_menu: true, - } - } -} - -/// A window control button type used in [`WindowButtonLayout`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum WindowButton { - /// The minimize button - Minimize, - /// The maximize button - Maximize, - /// The close button - Close, -} - -impl WindowButton { - /// Returns a stable element ID for rendering this button. - pub fn id(&self) -> &'static str { - match self { - WindowButton::Minimize => "minimize", - WindowButton::Maximize => "maximize", - WindowButton::Close => "close", - } - } - - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - fn index(&self) -> usize { - match self { - WindowButton::Minimize => 0, - WindowButton::Maximize => 1, - WindowButton::Close => 2, - } - } -} - -/// Maximum number of [`WindowButton`]s per side in the titlebar. -pub const MAX_BUTTONS_PER_SIDE: usize = 3; - -/// Describes which [`WindowButton`]s appear on each side of the titlebar. -/// -/// On Linux, this is read from the desktop environment's configuration -/// (e.g. GNOME's `gtk-decoration-layout` gsetting) via [`WindowButtonLayout::parse`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct WindowButtonLayout { - /// Buttons on the left side of the titlebar. - pub left: [Option; MAX_BUTTONS_PER_SIDE], - /// Buttons on the right side of the titlebar. - pub right: [Option; MAX_BUTTONS_PER_SIDE], -} - -#[cfg(any(target_os = "linux", target_os = "freebsd"))] -impl WindowButtonLayout { - /// Returns Zed's built-in fallback button layout for Linux titlebars. - pub fn linux_default() -> Self { - Self { - left: [None; MAX_BUTTONS_PER_SIDE], - right: [ - Some(WindowButton::Minimize), - Some(WindowButton::Maximize), - Some(WindowButton::Close), - ], - } - } - - /// Parses a GNOME-style `button-layout` string (e.g. `"close,minimize:maximize"`). - pub fn parse(layout_string: &str) -> Result { - fn parse_side( - s: &str, - seen_buttons: &mut [bool; MAX_BUTTONS_PER_SIDE], - unrecognized: &mut Vec, - ) -> [Option; MAX_BUTTONS_PER_SIDE] { - let mut result = [None; MAX_BUTTONS_PER_SIDE]; - let mut i = 0; - for name in s.split(',') { - let trimmed = name.trim(); - if trimmed.is_empty() { - continue; - } - let button = match trimmed { - "minimize" => Some(WindowButton::Minimize), - "maximize" => Some(WindowButton::Maximize), - "close" => Some(WindowButton::Close), - other => { - unrecognized.push(other.to_string()); - None - } - }; - if let Some(button) = button { - if seen_buttons[button.index()] { - continue; - } - if let Some(slot) = result.get_mut(i) { - *slot = Some(button); - seen_buttons[button.index()] = true; - i += 1; - } - } - } - result - } - - let (left_str, right_str) = layout_string.split_once(':').unwrap_or(("", layout_string)); - let mut unrecognized = Vec::new(); - let mut seen_buttons = [false; MAX_BUTTONS_PER_SIDE]; - let layout = Self { - left: parse_side(left_str, &mut seen_buttons, &mut unrecognized), - right: parse_side(right_str, &mut seen_buttons, &mut unrecognized), - }; - - if !unrecognized.is_empty() - && layout.left.iter().all(Option::is_none) - && layout.right.iter().all(Option::is_none) - { - bail!( - "button layout string {:?} contains no valid buttons (unrecognized: {})", - layout_string, - unrecognized.join(", ") - ); - } - - Ok(layout) - } - - /// Formats the layout back into a GNOME-style `button-layout` string. - #[cfg(test)] - pub fn format(&self) -> String { - fn format_side(buttons: &[Option; MAX_BUTTONS_PER_SIDE]) -> String { - buttons - .iter() - .flatten() - .map(|button| match button { - WindowButton::Minimize => "minimize", - WindowButton::Maximize => "maximize", - WindowButton::Close => "close", - }) - .collect::>() - .join(",") - } - - format!("{}:{}", format_side(&self.left), format_side(&self.right)) - } -} - -/// A type to describe which sides of the window are currently tiled in some way -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)] -pub struct Tiling { - /// Whether the top edge is tiled - pub top: bool, - /// Whether the left edge is tiled - pub left: bool, - /// Whether the right edge is tiled - pub right: bool, - /// Whether the bottom edge is tiled - pub bottom: bool, -} - -impl Tiling { - /// Initializes a [`Tiling`] type with all sides tiled - pub fn tiled() -> Self { - Self { - top: true, - left: true, - right: true, - bottom: true, - } - } - - /// Whether any edge is tiled - pub fn is_tiled(&self) -> bool { - self.top || self.left || self.right || self.bottom - } -} - -/// Callbacks for the accessibility adapter. -pub struct A11yCallbacks { - /// Called when the adapter is activated (a screen reader connects). - pub activation: Box Option + Send + 'static>, - /// Called when an action is requested by the screen reader. - pub action: Box, - /// Called when the adapter is deactivated (screen reader disconnects). - pub deactivation: Box, -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] -#[expect(missing_docs)] -pub struct RequestFrameOptions { - /// Whether a presentation is required. - pub require_presentation: bool, - /// Force refresh of all rendering states when true. - pub force_render: bool, -} - -/// The application's lifecycle phase, as owned and reported by a mobile OS. -/// -/// `Inactive` means visible but not receiving input (a system dialog on -/// top), while `Background` means not visible at all, with process death -/// possible at any time thereafter. -/// -/// | Phase | iOS | Android | -/// |--------------|------------------------------|--------------| -/// | `Active` | `didBecomeActive` | `onResume` | -/// | `Inactive` | `willResignActive` | `onPause` | -/// | `Background` | `didEnterBackground` | `onStop` | -/// | `Foreground` | `willEnterForeground` | `onStart` | -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum AppLifecyclePhase { - /// Foreground and receiving input. - Active, - /// Foreground (visible) but not receiving input. - Inactive, - /// Not visible. The GPU surface may be destroyed while backgrounded and - /// the process may be killed without further notice. - Background, - /// Becoming visible again, before input is restored. - Foreground, -} - -/// Regions of a window that are obscured or reserved by the system. -/// -/// Mobile applications often share space in their window with system-specific -/// geometry, from keyboards to camera notches. In GPUI, all this is abstracted -/// into a single "inset" which should be overlaid on the window's bounds. -/// It is up to the application develop to determine how to handle these cases. -#[derive(Debug, Clone, Default, PartialEq)] -pub struct WindowInsets { - /// Regions covered by system UI or hardware: status bar, display - /// cutouts/notch, home indicator, navigation bars. - /// (iOS: `safeAreaInsets`. Android: `WindowInsets` of types - /// `systemBars() | displayCutout()`.) - pub safe_area: Edges, - /// The region covered by the keyboard, when present. - /// (iOS: derived from `keyboardWillShow`/frame-change notifications. - /// Android: `WindowInsets.Type.ime()`.) - pub ime: Edges, -} - -impl WindowInsets { - /// The combined inset content should avoid. - pub fn effective(&self) -> Edges { - Edges { - top: self.safe_area.top.max(self.ime.top), - right: self.safe_area.right.max(self.ime.right), - bottom: self.safe_area.bottom.max(self.ime.bottom), - left: self.safe_area.left.max(self.ime.left), - } - } -} - -/// A change in the state of the focused text input. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum TextInputStateChange { - /// An editable element gained focus. - FocusGained, - /// The focused editable element lost focus. - FocusLost, - /// The selection or caret moved - SelectionChanged, - /// The document content changed outside of platform-initiated edits. - ContentChanged, -} - -#[expect(missing_docs)] -pub trait PlatformWindow: HasWindowHandle + HasDisplayHandle { - fn bounds(&self) -> Bounds; - fn is_maximized(&self) -> bool; - fn window_bounds(&self) -> WindowBounds; - fn content_size(&self) -> Size; - fn resize(&mut self, size: Size); - fn scale_factor(&self) -> f32; - fn appearance(&self) -> WindowAppearance; - fn display(&self) -> Option>; - fn mouse_position(&self) -> Point; - fn modifiers(&self) -> Modifiers; - fn capslock(&self) -> Capslock; - fn set_input_handler(&mut self, input_handler: PlatformInputHandler); - fn take_input_handler(&mut self) -> Option; - /// Apply the focused text region's [`TextInputConfiguration`] to the - /// platform's text input session (e.g. attributes of the hidden editable - /// element on web). Called only when the configuration changes, because - /// reconfiguring a live input session can restart the IME connection. - fn set_text_input_configuration(&mut self, _configuration: TextInputConfiguration) {} - fn prompt( - &self, - level: PromptLevel, - msg: &str, - detail: Option<&str>, - answers: &[PromptButton], - ) -> Option>; - fn activate(&self); - /// Requests that the operating system draw attention to this window. - fn request_attention(&self) {} - fn is_active(&self) -> bool; - fn is_hovered(&self) -> bool; - fn background_appearance(&self) -> WindowBackgroundAppearance; - fn set_title(&mut self, title: &str); - fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance); - fn minimize(&self); - fn zoom(&self); - fn toggle_fullscreen(&self); - fn is_fullscreen(&self) -> bool; - fn frame_waker(&self) -> Option> { - None - } - fn on_request_frame(&self, callback: Box); - fn on_input(&self, callback: Box DispatchEventResult>); - fn on_active_status_change(&self, callback: Box); - fn on_hover_status_change(&self, callback: Box); - fn on_resize(&self, callback: Box, f32)>); - fn on_moved(&self, callback: Box); - fn on_should_close(&self, callback: Box bool>); - fn on_hit_test_window_control(&self, callback: Box Option>); - fn on_close(&self, callback: Box); - fn on_appearance_changed(&self, callback: Box); - fn on_button_layout_changed(&self, _callback: Box) {} - fn draw(&self, scene: &Scene); - fn schedule_frame(&self) {} - fn sprite_atlas(&self) -> Arc; - fn is_subpixel_rendering_supported(&self) -> bool; - - // macOS specific methods - fn get_title(&self) -> String { - String::new() - } - fn tabbed_windows(&self) -> Option> { - None - } - fn tab_bar_visible(&self) -> bool { - false - } - fn set_edited(&mut self, _edited: bool) {} - fn set_document_path(&self, _path: Option<&std::path::Path>) {} - fn toggle_simple_fullscreen(&self) {} - fn is_simple_fullscreen(&self) -> bool { - false - } - #[cfg(target_os = "macos")] - fn set_traffic_light_position(&self, _position: Point) {} - fn show_character_palette(&self) {} - fn titlebar_double_click(&self, _is_resizable: bool, _is_minimizable: bool) {} - fn on_move_tab_to_new_window(&self, _callback: Box) {} - fn on_merge_all_windows(&self, _callback: Box) {} - fn on_select_previous_tab(&self, _callback: Box) {} - fn on_select_next_tab(&self, _callback: Box) {} - fn on_toggle_tab_bar(&self, _callback: Box) {} - fn merge_all_windows(&self) {} - fn move_tab_to_new_window(&self) {} - fn toggle_window_tab_overview(&self) {} - fn set_tabbing_identifier(&self, _identifier: Option) {} - - #[cfg(target_os = "windows")] - fn get_raw_handle(&self) -> windows::Win32::Foundation::HWND; - - // Linux specific methods - fn inner_window_bounds(&self) -> WindowBounds { - self.window_bounds() - } - fn request_decorations(&self, _decorations: WindowDecorations) {} - fn show_window_menu(&self, _position: Point) {} - fn start_window_move(&self) {} - fn can_start_external_drag(&self) -> bool { - false - } - fn start_external_drag(&self, _payload: &ExternalDragPayload) -> bool { - false - } - fn start_window_resize(&self, _edge: ResizeEdge) {} - fn set_exclusive_zone(&self, _zone: Pixels) {} - #[cfg(all(target_os = "linux", feature = "wayland"))] - fn set_exclusive_edge(&self, _edge: layer_shell::Anchor) {} - fn set_input_region(&self, _region: Option<&[Bounds]>) {} - fn window_decorations(&self) -> Decorations { - Decorations::Server - } - fn set_app_id(&mut self, _app_id: &str) {} - fn map_window(&mut self) -> anyhow::Result<()> { - Ok(()) - } - fn window_controls(&self) -> WindowControls { - WindowControls::default() - } - fn set_client_inset(&self, _inset: Pixels) {} - fn gpu_specs(&self) -> Option; - - fn update_ime_position(&self, _bounds: Bounds); - - // Mobile platform methods. - - /// The regions of this window currently obscured or reserved by the - /// system. Zero on platforms without such regions. - fn insets(&self) -> WindowInsets { - WindowInsets::default() - } - - /// Registers a callback invoked whenever [`Self::insets`] change. - /// - /// Contract: fires continuously during animated transitions (Android - /// `WindowInsetsAnimation` progress; on iOS the platform interpolates - /// the keyboard animation curve on frame ticks) and is exact at rest. - fn on_insets_changed(&self, _callback: Box) {} - - /// Sets the handler for the system back action (Android back - /// button/gesture; no source on iOS or desktop). - fn set_back_handler(&self, _callback: Box) {} - - /// Declares whether the application would currently handle the system - /// back action (e.g. navigation depth > 0). - fn set_back_enabled(&self, _enabled: bool) {} - - /// Requests that the soft keyboard be shown. - fn show_soft_keyboard(&self) {} - - /// Requests that the soft keyboard be hidden. - fn hide_soft_keyboard(&self) {} - - /// Inform the operating system that the text input state has changed - fn text_input_state_changed(&self, _change: TextInputStateChange) {} - - fn play_system_bell(&self) {} - - /// Initialize the accessibility adapter with callbacks. - fn a11y_init(&self, _callbacks: A11yCallbacks) {} - - /// Provide a TreeUpdate to the accessibility adapter. - fn a11y_tree_update(&self, _tree_update: accesskit::TreeUpdate) {} - - /// Inform the adapter of updated window bounds. - fn a11y_update_window_bounds(&self) {} - - #[cfg(any(test, feature = "test-support", feature = "bench-support"))] - fn as_test(&mut self) -> Option<&mut TestWindow> { - None - } - - /// Renders the given scene to a texture and returns the pixel data as an RGBA image. - /// This does not present the frame to screen - useful for visual testing where we want - /// to capture what would be rendered without displaying it or requiring the window to be visible. - #[cfg(any(test, feature = "test-support"))] - fn render_to_image(&self, _scene: &Scene) -> Result { - anyhow::bail!("render_to_image not implemented for this platform") - } -} - -/// A renderer for headless windows that can produce real rendered output. -#[cfg(any(test, feature = "test-support", feature = "bench-support"))] -pub trait PlatformHeadlessRenderer { - /// Render a scene and return the result as an RGBA image. - fn render_scene_to_image( - &mut self, - scene: &Scene, - size: Size, - ) -> Result; - - /// Render a scene to an offscreen target without reading the result back. - /// - /// This is the headless analogue of presenting a frame: it performs the - /// same CPU-side scene encoding and GPU submission as drawing to a real - /// window, but doesn't block on GPU completion or copy pixels back. - fn render_scene(&mut self, scene: &Scene, size: Size) -> Result<()>; - - /// Returns the sprite atlas used by this renderer. - fn sprite_atlas(&self) -> Arc; -} - -/// Type alias for runnables with metadata. -/// Previously an enum with a single variant, now simplified to a direct type alias. -#[doc(hidden)] -pub type RunnableVariant = Runnable; - -#[doc(hidden)] -pub type TimerResolutionGuard = gpui_util::Deferred>; - -#[doc(hidden)] -pub enum TasksIncluded { - OnlyCompleted, - CompletedAndRunning, -} - -/// This type is public so that our test macro can generate and use it, but it should not -/// be considered part of our public API. -#[doc(hidden)] -pub trait PlatformDispatcher: Send + Sync { - fn is_main_thread(&self) -> bool; - fn dispatch(&self, runnable: RunnableVariant, priority: Priority); - fn dispatch_on_main_thread(&self, runnable: RunnableVariant, priority: Priority); - fn dispatch_after(&self, duration: Duration, runnable: RunnableVariant); - - fn dispatch_on_main_thread_when_idle( - &self, - runnable: RunnableVariant, - timeout: Option, - ) { - let _ = timeout; - self.dispatch_on_main_thread(runnable, Priority::Low); - } - - fn idle_time_remaining(&self) -> Option { - None - } - - fn spawn_realtime(&self, f: Box); - - fn now(&self) -> Instant { - Instant::now() - } - - fn increase_timer_resolution(&self) -> TimerResolutionGuard { - gpui_util::defer(Box::new(|| {})) - } - - #[cfg(any(test, feature = "test-support", feature = "bench-support"))] - fn as_test(&self) -> Option<&TestDispatcher> { - None - } - - // This cfg must match the `threaded_dispatcher` module's, which implements - // this method whenever it compiles. - #[cfg(any(test, feature = "test-support", feature = "bench-support"))] - fn as_threaded(&self) -> Option<&ThreadedDispatcher> { - None - } -} - -#[expect(missing_docs)] -pub trait PlatformTextSystem: Send + Sync { - fn add_fonts(&self, fonts: Vec>) -> Result<()>; - /// Get all available font names. - fn all_font_names(&self) -> Vec; - /// Get the font ID for a font descriptor. - fn font_id(&self, descriptor: &Font) -> Result; - /// Prewarm any system font caches needed to shape text. - fn prewarm_fonts(&self, _font_ids: &[FontId]) {} - /// Get metrics for a font. - fn font_metrics(&self, font_id: FontId) -> FontMetrics; - /// Get typographic bounds for a glyph. - fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; - /// Get the advance width for a glyph. - fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; - /// Get the glyph ID for a character. - fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option; - /// Get raster bounds for a glyph. - fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result>; - /// Rasterize a glyph. - fn rasterize_glyph( - &self, - params: &RenderGlyphParams, - raster_bounds: Bounds, - ) -> Result<(Size, Vec)>; - /// Layout a line of text with the given font runs. - fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout; - /// Returns the recommended text rendering mode for the given font and size. - fn recommended_rendering_mode(&self, _font_id: FontId, _font_size: Pixels) - -> TextRenderingMode; - /// Returns the dilation level to use for a glyph painted in the given color. - fn glyph_dilation_for_color(&self, _color: Hsla) -> u8 { - 0 - } -} - -#[expect(missing_docs)] -pub struct NoopTextSystem; - -#[expect(missing_docs)] -impl NoopTextSystem { - #[allow(dead_code)] - pub fn new() -> Self { - Self - } -} - -impl PlatformTextSystem for NoopTextSystem { - fn add_fonts(&self, _fonts: Vec>) -> Result<()> { - Ok(()) - } - - fn all_font_names(&self) -> Vec { - Vec::new() - } - - fn font_id(&self, _descriptor: &Font) -> Result { - Ok(FontId(1)) - } - - fn font_metrics(&self, _font_id: FontId) -> FontMetrics { - FontMetrics { - units_per_em: 1000, - ascent: 1025.0, - descent: -275.0, - line_gap: 0.0, - underline_position: -95.0, - underline_thickness: 60.0, - cap_height: 698.0, - x_height: 516.0, - bounding_box: Bounds { - origin: Point { - x: -260.0, - y: -245.0, - }, - size: Size { - width: 1501.0, - height: 1364.0, - }, - }, - } - } - - fn typographic_bounds(&self, _font_id: FontId, _glyph_id: GlyphId) -> Result> { - Ok(Bounds { - origin: Point { x: 54.0, y: 0.0 }, - size: size(392.0, 528.0), - }) - } - - fn advance(&self, _font_id: FontId, glyph_id: GlyphId) -> Result> { - Ok(size(600.0 * glyph_id.0 as f32, 0.0)) - } - - fn glyph_for_char(&self, _font_id: FontId, ch: char) -> Option { - Some(GlyphId(ch.len_utf16() as u32)) - } - - fn glyph_raster_bounds(&self, _params: &RenderGlyphParams) -> Result> { - Ok(Default::default()) - } - - fn rasterize_glyph( - &self, - _params: &RenderGlyphParams, - raster_bounds: Bounds, - ) -> Result<(Size, Vec)> { - Ok((raster_bounds.size, Vec::new())) - } - - fn layout_line(&self, text: &str, font_size: Pixels, _runs: &[FontRun]) -> LineLayout { - let mut position = px(0.); - let metrics = self.font_metrics(FontId(0)); - let em_width = font_size - * self - .advance(FontId(0), self.glyph_for_char(FontId(0), 'm').unwrap()) - .unwrap() - .width - / metrics.units_per_em as f32; - let mut glyphs = Vec::new(); - for (ix, c) in text.char_indices() { - if let Some(glyph) = self.glyph_for_char(FontId(0), c) { - glyphs.push(ShapedGlyph { - id: glyph, - position: point(position, px(0.)), - index: ix, - is_emoji: glyph.0 == 2, - }); - if glyph.0 == 2 { - position += em_width * 2.0; - } else { - position += em_width; - } - } else { - position += em_width - } - } - let mut runs = Vec::default(); - if !glyphs.is_empty() { - runs.push(ShapedRun { - font_id: FontId(0), - glyphs, - }); - } else { - position = px(0.); - } - - LineLayout { - font_size, - width: position, - ascent: font_size * (metrics.ascent / metrics.units_per_em as f32), - descent: font_size * (metrics.descent / metrics.units_per_em as f32), - runs, - len: text.len(), - } - } - - fn recommended_rendering_mode( - &self, - _font_id: FontId, - _font_size: Pixels, - ) -> TextRenderingMode { - TextRenderingMode::Grayscale - } -} - -// Adapted from https://github.com/microsoft/terminal/blob/1283c0f5b99a2961673249fa77c6b986efb5086c/src/renderer/atlas/dwrite.cpp -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -/// Compute gamma correction ratios for subpixel text rendering. -#[allow(dead_code)] -pub fn get_gamma_correction_ratios(gamma: f32) -> [f32; 4] { - const GAMMA_INCORRECT_TARGET_RATIOS: [[f32; 4]; 13] = [ - [0.0000 / 4.0, 0.0000 / 4.0, 0.0000 / 4.0, 0.0000 / 4.0], // gamma = 1.0 - [0.0166 / 4.0, -0.0807 / 4.0, 0.2227 / 4.0, -0.0751 / 4.0], // gamma = 1.1 - [0.0350 / 4.0, -0.1760 / 4.0, 0.4325 / 4.0, -0.1370 / 4.0], // gamma = 1.2 - [0.0543 / 4.0, -0.2821 / 4.0, 0.6302 / 4.0, -0.1876 / 4.0], // gamma = 1.3 - [0.0739 / 4.0, -0.3963 / 4.0, 0.8167 / 4.0, -0.2287 / 4.0], // gamma = 1.4 - [0.0933 / 4.0, -0.5161 / 4.0, 0.9926 / 4.0, -0.2616 / 4.0], // gamma = 1.5 - [0.1121 / 4.0, -0.6395 / 4.0, 1.1588 / 4.0, -0.2877 / 4.0], // gamma = 1.6 - [0.1300 / 4.0, -0.7649 / 4.0, 1.3159 / 4.0, -0.3080 / 4.0], // gamma = 1.7 - [0.1469 / 4.0, -0.8911 / 4.0, 1.4644 / 4.0, -0.3234 / 4.0], // gamma = 1.8 - [0.1627 / 4.0, -1.0170 / 4.0, 1.6051 / 4.0, -0.3347 / 4.0], // gamma = 1.9 - [0.1773 / 4.0, -1.1420 / 4.0, 1.7385 / 4.0, -0.3426 / 4.0], // gamma = 2.0 - [0.1908 / 4.0, -1.2652 / 4.0, 1.8650 / 4.0, -0.3476 / 4.0], // gamma = 2.1 - [0.2031 / 4.0, -1.3864 / 4.0, 1.9851 / 4.0, -0.3501 / 4.0], // gamma = 2.2 - ]; - - const NORM13: f32 = ((0x10000 as f64) / (255.0 * 255.0) * 4.0) as f32; - const NORM24: f32 = ((0x100 as f64) / (255.0) * 4.0) as f32; - - let index = ((gamma * 10.0).round() as usize).clamp(10, 22) - 10; - let ratios = GAMMA_INCORRECT_TARGET_RATIOS[index]; - - [ - ratios[0] * NORM13, - ratios[1] * NORM24, - ratios[2] * NORM13, - ratios[3] * NORM24, - ] -} - -#[derive(PartialEq, Eq, Hash, Clone)] -#[expect(missing_docs)] -pub enum AtlasKey { - Glyph(RenderGlyphParams), - Svg(RenderSvgParams), - Image(RenderImageParams), -} - -impl AtlasKey { - #[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) - )] - /// Returns the texture kind for this atlas key. - pub fn texture_kind(&self) -> AtlasTextureKind { - match self { - AtlasKey::Glyph(params) => { - if params.is_emoji { - AtlasTextureKind::Polychrome - } else if params.subpixel_rendering { - AtlasTextureKind::Subpixel - } else { - AtlasTextureKind::Monochrome - } - } - AtlasKey::Svg(_) => AtlasTextureKind::Monochrome, - AtlasKey::Image(_) => AtlasTextureKind::Polychrome, - } - } -} - -impl From for AtlasKey { - fn from(params: RenderGlyphParams) -> Self { - Self::Glyph(params) - } -} - -impl From for AtlasKey { - fn from(params: RenderSvgParams) -> Self { - Self::Svg(params) - } -} - -impl From for AtlasKey { - fn from(params: RenderImageParams) -> Self { - Self::Image(params) - } -} - -#[expect(missing_docs)] -pub trait PlatformAtlas { - fn get_or_insert_with<'a>( - &self, - key: &AtlasKey, - build: &mut dyn FnMut() -> Result, Cow<'a, [u8]>)>>, - ) -> Result>; - fn remove(&self, key: &AtlasKey); - - #[cfg(any(test, feature = "test-support", feature = "bench-support"))] - fn contains(&self, _key: &AtlasKey) -> bool { - false - } -} - -#[doc(hidden)] -pub struct AtlasTextureList { - pub textures: Vec>, - pub free_list: Vec, -} - -impl Default for AtlasTextureList { - fn default() -> Self { - Self { - textures: Vec::default(), - free_list: Vec::default(), - } - } -} - -impl ops::Index for AtlasTextureList { - type Output = Option; - - fn index(&self, index: usize) -> &Self::Output { - &self.textures[index] - } -} - -impl AtlasTextureList { - #[allow(unused)] - pub fn drain(&mut self) -> std::vec::Drain<'_, Option> { - self.free_list.clear(); - self.textures.drain(..) - } - - #[allow(dead_code)] - pub fn iter_mut(&mut self) -> impl DoubleEndedIterator { - self.textures.iter_mut().flatten() - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(C)] -#[expect(missing_docs)] -pub struct AtlasTile { - /// The texture this tile belongs to. - pub texture_id: AtlasTextureId, - /// The unique ID of this tile within its texture. - pub tile_id: TileId, - /// Padding around the tile content in pixels. - pub padding: u32, - /// The bounds of this tile within the texture. - pub bounds: Bounds, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[repr(C)] -#[expect(missing_docs)] -pub struct AtlasTextureId { - // We use u32 instead of usize for Metal Shader Language compatibility - /// The index of this texture in the atlas. - pub index: u32, - /// The kind of content stored in this texture. - pub kind: AtlasTextureKind, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[repr(C)] -#[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) -)] -#[expect(missing_docs)] -pub enum AtlasTextureKind { - Monochrome = 0, - Polychrome = 1, - Subpixel = 2, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -#[repr(C)] -#[expect(missing_docs)] -pub struct TileId(pub u32); - -impl From for TileId { - fn from(id: etagere::AllocId) -> Self { - Self(id.serialize()) - } -} - -impl From for etagere::AllocId { - fn from(id: TileId) -> Self { - Self::deserialize(id.0) - } -} - -#[expect(missing_docs)] -pub struct PlatformInputHandler { - cx: AsyncWindowContext, - handler: Box, -} - -#[expect(missing_docs)] -#[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) -)] -impl PlatformInputHandler { - pub fn new(cx: AsyncWindowContext, handler: Box) -> Self { - Self { cx, handler } - } - - pub fn selected_text_range(&mut self, ignore_disabled_input: bool) -> Option { - self.cx - .update(|window, cx| { - self.handler - .selected_text_range(ignore_disabled_input, window, cx) - }) - .ok() - .flatten() - } - - #[cfg_attr(target_os = "windows", allow(dead_code))] - pub fn marked_text_range(&mut self) -> Option> { - self.cx - .update(|window, cx| self.handler.marked_text_range(window, cx)) - .ok() - .flatten() - } - - #[cfg_attr( - any(target_os = "linux", target_os = "freebsd", target_os = "windows"), - allow(dead_code) - )] - pub fn text_for_range( - &mut self, - range_utf16: Range, - adjusted: &mut Option>, - ) -> Option { - self.cx - .update(|window, cx| { - self.handler - .text_for_range(range_utf16, adjusted, window, cx) - }) - .ok() - .flatten() - } - - pub fn replace_text_in_range(&mut self, replacement_range: Option>, text: &str) { - self.cx - .update(|window, cx| { - self.handler - .replace_text_in_range(replacement_range, text, window, cx); - }) - .ok(); - } - - pub fn replace_and_mark_text_in_range( - &mut self, - range_utf16: Option>, - new_text: &str, - new_selected_range: Option>, - ) { - self.cx - .update(|window, cx| { - self.handler.replace_and_mark_text_in_range( - range_utf16, - new_text, - new_selected_range, - window, - cx, - ) - }) - .ok(); - } - - #[cfg_attr(target_os = "windows", allow(dead_code))] - pub fn unmark_text(&mut self) { - self.cx - .update(|window, cx| self.handler.unmark_text(window, cx)) - .ok(); - } - - pub fn paste(&mut self, item: ClipboardItem) { - self.cx - .update(|window, cx| self.handler.paste(item, window, cx)) - .ok(); - } - - pub fn bounds_for_range(&mut self, range_utf16: Range) -> Option> { - self.cx - .update(|window, cx| self.handler.bounds_for_range(range_utf16, window, cx)) - .ok() - .flatten() - } - - #[allow(dead_code)] - pub fn apple_press_and_hold_enabled(&mut self) -> bool { - self.handler.apple_press_and_hold_enabled() - } - - pub fn dispatch_input(&mut self, input: &str, window: &mut Window, cx: &mut App) { - self.handler.replace_text_in_range(None, input, window, cx); - } - - pub fn compute_ime_candidate_bounds( - marked_range: Option>, - selection: &UTF16Selection, - mut bounds_for_range: impl FnMut(Range) -> Option>, - ) -> Option> { - if let Some(marked_range) = marked_range { - // Default to the start of the marked (composing) range. - let mut line_start = marked_range.start; - - // Walk backward from the caret looking for a line break. A change in - // the Y coordinate means we crossed into the previous visual line, so - // the line start is one position after the break point. - let caret = selection.range.end; - if let Some(caret_bounds) = bounds_for_range(caret..caret) { - for i in (marked_range.start..caret).rev() { - if let Some(b) = bounds_for_range(i..i) { - if (b.origin.y - caret_bounds.origin.y).abs() > px(0.1) { - line_start = i + 1; - break; - } - } - } - } - bounds_for_range(line_start..line_start) - } else { - // No active composition — use the selection endpoint. - let offset = if selection.reversed { - selection.range.start - } else { - selection.range.end - }; - bounds_for_range(offset..offset) - } - } - - pub fn selected_bounds(&mut self, window: &mut Window, cx: &mut App) -> Option> { - let marked_range = self.handler.marked_text_range(window, cx); - let selection = self.handler.selected_text_range(true, window, cx)?; - Self::compute_ime_candidate_bounds(marked_range, &selection, |range| { - self.handler.bounds_for_range(range, window, cx) - }) - } - - pub fn ime_candidate_bounds(&mut self) -> Option> { - let marked_range = self.marked_text_range(); - let selection = self.selected_text_range(true)?; - Self::compute_ime_candidate_bounds(marked_range, &selection, |range| { - self.bounds_for_range(range) - }) - } - - #[allow(unused)] - pub fn character_index_for_point(&mut self, point: Point) -> Option { - self.cx - .update(|window, cx| self.handler.character_index_for_point(point, window, cx)) - .ok() - .flatten() - } - - /// See [`InputHandler::set_selected_text_range`]. - pub fn set_selected_text_range(&mut self, range_utf16: Range) { - self.cx - .update(|window, cx| { - self.handler - .set_selected_text_range(range_utf16, window, cx) - }) - .ok(); - } - - /// See [`InputHandler::element_bounds`]. - pub fn element_bounds(&mut self) -> Option> { - self.cx - .update(|window, cx| self.handler.element_bounds(window, cx)) - .ok() - .flatten() - } - - /// See [`InputHandler::text_length_utf16`]. - pub fn text_length_utf16(&mut self) -> Option { - self.cx - .update(|window, cx| self.handler.text_length_utf16(window, cx)) - .ok() - .flatten() - } - - #[allow(dead_code)] - pub fn accepts_text_input(&mut self, window: &mut Window, cx: &mut App) -> bool { - self.handler.accepts_text_input(window, cx) - } - - #[allow(dead_code)] - pub fn query_accepts_text_input(&mut self) -> bool { - self.cx - .update(|window, cx| self.handler.accepts_text_input(window, cx)) - .unwrap_or(true) - } - - /// See [`InputHandler::prefers_ime_for_printable_keys`]. - /// - /// This is not a pure delegation to the handler: while a multi-stroke binding is pending this - /// returns `false` regardless of the handler's preference, because the next printable key may - /// complete a binding whose prefix already bypassed the IME. - pub fn query_prefers_ime_for_printable_keys(&mut self) -> bool { - self.cx - .update(|window, cx| { - // The next printable key may complete a chord whose prefix bypassed the IME. - !window.has_pending_keystrokes() - && self.handler.prefers_ime_for_printable_keys(window, cx) - }) - .unwrap_or(false) - } - - /// See [`InputHandler::text_input_configuration`]. - pub fn text_input_configuration( - &mut self, - window: &mut Window, - cx: &mut App, - ) -> TextInputConfiguration { - self.handler.text_input_configuration(window, cx) - } - - /// See [`InputHandler::text_input_editable_range`]. - pub fn text_input_editable_range(&mut self) -> Option> { - self.cx - .update(|window, cx| self.handler.text_input_editable_range(window, cx)) - .ok() - .flatten() - } -} - -/// A struct representing a selection in a text buffer, in UTF16 characters. -/// This is different from a range because the head may be before the tail. -#[derive(Debug)] -pub struct UTF16Selection { - /// The range of text in the document this selection corresponds to - /// in UTF16 characters. - pub range: Range, - /// Whether the head of this selection is at the start (true), or end (false) - /// of the range - pub reversed: bool, -} - -/// Zed's interface for handling text input from the platform's IME system -/// This is currently a 1:1 exposure of the NSTextInputClient API: -/// -/// -pub trait InputHandler: 'static { - /// Get the range of the user's currently selected text, if any - /// Corresponds to [selectedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438242-selectedrange) - /// - /// Return value is in terms of UTF-16 characters, from 0 to the length of the document - fn selected_text_range( - &mut self, - ignore_disabled_input: bool, - window: &mut Window, - cx: &mut App, - ) -> Option; - - /// Get the range of the currently marked text, if any - /// Corresponds to [markedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438250-markedrange) - /// - /// Return value is in terms of UTF-16 characters, from 0 to the length of the document - fn marked_text_range(&mut self, window: &mut Window, cx: &mut App) -> Option>; - - /// Get the text for the given document range in UTF-16 characters - /// Corresponds to [attributedSubstring(forProposedRange: actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438238-attributedsubstring) - /// - /// range_utf16 is in terms of UTF-16 characters - fn text_for_range( - &mut self, - range_utf16: Range, - adjusted_range: &mut Option>, - window: &mut Window, - cx: &mut App, - ) -> Option; - - /// Replace the text in the given document range with the given text - /// Corresponds to [insertText(_:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438258-inserttext) - /// - /// replacement_range is in terms of UTF-16 characters - fn replace_text_in_range( - &mut self, - replacement_range: Option>, - text: &str, - window: &mut Window, - cx: &mut App, - ); - - /// Replace the text in the given document range with the given text, - /// and mark the given text as part of an IME 'composing' state - /// Corresponds to [setMarkedText(_:selectedRange:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438246-setmarkedtext) - /// - /// range_utf16 is in terms of UTF-16 characters - /// new_selected_range is in terms of UTF-16 characters - fn replace_and_mark_text_in_range( - &mut self, - range_utf16: Option>, - new_text: &str, - new_selected_range: Option>, - window: &mut Window, - cx: &mut App, - ); - - /// Remove the IME 'composing' state from the document - /// Corresponds to [unmarkText()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438239-unmarktext) - fn unmark_text(&mut self, window: &mut Window, cx: &mut App); - - /// Insert a platform-initiated paste at the current selection. - /// - /// Platforms that deliver paste as an input event rather than through an - /// application-defined action (e.g. the DOM `paste` event on web) call - /// this with the full clipboard contents. The default implementation - /// inserts only the plain-text portion of the item. - fn paste(&mut self, item: ClipboardItem, window: &mut Window, cx: &mut App) { - if let Some(text) = item.text() { - self.replace_text_in_range(None, &text, window, cx); - } - } - - /// Get the bounds of the given document range in screen coordinates - /// Corresponds to [firstRect(forCharacterRange:actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438240-firstrect) - /// - /// This is used for positioning the IME candidate window - fn bounds_for_range( - &mut self, - range_utf16: Range, - window: &mut Window, - cx: &mut App, - ) -> Option>; - - /// Get the character offset for the given point in terms of UTF16 characters - /// - /// Corresponds to [characterIndexForPoint:](https://developer.apple.com/documentation/appkit/nstextinputclient/characterindex(for:)) - fn character_index_for_point( - &mut self, - point: Point, - window: &mut Window, - cx: &mut App, - ) -> Option; - - /// Set the range of the user's currently selected text. - /// - /// This is the reverse data-flow direction from [`Self::selected_text_range`]: - /// platforms call it when the system text machinery moves the selection on the - /// application's behalf — e.g. the user drags a system selection handle or - /// invokes Select All from system UI (iOS `UITextInput setSelectedTextRange:`, - /// Android `InputConnection.setSelection`). - /// - /// range_utf16 is in terms of UTF-16 characters, from 0 to the length of the document - fn set_selected_text_range( - &mut self, - _range_utf16: Range, - _window: &mut Window, - _cx: &mut App, - ) { - } - - /// Get the bounds of the focused text element in window coordinates, if known. - /// - /// This is the pull counterpart to the [`PlatformWindow::update_ime_position`] - /// push: mobile platforms ask for the focused element's geometry when they - /// need it (e.g. to frame system text-interaction UI overlaid on the focused - /// element). - fn element_bounds(&mut self, _window: &mut Window, _cx: &mut App) -> Option> { - None - } - - /// Get the length of the document in UTF-16 characters, if known. - fn text_length_utf16(&mut self, _window: &mut Window, _cx: &mut App) -> Option { - None - } - - /// Allows a given input context to opt into getting raw key repeats instead of - /// sending these to the platform. - /// TODO: Ideally we should be able to set ApplePressAndHoldEnabled in NSUserDefaults - /// (which is how iTerm does it) but it doesn't seem to work for me. - #[allow(dead_code)] - fn apple_press_and_hold_enabled(&mut self) -> bool { - true - } - - /// Returns whether this handler is accepting text input to be inserted. - fn accepts_text_input(&mut self, _window: &mut Window, _cx: &mut App) -> bool { - true - } - - /// The contiguous range of text, in UTF-16 code units, that platform text - /// input may read and edit around the current selection. - /// - /// Platforms that mirror document text into an IME-editable buffer clamp - /// the mirrored window to this range, so multi-step IME edit gestures - /// (word deletion, autocorrect rewrites, suggestion picks) cannot reach - /// content outside it. The range should contain the current selection; - /// when it cannot (a selection spanning a region boundary), platforms - /// degrade the mirrored IME context rather than widening the range. - /// `None` places no bound. - fn text_input_editable_range( - &mut self, - _window: &mut Window, - _cx: &mut App, - ) -> Option> { - None - } - - /// Returns whether printable keys should be routed to the IME before keybinding - /// matching when a non-ASCII input source (e.g. Japanese, Korean, Chinese IME) - /// is active. This prevents multi-stroke keybindings like `jj` from intercepting - /// keys that the IME should compose. - /// - /// Defaults to `false`. The editor overrides this based on whether it expects - /// character input (e.g. Vim insert mode returns `true`, normal mode returns `false`). - /// The terminal keeps the default `false` so that raw keys reach the terminal process. - fn prefers_ime_for_printable_keys(&mut self, _window: &mut Window, _cx: &mut App) -> bool { - false - } - - /// Get this handler's preferences for platform text assistance. - /// - /// GPUI re-queries this every frame and forwards it to the platform window - /// only when it changes, so implementations must be cheap and may vary the - /// result with application state (e.g. with the cursor's position). - fn text_input_configuration( - &mut self, - _window: &mut Window, - _cx: &mut App, - ) -> TextInputConfiguration { - TextInputConfiguration::default() - } -} - -/// Platform text-assistance preferences for the focused text region. -/// -/// Returned by [`InputHandler::text_input_configuration`] and forwarded to the -/// platform whenever it changes; the platform maps the fields onto its native -/// input-session attributes (on web, DOM attributes of the hidden editable -/// element such as `autocorrect` and `enterkeyhint`). -/// -/// The default disables all text assistance and requests no particular action -/// key presentation. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct TextInputConfiguration { - /// Whether the platform may automatically correct entered text. - pub autocorrect: bool, - /// How software keyboards automatically capitalize entered text. - pub autocapitalize: Autocapitalize, - /// Whether software keyboards may offer word suggestions and spellcheck. - pub suggestions: bool, - /// The action advertised on a software keyboard's confirm ("enter") key. - pub input_action: TextInputAction, -} - -/// Automatic capitalization applied by software keyboards. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum Autocapitalize { - /// No automatic capitalization. - #[default] - None, - /// Capitalize the first letter of each word. - Words, - /// Capitalize the first letter of each sentence. - Sentences, - /// Capitalize every letter. - Characters, -} - -/// The action a software keyboard advertises on its confirm ("enter") key. -/// -/// This affects only how the key is presented (icon or label); pressing it is -/// still delivered as ordinary input. -/// -/// The variants are the HTML `enterkeyhint` attribute's value set -/// (), -/// which also maps onto Android's `IME_ACTION_*` constants and iOS's -/// `UIReturnKeyType`; [`TextInputAction::Unspecified`] means "emit no hint". -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum TextInputAction { - /// Let the platform choose its default presentation. - #[default] - Unspecified, - /// Inserting a line break. - Enter, - /// Committing the field's value. - Done, - /// Navigating to the typed target. - Go, - /// Moving to the next field. - Next, - /// Moving to the previous field. - Previous, - /// Executing a search. - Search, - /// Sending a message. - Send, -} - -/// The variables that can be configured when creating a new window -#[derive(Debug)] -pub struct WindowOptions { - /// Specifies the state and bounds of the window in screen coordinates. - /// - `None`: Inherit the bounds. - /// - `Some(WindowBounds)`: Open a window with corresponding state and its restore size. - pub window_bounds: Option, - - /// The titlebar configuration of the window - pub titlebar: Option, - - /// Whether the window should be focused when created - pub focus: bool, - - /// Whether the window should be shown when created - pub show: bool, - - /// The kind of window to create - pub kind: WindowKind, - - /// Whether the window can be moved by the user. When `false`, the user cannot drag - /// the window (on macOS this sets `NSWindow.isMovable`, which also disables the - /// Window-menu tiling items); programmatic moves are still allowed. - pub is_movable: bool, - - /// Whether the application owns dragging of the (custom) titlebar, rather than - /// AppKit. Only has an effect on macOS. - /// - /// Set this to `true` for windows that draw their own titlebar and move the window - /// themselves via [`Window::start_window_move`]. It marks the whole content view as - /// app-owned titlebar content, so AppKit neither drags the window from the titlebar - /// nor delays titlebar clicks while disambiguating double-clicks (a delay first - /// observed on macOS 27). It is independent of `is_movable`, so such windows stay - /// user-movable (via their own drag) and keep the Window-menu tiling items enabled. - /// - /// Leave this `false` for windows that rely on AppKit's native titlebar dragging. - pub app_owns_titlebar_drag: bool, - - /// The minimum interval between animation frames while the window is inactive. - /// - /// Set to `None` to disable inactive-window animation frame throttling. - pub inactive_frame_interval: Option, - - /// Whether the window should be resizable by the user - pub is_resizable: bool, - - /// Whether the window should be minimized by the user - pub is_minimizable: bool, - - /// The display to create the window on, if this is None, - /// the window will be created on the main display - pub display_id: Option, - - /// The appearance of the window background. - pub window_background: WindowBackgroundAppearance, - - /// Application identifier of the window. Can by used by desktop environments to group applications together. - pub app_id: Option, - - /// Window minimum size - pub window_min_size: Option>, - - /// Whether to use client or server-side decorations on X11 and Wayland. - /// The platform may ignore requests it cannot satisfy. - pub window_decorations: Option, - - /// Icon image (X11 only) - pub icon: Option>, - - /// Tab group name, allows opening the window as a native tab on macOS 10.12+. Windows with the same tabbing identifier will be grouped together. - pub tabbing_identifier: Option, -} - -/// The variables that can be configured when creating a new window -#[derive(Debug)] -#[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) -)] -#[allow(missing_docs)] -pub struct WindowParams { - pub bounds: Bounds, - - /// The titlebar configuration of the window - #[cfg_attr(feature = "wayland", allow(dead_code))] - pub titlebar: Option, - - /// The kind of window to create - #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))] - pub kind: WindowKind, - - /// Whether the window should be movable by the user - #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))] - pub is_movable: bool, - - /// Whether the application owns dragging of the (custom) titlebar (macOS only) - #[cfg_attr( - any(target_os = "linux", target_os = "freebsd", target_os = "windows"), - allow(dead_code) - )] - pub app_owns_titlebar_drag: bool, - - /// Whether the window should be resizable by the user - #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))] - pub is_resizable: bool, - - /// Whether the window should be minimized by the user - #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))] - pub is_minimizable: bool, - - #[cfg_attr( - any(target_os = "linux", target_os = "freebsd", target_os = "windows"), - allow(dead_code) - )] - pub focus: bool, - - #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))] - pub show: bool, - - /// An image to set as the window icon (x11 only) - #[cfg_attr(feature = "wayland", allow(dead_code))] - pub icon: Option>, - - #[cfg_attr(feature = "wayland", allow(dead_code))] - pub display_id: Option, - - #[cfg_attr(feature = "wayland", allow(dead_code))] - pub app_id: Option, - - pub window_min_size: Option>, - - #[cfg(target_os = "macos")] - pub tabbing_identifier: Option, -} - -/// Represents the status of how a window should be opened. -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum WindowBounds { - /// Indicates that the window should open in a windowed state with the given bounds. - Windowed(Bounds), - /// Indicates that the window should open in a maximized state. - /// The bounds provided here represent the restore size of the window. - Maximized(Bounds), - /// Indicates that the window should open in fullscreen mode. - /// The bounds provided here represent the restore size of the window. - Fullscreen(Bounds), -} - -impl Default for WindowBounds { - fn default() -> Self { - WindowBounds::Windowed(Bounds::default()) - } -} - -impl WindowBounds { - /// Retrieve the inner bounds - pub fn get_bounds(&self) -> Bounds { - match self { - WindowBounds::Windowed(bounds) => *bounds, - WindowBounds::Maximized(bounds) => *bounds, - WindowBounds::Fullscreen(bounds) => *bounds, - } - } - - /// Creates a new window bounds that centers the window on the screen. - pub fn centered(size: Size, cx: &App) -> Self { - WindowBounds::Windowed(Bounds::centered(None, size, cx)) - } -} - -impl Default for WindowOptions { - fn default() -> Self { - Self { - window_bounds: None, - titlebar: Some(TitlebarOptions { - title: Default::default(), - appears_transparent: Default::default(), - traffic_light_position: Default::default(), - }), - focus: true, - show: true, - kind: WindowKind::Normal, - is_movable: true, - app_owns_titlebar_drag: false, - inactive_frame_interval: Some(Duration::from_micros(33_333)), - is_resizable: true, - is_minimizable: true, - display_id: None, - window_background: WindowBackgroundAppearance::default(), - icon: None, - app_id: None, - window_min_size: None, - window_decorations: None, - tabbing_identifier: None, - } - } -} - -/// The options that can be configured for a window's titlebar -#[derive(Debug, Default)] -pub struct TitlebarOptions { - /// The initial title of the window - pub title: Option, - - /// Should the default system titlebar be hidden to allow for a custom-drawn titlebar? (macOS and Windows only) - /// Refer to [`WindowOptions::window_decorations`] on Linux - pub appears_transparent: bool, - - /// The position of the macOS traffic light buttons - pub traffic_light_position: Option>, -} - -/// The kind of window to create -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum WindowKind { - /// A normal application window - Normal, - - /// A window that appears above all other windows, usually used for alerts or popups - /// use sparingly! - PopUp, - - /// A parent-anchored, platform-native popup window for menus, comboboxes, context menus and - /// tooltips. Unlike [`WindowKind::PopUp`], it is positioned relative to a parent window. - /// - /// The popup's size comes from [`WindowOptions::window_bounds`], whose origin is ignored. - /// See [`popup::PopupOptions`] for the placement options. Platforms without a native - /// implementation reject it with [`popup::PopupNotSupportedError`]. - AnchoredPopup(popup::PopupOptions), - - /// A floating window that appears on top of its parent window - Floating, - - /// A Wayland LayerShell window, used to draw overlays or backgrounds for applications such as - /// docks, notifications or wallpapers. - #[cfg(all(target_os = "linux", feature = "wayland"))] - LayerShell(layer_shell::LayerShellOptions), - - /// A window that appears on top of its parent window and blocks interaction with it - /// until the modal window is closed - Dialog, -} - -/// The appearance of the window, as defined by the operating system. -/// -/// On macOS, this corresponds to named [`NSAppearance`](https://developer.apple.com/documentation/appkit/nsappearance) -/// values. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub enum WindowAppearance { - /// A light appearance. - /// - /// On macOS, this corresponds to the `aqua` appearance. - #[default] - Light, - - /// A light appearance with vibrant colors. - /// - /// On macOS, this corresponds to the `NSAppearanceNameVibrantLight` appearance. - VibrantLight, - - /// A dark appearance. - /// - /// On macOS, this corresponds to the `darkAqua` appearance. - Dark, - - /// A dark appearance with vibrant colors. - /// - /// On macOS, this corresponds to the `NSAppearanceNameVibrantDark` appearance. - VibrantDark, -} - -/// The appearance of the background of the window itself, when there is -/// no content or the content is transparent. -#[derive(Copy, Clone, Debug, Default, PartialEq)] -pub enum WindowBackgroundAppearance { - /// Opaque. - /// - /// This lets the window manager know that content behind this - /// window does not need to be drawn. - /// - /// Actual color depends on the system and themes should define a fully - /// opaque background color instead. - #[default] - Opaque, - /// Plain alpha transparency. - Transparent, - /// Transparency, but the contents behind the window are blurred. - /// - /// Not always supported. - Blurred, - /// The Mica backdrop material, supported on Windows 11. - MicaBackdrop, - /// The Mica Alt backdrop material, supported on Windows 11. - MicaAltBackdrop, -} - -/// The text rendering mode to use for drawing glyphs. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub enum TextRenderingMode { - /// Use the platform's default text rendering mode. - #[default] - PlatformDefault, - /// Use subpixel (ClearType-style) text rendering. - Subpixel, - /// Use grayscale text rendering. - Grayscale, -} - -/// The options that can be configured for a file dialog prompt -#[derive(Clone, Debug)] -pub struct PathPromptOptions { - /// Should the prompt allow files to be selected? - pub files: bool, - /// Should the prompt allow directories to be selected? - pub directories: bool, - /// Should the prompt allow multiple files to be selected? - pub multiple: bool, - /// The prompt to show to a user when selecting a path - pub prompt: Option, -} - -/// What kind of prompt styling to show -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum PromptLevel { - /// A prompt that is shown when the user should be notified of something - Info, - - /// A prompt that is shown when the user needs to be warned of a potential problem - Warning, - - /// A prompt that is shown when a critical problem has occurred - Critical, -} - -/// Prompt Button -#[derive(Clone, Debug, PartialEq)] -pub enum PromptButton { - /// Ok button - Ok(SharedString), - /// Cancel button - Cancel(SharedString), - /// Other button - Other(SharedString), -} - -impl PromptButton { - /// Create a button with label - pub fn new(label: impl Into) -> Self { - PromptButton::Other(label.into()) - } - - /// Create an Ok button - pub fn ok(label: impl Into) -> Self { - PromptButton::Ok(label.into()) - } - - /// Create a Cancel button - pub fn cancel(label: impl Into) -> Self { - PromptButton::Cancel(label.into()) - } - - /// Returns true if this button is a cancel button. - #[allow(dead_code)] - pub fn is_cancel(&self) -> bool { - matches!(self, PromptButton::Cancel(_)) - } - - /// Returns the label of the button - pub fn label(&self) -> &SharedString { - match self { - PromptButton::Ok(label) => label, - PromptButton::Cancel(label) => label, - PromptButton::Other(label) => label, - } - } -} - -impl From<&str> for PromptButton { - fn from(value: &str) -> Self { - match value.to_lowercase().as_str() { - "ok" => PromptButton::Ok("OK".into()), - "cancel" => PromptButton::Cancel("Cancel".into()), - _ => PromptButton::Other(SharedString::from(value.to_owned())), - } - } -} - -/// The style of the cursor (pointer) -#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] -pub enum CursorStyle { - /// The default cursor - #[default] - Arrow, - - /// A text input cursor - /// corresponds to the CSS cursor value `text` - IBeam, - - /// A crosshair cursor - /// corresponds to the CSS cursor value `crosshair` - Crosshair, - - /// A closed hand cursor - /// corresponds to the CSS cursor value `grabbing` - ClosedHand, - - /// An open hand cursor - /// corresponds to the CSS cursor value `grab` - OpenHand, - - /// A pointing hand cursor - /// corresponds to the CSS cursor value `pointer` - PointingHand, - - /// A resize left cursor - /// corresponds to the CSS cursor value `w-resize` - ResizeLeft, - - /// A resize right cursor - /// corresponds to the CSS cursor value `e-resize` - ResizeRight, - - /// A resize cursor to the left and right - /// corresponds to the CSS cursor value `ew-resize` - ResizeLeftRight, - - /// A resize up cursor - /// corresponds to the CSS cursor value `n-resize` - ResizeUp, - - /// A resize down cursor - /// corresponds to the CSS cursor value `s-resize` - ResizeDown, - - /// A resize cursor directing up and down - /// corresponds to the CSS cursor value `ns-resize` - ResizeUpDown, - - /// A resize cursor directing up-left and down-right - /// corresponds to the CSS cursor value `nesw-resize` - ResizeUpLeftDownRight, - - /// A resize cursor directing up-right and down-left - /// corresponds to the CSS cursor value `nwse-resize` - ResizeUpRightDownLeft, - - /// A cursor indicating that the item/column can be resized horizontally. - /// corresponds to the CSS cursor value `col-resize` - ResizeColumn, - - /// A cursor indicating that the item/row can be resized vertically. - /// corresponds to the CSS cursor value `row-resize` - ResizeRow, - - /// A text input cursor for vertical layout - /// corresponds to the CSS cursor value `vertical-text` - IBeamCursorForVerticalLayout, - - /// A cursor indicating that the operation is not allowed - /// corresponds to the CSS cursor value `not-allowed` - OperationNotAllowed, - - /// A cursor indicating that the operation will result in a link - /// corresponds to the CSS cursor value `alias` - DragLink, - - /// A cursor indicating that the operation will result in a copy - /// corresponds to the CSS cursor value `copy` - DragCopy, - - /// A cursor indicating that the operation will result in a context menu - /// corresponds to the CSS cursor value `context-menu` - ContextualMenu, -} - -/// A clipboard item that should be copied to the clipboard -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ClipboardItem { - /// The entries in this clipboard item. - pub entries: Vec, -} - -/// An error produced by [`Platform::read_from_clipboard_async`]. -/// -/// Callers surface these failures to users, so the variants distinguish -/// conditions that call for different user-facing guidance. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum ClipboardReadError { - /// The platform clipboard is not available in this context, e.g. the - /// browser does not expose the async clipboard API or the page is not a - /// secure context. - Unavailable, - /// The platform refused access, e.g. the user declined the browser's - /// clipboard permission prompt or paste confirmation. - Denied(String), - /// The clipboard contents could not be converted into a - /// [`ClipboardItem`]. - UnsupportedContent, -} - -impl std::fmt::Display for ClipboardReadError { - fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Unavailable => formatter.write_str("the clipboard is unavailable"), - Self::Denied(message) => { - write!(formatter, "clipboard access was denied: {message}") - } - Self::UnsupportedContent => { - formatter.write_str("the clipboard contents are unsupported") - } - } - } -} - -impl std::error::Error for ClipboardReadError {} - -/// Either a ClipboardString or a ClipboardImage -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ClipboardEntry { - /// A string entry - String(ClipboardString), - /// An image entry - Image(Image), - /// A file entry - ExternalPaths(crate::ExternalPaths), -} - -impl ClipboardItem { - /// Create a new ClipboardItem::String with no associated metadata - pub fn new_string(text: String) -> Self { - Self { - entries: vec![ClipboardEntry::String(ClipboardString::new(text))], - } - } - - /// Create a new ClipboardItem::String with the given text and associated metadata - pub fn new_string_with_metadata(text: String, metadata: String) -> Self { - Self { - entries: vec![ClipboardEntry::String(ClipboardString { - text, - metadata: Some(metadata), - })], - } - } - - /// Create a new ClipboardItem::String with the given text and associated metadata - pub fn new_string_with_json_metadata(text: String, metadata: T) -> Self { - Self { - entries: vec![ClipboardEntry::String( - ClipboardString::new(text).with_json_metadata(metadata), - )], - } - } - - /// Create a new ClipboardItem::Image with the given image with no associated metadata - pub fn new_image(image: &Image) -> Self { - Self { - entries: vec![ClipboardEntry::Image(image.clone())], - } - } - - /// Concatenates together all the ClipboardString entries in the item. - /// Returns None if there were no ClipboardString entries. - pub fn text(&self) -> Option { - let mut answer = String::new(); - - for entry in self.entries.iter() { - if let ClipboardEntry::String(ClipboardString { text, metadata: _ }) = entry { - answer.push_str(text); - } - } - - if answer.is_empty() { - for entry in self.entries.iter() { - if let ClipboardEntry::ExternalPaths(paths) = entry { - for path in &paths.0 { - use std::fmt::Write as _; - _ = write!(answer, "{}", path.display()); - } - } - } - } - - if !answer.is_empty() { - Some(answer) - } else { - None - } - } - - /// If this item is one ClipboardEntry::String, returns its metadata. - #[cfg_attr(not(target_os = "windows"), allow(dead_code))] - pub fn metadata(&self) -> Option<&String> { - match self.entries().first() { - Some(ClipboardEntry::String(clipboard_string)) if self.entries.len() == 1 => { - clipboard_string.metadata.as_ref() - } - _ => None, - } - } - - /// Get the item's entries - pub fn entries(&self) -> &[ClipboardEntry] { - &self.entries - } - - /// Get owned versions of the item's entries - pub fn into_entries(self) -> impl Iterator { - self.entries.into_iter() - } -} - -impl From for ClipboardEntry { - fn from(value: ClipboardString) -> Self { - Self::String(value) - } -} - -impl From for ClipboardEntry { - fn from(value: String) -> Self { - Self::from(ClipboardString::from(value)) - } -} - -impl From for ClipboardEntry { - fn from(value: Image) -> Self { - Self::Image(value) - } -} - -impl From for ClipboardItem { - fn from(value: ClipboardEntry) -> Self { - Self { - entries: vec![value], - } - } -} - -impl From for ClipboardItem { - fn from(value: String) -> Self { - Self::from(ClipboardEntry::from(value)) - } -} - -impl From for ClipboardItem { - fn from(value: Image) -> Self { - Self::from(ClipboardEntry::from(value)) - } -} - -/// One of the editor's supported image formats (e.g. PNG, JPEG) - used when dealing with images in the clipboard -#[derive(Clone, Copy, Debug, Eq, PartialEq, EnumIter, Hash)] -pub enum ImageFormat { - // Sorted from most to least likely to be pasted into an editor, - // which matters when we iterate through them trying to see if - // clipboard content matches them. - /// .png - Png, - /// .jpeg or .jpg - Jpeg, - /// .webp - Webp, - /// .gif - Gif, - /// .svg - Svg, - /// .bmp - Bmp, - /// .tif or .tiff - Tiff, - /// .ico - Ico, - /// Netpbm image formats (.pbm, .ppm, .pgm). - Pnm, -} - -impl ImageFormat { - /// Returns the mime type for the ImageFormat - pub const fn mime_type(self) -> &'static str { - match self { - ImageFormat::Png => "image/png", - ImageFormat::Jpeg => "image/jpeg", - ImageFormat::Webp => "image/webp", - ImageFormat::Gif => "image/gif", - ImageFormat::Svg => "image/svg+xml", - ImageFormat::Bmp => "image/bmp", - ImageFormat::Tiff => "image/tiff", - ImageFormat::Ico => "image/ico", - ImageFormat::Pnm => "image/x-portable-anymap", - } - } - - /// Returns the file extension for this image format (without leading dot). - pub const fn extension(self) -> &'static str { - match self { - ImageFormat::Png => "png", - ImageFormat::Jpeg => "jpg", - ImageFormat::Webp => "webp", - ImageFormat::Gif => "gif", - ImageFormat::Svg => "svg", - ImageFormat::Bmp => "bmp", - ImageFormat::Tiff => "tiff", - ImageFormat::Ico => "ico", - ImageFormat::Pnm => "pnm", - } - } - - /// Returns the ImageFormat for the given mime type, including known aliases. - pub fn from_mime_type(mime_type: &str) -> Option { - use strum::IntoEnumIterator; - Self::iter() - .find(|format| format.mime_type() == mime_type) - .or_else(|| Self::from_mime_type_alias(mime_type)) - } - - /// Non-canonical mime types that some producers use in the wild. - /// Unlike `mime_type()` which returns the single canonical form, - /// these are legacy or shortened variants we still need to recognize. - fn from_mime_type_alias(mime_type: &str) -> Option { - match mime_type { - "image/jpg" => Some(Self::Jpeg), - "image/tif" => Some(Self::Tiff), - _ => None, - } - } -} - -/// An image, with a format and certain bytes -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Image { - /// The image format the bytes represent (e.g. PNG) - pub format: ImageFormat, - /// The raw image bytes - pub bytes: Vec, - /// The unique ID for the image - pub id: u64, -} - -pub(crate) fn decode_static_image( - bytes: &[u8], - format: image::ImageFormat, -) -> Result> { - let decoder = image::ImageReader::with_format(Cursor::new(bytes), format) - .into_decoder() - .context("creating image decoder")?; - decode_static_image_from_decoder(decoder) -} - -pub(crate) fn decode_static_image_from_decoder( - mut decoder: impl image::ImageDecoder, -) -> Result> { - let orientation = decoder - .orientation() - .context("reading decoder's orientation")?; - let mut image = DynamicImage::from_decoder(decoder).context("decoding image")?; - image.apply_orientation(orientation); - - let mut data = image.into_rgba8(); - for pixel in data.chunks_exact_mut(4) { - pixel.swap(0, 2); - } - - Ok(SmallVec::from_elem(Frame::new(data), 1)) -} - -impl Hash for Image { - fn hash(&self, state: &mut H) { - state.write_u64(self.id); - } -} - -impl Image { - /// An empty image containing no data - pub fn empty() -> Self { - Self::from_bytes(ImageFormat::Png, Vec::new()) - } - - /// Create an image from a format and bytes - pub fn from_bytes(format: ImageFormat, bytes: Vec) -> Self { - Self { - id: hash(&bytes), - format, - bytes, - } - } - - /// Get this image's ID - pub fn id(&self) -> u64 { - self.id - } - - /// Use the GPUI `use_asset` API to make this image renderable - pub fn use_render_image( - self: Arc, - window: &mut Window, - cx: &mut App, - ) -> Option> { - ImageSource::Image(self) - .use_data(None, window, cx) - .and_then(|result| result.ok()) - } - - /// Use the GPUI `get_asset` API to make this image renderable - pub fn get_render_image( - self: Arc, - window: &mut Window, - cx: &mut App, - ) -> Option> { - ImageSource::Image(self) - .get_data(None, window, cx) - .and_then(|result| result.ok()) - } - - /// Use the GPUI `remove_asset` API to drop this image, if possible. - pub fn remove_asset(self: Arc, cx: &mut App) { - ImageSource::Image(self).remove_asset(cx); - } - - /// Check whether this image is present in GPUI's asset cache (loading or - /// loaded), without fetching it. - #[cfg(any(test, feature = "test-support"))] - pub fn is_asset_cached(self: &Arc, cx: &App) -> bool { - ImageSource::Image(self.clone()).is_asset_cached(cx) - } - - /// Convert the clipboard image to an `ImageData` object. - pub fn to_image_data(&self, svg_renderer: SvgRenderer) -> Result> { - let frames = match self.format { - ImageFormat::Gif => { - let decoder = GifDecoder::new(Cursor::new(&self.bytes))?; - let mut frames = SmallVec::new(); - - for frame in decoder.into_frames() { - match frame { - Ok(mut frame) => { - // Convert from RGBA to BGRA. - for pixel in frame.buffer_mut().chunks_exact_mut(4) { - pixel.swap(0, 2); - } - frames.push(frame); - } - Err(err) => { - log::debug!("Skipping GIF frame due to decode error: {err}"); - } - } - } - - if frames.is_empty() { - anyhow::bail!("GIF could not be decoded: all frames failed"); - } - - frames - } - ImageFormat::Png => decode_static_image(&self.bytes, image::ImageFormat::Png)?, - ImageFormat::Jpeg => decode_static_image(&self.bytes, image::ImageFormat::Jpeg)?, - ImageFormat::Webp => decode_static_image(&self.bytes, image::ImageFormat::WebP)?, - ImageFormat::Bmp => decode_static_image(&self.bytes, image::ImageFormat::Bmp)?, - ImageFormat::Tiff => decode_static_image(&self.bytes, image::ImageFormat::Tiff)?, - ImageFormat::Ico => decode_static_image(&self.bytes, image::ImageFormat::Ico)?, - ImageFormat::Svg => { - return svg_renderer - .render_single_frame(&self.bytes, 1.0) - .map_err(Into::into); - } - ImageFormat::Pnm => decode_static_image(&self.bytes, image::ImageFormat::Pnm)?, - }; - - Ok(Arc::new(RenderImage::new(frames))) - } - - /// Get the format of the clipboard image - pub fn format(&self) -> ImageFormat { - self.format - } - - /// Get the raw bytes of the clipboard image - pub fn bytes(&self) -> &[u8] { - self.bytes.as_slice() - } -} - -/// A clipboard item that should be copied to the clipboard -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ClipboardString { - /// The text content. - pub text: String, - /// Optional metadata associated with this clipboard string. - pub metadata: Option, -} - -impl ClipboardString { - /// Create a new clipboard string with the given text - pub fn new(text: String) -> Self { - Self { - text, - metadata: None, - } - } - - /// Return a new clipboard item with the metadata replaced by the given metadata, - /// after serializing it as JSON. - pub fn with_json_metadata(mut self, metadata: T) -> Self { - self.metadata = Some(serde_json::to_string(&metadata).unwrap()); - self - } - - /// Get the text of the clipboard string - pub fn text(&self) -> &String { - &self.text - } - - /// Get the owned text of the clipboard string - pub fn into_text(self) -> String { - self.text - } - - /// Get the metadata of the clipboard string, formatted as JSON - pub fn metadata_json(&self) -> Option - where - T: for<'a> Deserialize<'a>, - { - self.metadata - .as_ref() - .and_then(|m| serde_json::from_str(m).ok()) - } - - #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))] - /// Compute a hash of the given text for clipboard change detection. - pub fn text_hash(text: &str) -> u64 { - let mut hasher = SeaHasher::new(); - text.hash(&mut hasher); - hasher.finish() - } -} - -impl From for ClipboardString { - fn from(value: String) -> Self { - Self { - text: value, - metadata: None, - } - } -} - -#[cfg(test)] -mod image_tests { - use super::*; - use std::sync::Arc; - - #[test] - fn test_image_to_image_data_applies_exif_orientation() { - let image = Image::from_bytes( - ImageFormat::Jpeg, - include_bytes!("../examples/image/exif-orientation-rotate-180.jpg").to_vec(), - ); - - let render_image = image.to_image_data(SvgRenderer::new(Arc::new(()))).unwrap(); - - assert_eq!(render_image.size(0), size(16.into(), 32.into())); - - let bytes = render_image.as_bytes(0).unwrap(); - assert_eq!(&bytes[..4], &[255, 255, 255, 255]); - assert_eq!(&bytes[(16 * 32 - 1) * 4..], &[0, 0, 0, 255]); - } - - #[test] - fn test_svg_image_to_image_data_converts_to_bgra() { - let image = Image::from_bytes( - ImageFormat::Svg, - br##" - -"## - .to_vec(), - ); - - let render_image = image.to_image_data(SvgRenderer::new(Arc::new(()))).unwrap(); - let bytes = render_image.as_bytes(0).unwrap(); - - for pixel in bytes.chunks_exact(4) { - assert_eq!(pixel, &[0xF8, 0xBD, 0x38, 0xFF]); - } - } -} - -#[cfg(all(test, any(target_os = "linux", target_os = "freebsd")))] -mod tests { - use super::*; - use std::collections::HashSet; - - #[test] - fn test_window_button_layout_parse_standard() { - let layout = WindowButtonLayout::parse("close,minimize:maximize").unwrap(); - assert_eq!( - layout.left, - [ - Some(WindowButton::Close), - Some(WindowButton::Minimize), - None - ] - ); - assert_eq!(layout.right, [Some(WindowButton::Maximize), None, None]); - } - - #[test] - fn test_window_button_layout_parse_right_only() { - let layout = WindowButtonLayout::parse("minimize,maximize,close").unwrap(); - assert_eq!(layout.left, [None, None, None]); - assert_eq!( - layout.right, - [ - Some(WindowButton::Minimize), - Some(WindowButton::Maximize), - Some(WindowButton::Close) - ] - ); - } - - #[test] - fn test_window_button_layout_parse_left_only() { - let layout = WindowButtonLayout::parse("close,minimize,maximize:").unwrap(); - assert_eq!( - layout.left, - [ - Some(WindowButton::Close), - Some(WindowButton::Minimize), - Some(WindowButton::Maximize) - ] - ); - assert_eq!(layout.right, [None, None, None]); - } - - #[test] - fn test_window_button_layout_parse_with_whitespace() { - let layout = WindowButtonLayout::parse(" close , minimize : maximize ").unwrap(); - assert_eq!( - layout.left, - [ - Some(WindowButton::Close), - Some(WindowButton::Minimize), - None - ] - ); - assert_eq!(layout.right, [Some(WindowButton::Maximize), None, None]); - } - - #[test] - fn test_window_button_layout_parse_empty() { - let layout = WindowButtonLayout::parse("").unwrap(); - assert_eq!(layout.left, [None, None, None]); - assert_eq!(layout.right, [None, None, None]); - } - - #[test] - fn test_window_button_layout_parse_intentionally_empty() { - let layout = WindowButtonLayout::parse(":").unwrap(); - assert_eq!(layout.left, [None, None, None]); - assert_eq!(layout.right, [None, None, None]); - } - - #[test] - fn test_window_button_layout_parse_invalid_buttons() { - let layout = WindowButtonLayout::parse("close,invalid,minimize:maximize,foo").unwrap(); - assert_eq!( - layout.left, - [ - Some(WindowButton::Close), - Some(WindowButton::Minimize), - None - ] - ); - assert_eq!(layout.right, [Some(WindowButton::Maximize), None, None]); - } - - #[test] - fn test_window_button_layout_parse_deduplicates_same_side_buttons() { - let layout = WindowButtonLayout::parse("close,close,minimize").unwrap(); - assert_eq!( - layout.right, - [ - Some(WindowButton::Close), - Some(WindowButton::Minimize), - None - ] - ); - assert_eq!(layout.format(), ":close,minimize"); - } - - #[test] - fn test_window_button_layout_parse_deduplicates_buttons_across_sides() { - let layout = WindowButtonLayout::parse("close:maximize,close,minimize").unwrap(); - assert_eq!(layout.left, [Some(WindowButton::Close), None, None]); - assert_eq!( - layout.right, - [ - Some(WindowButton::Maximize), - Some(WindowButton::Minimize), - None - ] - ); - - let button_ids: Vec<_> = layout - .left - .iter() - .chain(layout.right.iter()) - .flatten() - .map(WindowButton::id) - .collect(); - let unique_button_ids = button_ids.iter().copied().collect::>(); - assert_eq!(unique_button_ids.len(), button_ids.len()); - assert_eq!(layout.format(), "close:maximize,minimize"); - } - - #[test] - fn test_window_button_layout_parse_gnome_style() { - let layout = WindowButtonLayout::parse("close").unwrap(); - assert_eq!(layout.left, [None, None, None]); - assert_eq!(layout.right, [Some(WindowButton::Close), None, None]); - } - - #[test] - fn test_window_button_layout_parse_elementary_style() { - let layout = WindowButtonLayout::parse("close:maximize").unwrap(); - assert_eq!(layout.left, [Some(WindowButton::Close), None, None]); - assert_eq!(layout.right, [Some(WindowButton::Maximize), None, None]); - } - - #[test] - fn test_window_button_layout_round_trip() { - let cases = [ - "close:minimize,maximize", - "minimize,maximize,close:", - ":close", - "close:", - "close:maximize", - ":", - ]; - - for case in cases { - let layout = WindowButtonLayout::parse(case).unwrap(); - assert_eq!(layout.format(), case, "Round-trip failed for: {}", case); - } - } - - #[test] - fn test_window_button_layout_linux_default() { - let layout = WindowButtonLayout::linux_default(); - assert_eq!(layout.left, [None, None, None]); - assert_eq!( - layout.right, - [ - Some(WindowButton::Minimize), - Some(WindowButton::Maximize), - Some(WindowButton::Close) - ] - ); - - let round_tripped = WindowButtonLayout::parse(&layout.format()).unwrap(); - assert_eq!(round_tripped, layout); - } - - #[test] - fn test_window_button_layout_parse_all_invalid() { - assert!(WindowButtonLayout::parse("asdfghjkl").is_err()); - } -} diff --git a/crates/gpui_pre_apple/vendor/gpui/src/scene.rs b/crates/gpui_pre_apple/vendor/gpui/src/scene.rs deleted file mode 100644 index 46c1acf5..00000000 --- a/crates/gpui_pre_apple/vendor/gpui/src/scene.rs +++ /dev/null @@ -1,1022 +0,0 @@ -// todo("windows"): remove -#![cfg_attr(windows, allow(dead_code))] - -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -use crate::{ - bounds_tree::BoundsTree, point, AtlasTextureId, AtlasTile, Background, Bounds, ContentMask, - Corners, Edges, Hsla, Pixels, Point, Radians, ScaledPixels, Size, -}; -use std::{ - fmt::Debug, - iter::Peekable, - ops::{Add, Range, Sub}, - slice, -}; - -#[allow(non_camel_case_types, unused)] -#[expect(missing_docs)] -pub type PathVertex_ScaledPixels = PathVertex; - -#[expect(missing_docs)] -pub type DrawOrder = u32; - -/// A boolean stored as a `u32` so that GPU-facing structs contain no -/// compiler-inserted padding bytes, which would be undefined behavior to -/// reinterpret as `&[u8]` when writing instance buffers. Guaranteed to be -/// `0` or `1` by construction; shaders read it as a `u32`/`uint`. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(transparent)] -pub struct PaddedBool32(u32); - -impl From for PaddedBool32 { - fn from(value: bool) -> Self { - PaddedBool32(value as u32) - } -} - -#[derive(Default)] -#[expect(missing_docs)] -pub struct Scene { - /// Immutable rounded clip nodes referenced by primitive masks. - pub rounded_clips: Vec>, - clip_indices: std::collections::HashMap<[u32; 13], u32>, - pub(crate) paint_operations: Vec, - primitive_bounds: BoundsTree, - layer_stack: Vec, - pub shadows: Vec, - pub quads: Vec, - pub paths: Vec>, - pub underlines: Vec, - pub monochrome_sprites: Vec, - pub subpixel_sprites: Vec, - pub polychrome_sprites: Vec, - pub surfaces: Vec, -} - -#[expect(missing_docs)] -impl Scene { - pub fn clear(&mut self) { - self.rounded_clips.clear(); - self.clip_indices.clear(); - self.paint_operations.clear(); - self.primitive_bounds.clear(); - self.layer_stack.clear(); - self.paths.clear(); - self.shadows.clear(); - self.quads.clear(); - self.underlines.clear(); - self.monochrome_sprites.clear(); - self.subpixel_sprites.clear(); - self.polychrome_sprites.clear(); - self.surfaces.clear(); - } - - pub fn len(&self) -> usize { - self.paint_operations.len() - } - - /// Intern a clip node for this frame. Identical ancestor chains share nodes. - pub fn insert_clip(&mut self, clip: crate::RoundedClip) -> u32 { - assert!( - clip.parent as usize <= self.rounded_clips.len(), - "clip parent must already exist in this scene" - ); - let key = [ - clip.bounds.origin.x.0.to_bits(), - clip.bounds.origin.y.0.to_bits(), - clip.bounds.size.width.0.to_bits(), - clip.bounds.size.height.0.to_bits(), - clip.radii_x.top_left.0.to_bits(), - clip.radii_x.top_right.0.to_bits(), - clip.radii_x.bottom_right.0.to_bits(), - clip.radii_x.bottom_left.0.to_bits(), - clip.radii_y.top_left.0.to_bits(), - clip.radii_y.top_right.0.to_bits(), - clip.radii_y.bottom_right.0.to_bits(), - clip.radii_y.bottom_left.0.to_bits(), - clip.parent, - ]; - if let Some(index) = self.clip_indices.get(&key) { - return *index; - } - let index = u32::try_from(self.rounded_clips.len()).expect("scene clip index overflow") + 1; - self.rounded_clips.push(clip); - self.clip_indices.insert(key, index); - index - } - - fn import_clip(&mut self, mut index: u32, previous: &Scene) -> u32 { - let mut chain = Vec::new(); - while index != 0 { - let clip = previous.rounded_clips[index as usize - 1]; - index = clip.parent; - chain.push(clip); - } - let mut parent = 0; - for mut clip in chain.into_iter().rev() { - clip.parent = parent; - parent = self.insert_clip(clip); - } - parent - } - - pub fn push_layer(&mut self, bounds: Bounds) { - let order = self.primitive_bounds.insert(bounds); - self.layer_stack.push(order); - self.paint_operations - .push(PaintOperation::StartLayer(bounds)); - } - - pub fn pop_layer(&mut self) { - self.layer_stack.pop(); - self.paint_operations.push(PaintOperation::EndLayer); - } - - pub fn insert_primitive(&mut self, primitive: impl Into) { - let mut primitive = primitive.into(); - let clipped_bounds = primitive - .bounds() - .intersect(&primitive.content_mask().bounds); - - if clipped_bounds.is_empty() { - return; - } - - let order = self - .layer_stack - .last() - .copied() - .unwrap_or_else(|| self.primitive_bounds.insert(clipped_bounds)); - match &mut primitive { - Primitive::Shadow(shadow) => { - shadow.order = order; - self.shadows.push(*shadow); - } - Primitive::Quad(quad) => { - quad.order = order; - self.quads.push(*quad); - } - Primitive::Path(path) => { - path.order = order; - path.id = PathId(self.paths.len()); - self.paths.push(path.clone()); - } - Primitive::Underline(underline) => { - underline.order = order; - self.underlines.push(*underline); - } - Primitive::MonochromeSprite(sprite) => { - sprite.order = order; - self.monochrome_sprites.push(*sprite); - } - Primitive::SubpixelSprite(sprite) => { - sprite.order = order; - self.subpixel_sprites.push(*sprite); - } - Primitive::PolychromeSprite(sprite) => { - sprite.order = order; - self.polychrome_sprites.push(*sprite); - } - Primitive::Surface(surface) => { - surface.order = order; - self.surfaces.push(surface.clone()); - } - } - self.paint_operations - .push(PaintOperation::Primitive(primitive)); - } - - pub fn replay(&mut self, range: Range, prev_scene: &Scene) { - let mut clip_remapping = std::collections::HashMap::new(); - for operation in &prev_scene.paint_operations[range] { - match operation { - PaintOperation::Primitive(primitive) => { - let mut primitive = primitive.clone(); - let mask = primitive.content_mask_mut(); - if mask.clip_index != 0 { - mask.clip_index = *clip_remapping - .entry(mask.clip_index) - .or_insert_with(|| self.import_clip(mask.clip_index, prev_scene)); - } - self.insert_primitive(primitive); - } - PaintOperation::StartLayer(bounds) => self.push_layer(*bounds), - PaintOperation::EndLayer => self.pop_layer(), - } - } - } - - pub fn finish(&mut self) { - self.shadows.sort_by_key(|shadow| shadow.order); - self.quads.sort_by_key(|quad| quad.order); - self.paths.sort_by_key(|path| path.order); - self.underlines.sort_by_key(|underline| underline.order); - self.monochrome_sprites - .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id)); - self.subpixel_sprites - .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id)); - self.polychrome_sprites - .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id)); - self.surfaces.sort_by_key(|surface| surface.order); - } - - #[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) - )] - pub fn batches(&self) -> impl Iterator + '_ { - BatchIterator { - shadows_start: 0, - shadows_iter: self.shadows.iter().peekable(), - quads_start: 0, - quads_iter: self.quads.iter().peekable(), - paths_start: 0, - paths_iter: self.paths.iter().peekable(), - underlines_start: 0, - underlines_iter: self.underlines.iter().peekable(), - monochrome_sprites_start: 0, - monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(), - subpixel_sprites_start: 0, - subpixel_sprites_iter: self.subpixel_sprites.iter().peekable(), - polychrome_sprites_start: 0, - polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(), - surfaces_start: 0, - surfaces_iter: self.surfaces.iter().peekable(), - } - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)] -#[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) -)] -pub(crate) enum PrimitiveKind { - Shadow, - #[default] - Quad, - Path, - Underline, - MonochromeSprite, - SubpixelSprite, - PolychromeSprite, - Surface, -} - -pub(crate) enum PaintOperation { - Primitive(Primitive), - StartLayer(Bounds), - EndLayer, -} - -#[derive(Clone)] -#[expect(missing_docs)] -pub enum Primitive { - Shadow(Shadow), - Quad(Quad), - Path(Path), - Underline(Underline), - MonochromeSprite(MonochromeSprite), - SubpixelSprite(SubpixelSprite), - PolychromeSprite(PolychromeSprite), - Surface(PaintSurface), -} - -#[expect(missing_docs)] -impl Primitive { - pub fn bounds(&self) -> &Bounds { - 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::SubpixelSprite(sprite) => &sprite.bounds, - Primitive::PolychromeSprite(sprite) => &sprite.bounds, - Primitive::Surface(surface) => &surface.bounds, - } - } - - pub fn content_mask(&self) -> &ContentMask { - 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::SubpixelSprite(sprite) => &sprite.content_mask, - Primitive::PolychromeSprite(sprite) => &sprite.content_mask, - Primitive::Surface(surface) => &surface.content_mask, - } - } - - pub fn content_mask_mut(&mut self) -> &mut ContentMask { - match self { - Primitive::Shadow(shadow) => &mut shadow.content_mask, - Primitive::Quad(quad) => &mut quad.content_mask, - Primitive::Path(path) => &mut path.content_mask, - Primitive::Underline(underline) => &mut underline.content_mask, - Primitive::MonochromeSprite(sprite) => &mut sprite.content_mask, - Primitive::SubpixelSprite(sprite) => &mut sprite.content_mask, - Primitive::PolychromeSprite(sprite) => &mut sprite.content_mask, - Primitive::Surface(surface) => &mut surface.content_mask, - } - } -} - -#[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) -)] -struct BatchIterator<'a> { - shadows_start: usize, - shadows_iter: Peekable>, - quads_start: usize, - quads_iter: Peekable>, - paths_start: usize, - paths_iter: Peekable>>, - underlines_start: usize, - underlines_iter: Peekable>, - monochrome_sprites_start: usize, - monochrome_sprites_iter: Peekable>, - subpixel_sprites_start: usize, - subpixel_sprites_iter: Peekable>, - polychrome_sprites_start: usize, - polychrome_sprites_iter: Peekable>, - surfaces_start: usize, - surfaces_iter: Peekable>, -} - -impl<'a> Iterator for BatchIterator<'a> { - type Item = PrimitiveBatch; - - fn next(&mut self) -> Option { - 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.subpixel_sprites_iter.peek().map(|s| s.order), - PrimitiveKind::SubpixelSprite, - ), - ( - 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_and_kind) = if first.0.is_some() { - (first.1, (second.0.unwrap_or(u32::MAX), second.1)) - } 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, batch_kind) < max_order_and_kind) - .is_some() - { - shadows_end += 1; - } - self.shadows_start = shadows_end; - Some(PrimitiveBatch::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, batch_kind) < max_order_and_kind) - .is_some() - { - quads_end += 1; - } - self.quads_start = quads_end; - Some(PrimitiveBatch::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, batch_kind) < max_order_and_kind) - .is_some() - { - paths_end += 1; - } - self.paths_start = paths_end; - Some(PrimitiveBatch::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, batch_kind) < max_order_and_kind) - .is_some() - { - underlines_end += 1; - } - self.underlines_start = underlines_end; - Some(PrimitiveBatch::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, batch_kind) < max_order_and_kind - && sprite.tile.texture_id == texture_id - }) - .is_some() - { - sprites_end += 1; - } - self.monochrome_sprites_start = sprites_end; - Some(PrimitiveBatch::MonochromeSprites { - texture_id, - range: sprites_start..sprites_end, - }) - } - PrimitiveKind::SubpixelSprite => { - let texture_id = self.subpixel_sprites_iter.peek().unwrap().tile.texture_id; - let sprites_start = self.subpixel_sprites_start; - let mut sprites_end = sprites_start + 1; - self.subpixel_sprites_iter.next(); - while self - .subpixel_sprites_iter - .next_if(|sprite| { - (sprite.order, batch_kind) < max_order_and_kind - && sprite.tile.texture_id == texture_id - }) - .is_some() - { - sprites_end += 1; - } - self.subpixel_sprites_start = sprites_end; - Some(PrimitiveBatch::SubpixelSprites { - texture_id, - range: 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 = sprites_start + 1; - self.polychrome_sprites_iter.next(); - while self - .polychrome_sprites_iter - .next_if(|sprite| { - (sprite.order, batch_kind) < max_order_and_kind - && sprite.tile.texture_id == texture_id - }) - .is_some() - { - sprites_end += 1; - } - self.polychrome_sprites_start = sprites_end; - Some(PrimitiveBatch::PolychromeSprites { - texture_id, - range: 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, batch_kind) < max_order_and_kind) - .is_some() - { - surfaces_end += 1; - } - self.surfaces_start = surfaces_end; - Some(PrimitiveBatch::Surfaces(surfaces_start..surfaces_end)) - } - } - } -} - -#[derive(Debug)] -#[cfg_attr( - all( - any(target_os = "linux", target_os = "freebsd"), - not(any(feature = "x11", feature = "wayland")) - ), - allow(dead_code) -)] -#[allow(missing_docs)] -pub enum PrimitiveBatch { - Shadows(Range), - Quads(Range), - Paths(Range), - Underlines(Range), - MonochromeSprites { - texture_id: AtlasTextureId, - range: Range, - }, - #[cfg_attr(target_os = "macos", allow(dead_code))] - SubpixelSprites { - texture_id: AtlasTextureId, - range: Range, - }, - PolychromeSprites { - texture_id: AtlasTextureId, - range: Range, - }, - Surfaces(Range), -} - -impl PrimitiveBatch { - #[expect(missing_docs)] - pub fn label(&self) -> String { - match self { - Self::Shadows(range) => format!("shadows ({})", range.len()), - Self::Quads(range) => format!("quads ({})", range.len()), - Self::Paths(range) => format!("paths ({})", range.len()), - Self::Underlines(range) => format!("underlines ({})", range.len()), - Self::MonochromeSprites { texture_id, range } => { - format!( - "monochrome sprites ({}) on atlas {}", - range.len(), - texture_id.index - ) - } - Self::SubpixelSprites { texture_id, range } => { - format!( - "subpixel sprites ({}) on atlas {}", - range.len(), - texture_id.index - ) - } - Self::PolychromeSprites { texture_id, range } => { - format!( - "polychrome sprites ({}) on atlas {}", - range.len(), - texture_id.index - ) - } - Self::Surfaces(range) => format!("surfaces ({})", range.len()), - } - } -} - -#[derive(Default, Debug, Copy, Clone)] -#[repr(C)] -#[expect(missing_docs)] -pub struct Quad { - pub order: DrawOrder, - pub border_style: BorderStyle, - pub bounds: Bounds, - pub content_mask: ContentMask, - pub background: Background, - pub border_color: Hsla, - pub corner_radii: Corners, - pub border_widths: Edges, -} - -impl From for Primitive { - fn from(quad: Quad) -> Self { - Primitive::Quad(quad) - } -} - -#[derive(Debug, Copy, Clone)] -#[repr(C)] -#[expect(missing_docs)] -pub struct Underline { - pub order: DrawOrder, - pub pad: u32, // align to 8 bytes - pub bounds: Bounds, - pub content_mask: ContentMask, - pub color: Hsla, - pub thickness: ScaledPixels, - pub wavy: PaddedBool32, -} - -impl From for Primitive { - fn from(underline: Underline) -> Self { - Primitive::Underline(underline) - } -} - -#[derive(Debug, Copy, Clone)] -#[repr(C)] -#[expect(missing_docs)] -pub struct Shadow { - pub order: DrawOrder, - pub blur_radius: ScaledPixels, - pub bounds: Bounds, - pub corner_radii: Corners, - pub content_mask: ContentMask, - pub color: Hsla, - pub element_bounds: Bounds, - pub element_corner_radii: Corners, - /// 0 = drop shadow (rendered outside the element), 1 = inset shadow (rendered inside). - pub inset: u32, - pub pad: u32, // align to 8 bytes -} - -impl From for Primitive { - fn from(shadow: Shadow) -> Self { - Primitive::Shadow(shadow) - } -} - -/// The style of a border. -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] -#[repr(C)] -pub enum BorderStyle { - /// A solid border. - #[default] - Solid = 0, - /// A dashed border. - Dashed = 1, -} - -/// A data type representing a 2 dimensional transformation that can be applied to an element. -#[derive(Debug, Clone, Copy, PartialEq)] -#[repr(C)] -pub struct TransformationMatrix { - /// 2x2 matrix containing rotation and scale, - /// stored row-major - pub rotation_scale: [[f32; 2]; 2], - /// translation vector - pub translation: [f32; 2], -} - -impl Eq for TransformationMatrix {} - -impl TransformationMatrix { - /// The unit matrix, has no effect. - pub fn unit() -> Self { - Self { - rotation_scale: [[1.0, 0.0], [0.0, 1.0]], - translation: [0.0, 0.0], - } - } - - /// Move the origin by a given point - pub fn translate(mut self, point: Point) -> Self { - self.compose(Self { - rotation_scale: [[1.0, 0.0], [0.0, 1.0]], - translation: [point.x.0, point.y.0], - }) - } - - /// Clockwise rotation in radians around the origin - pub fn rotate(self, angle: Radians) -> Self { - self.compose(Self { - rotation_scale: [ - [angle.0.cos(), -angle.0.sin()], - [angle.0.sin(), angle.0.cos()], - ], - translation: [0.0, 0.0], - }) - } - - /// Scale around the origin - pub fn scale(self, size: Size) -> Self { - self.compose(Self { - rotation_scale: [[size.width, 0.0], [0.0, size.height]], - translation: [0.0, 0.0], - }) - } - - /// Perform matrix multiplication with another transformation - /// to produce a new transformation that is the result of - /// applying both transformations: first, `other`, then `self`. - #[inline] - pub fn compose(self, other: TransformationMatrix) -> TransformationMatrix { - if other == Self::unit() { - return self; - } - // Perform matrix multiplication - TransformationMatrix { - rotation_scale: [ - [ - self.rotation_scale[0][0] * other.rotation_scale[0][0] - + self.rotation_scale[0][1] * other.rotation_scale[1][0], - self.rotation_scale[0][0] * other.rotation_scale[0][1] - + self.rotation_scale[0][1] * other.rotation_scale[1][1], - ], - [ - self.rotation_scale[1][0] * other.rotation_scale[0][0] - + self.rotation_scale[1][1] * other.rotation_scale[1][0], - self.rotation_scale[1][0] * other.rotation_scale[0][1] - + self.rotation_scale[1][1] * other.rotation_scale[1][1], - ], - ], - translation: [ - self.translation[0] - + self.rotation_scale[0][0] * other.translation[0] - + self.rotation_scale[0][1] * other.translation[1], - self.translation[1] - + self.rotation_scale[1][0] * other.translation[0] - + self.rotation_scale[1][1] * other.translation[1], - ], - } - } - - /// Apply transformation to a point, mainly useful for debugging - pub fn apply(&self, point: Point) -> Point { - let input = [point.x.0, point.y.0]; - let mut output = self.translation; - for (i, output_cell) in output.iter_mut().enumerate() { - for (k, input_cell) in input.iter().enumerate() { - *output_cell += self.rotation_scale[i][k] * *input_cell; - } - } - Point::new(output[0].into(), output[1].into()) - } -} - -impl Default for TransformationMatrix { - fn default() -> Self { - Self::unit() - } -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -#[expect(missing_docs)] -pub struct MonochromeSprite { - pub order: DrawOrder, - pub pad: u32, - pub bounds: Bounds, - pub content_mask: ContentMask, - pub color: Hsla, - pub tile: AtlasTile, - pub transformation: TransformationMatrix, -} - -impl From for Primitive { - fn from(sprite: MonochromeSprite) -> Self { - Primitive::MonochromeSprite(sprite) - } -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -#[expect(missing_docs)] -pub struct SubpixelSprite { - pub order: DrawOrder, - pub pad: u32, // align to 8 bytes - pub bounds: Bounds, - pub content_mask: ContentMask, - pub color: Hsla, - pub tile: AtlasTile, - pub transformation: TransformationMatrix, -} - -impl From for Primitive { - fn from(sprite: SubpixelSprite) -> Self { - Primitive::SubpixelSprite(sprite) - } -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -#[expect(missing_docs)] -pub struct PolychromeSprite { - pub order: DrawOrder, - pub pad: u32, - pub grayscale: PaddedBool32, - pub opacity: f32, - pub bounds: Bounds, - pub content_mask: ContentMask, - pub corner_radii: Corners, - pub tile: AtlasTile, -} - -impl From for Primitive { - fn from(sprite: PolychromeSprite) -> Self { - Primitive::PolychromeSprite(sprite) - } -} - -#[derive(Clone, Debug)] -#[allow(missing_docs)] -pub struct PaintSurface { - pub order: DrawOrder, - pub bounds: Bounds, - pub content_mask: ContentMask, - #[cfg(target_os = "macos")] - pub image_buffer: core_video::pixel_buffer::CVPixelBuffer, -} - -impl From for Primitive { - fn from(surface: PaintSurface) -> Self { - Primitive::Surface(surface) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[expect(missing_docs)] -pub struct PathId(pub usize); - -/// A line made up of a series of vertices and control points. -#[derive(Clone, Debug)] -#[expect(missing_docs)] -pub struct Path { - pub id: PathId, - pub order: DrawOrder, - pub bounds: Bounds

, - pub content_mask: ContentMask

, - pub vertices: Vec>, - pub color: Background, - start: Point

, - current: Point

, - contour_count: usize, -} - -impl Path { - /// Create a new path with the given starting point. - pub fn new(start: Point) -> Self { - Self { - id: PathId(0), - order: DrawOrder::default(), - vertices: Vec::new(), - start, - current: start, - bounds: Bounds { - origin: start, - size: Default::default(), - }, - content_mask: Default::default(), - color: Default::default(), - contour_count: 0, - } - } - - /// Scale this path by the given factor. - pub fn scale(&self, factor: f32) -> Path { - 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, - } - } - - /// Move the start, current point to the given point. - pub fn move_to(&mut self, to: Point) { - self.contour_count += 1; - self.start = to; - self.current = to; - } - - /// Draw a straight line from the current point to the given point. - pub fn line_to(&mut self, to: Point) { - 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; - } - - /// Draw a curve from the current point to the given point, using the given control point. - pub fn curve_to(&mut self, to: Point, ctrl: Point) { - 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; - } - - /// Push a triangle to the Path. - pub fn push_triangle( - &mut self, - xy: (Point, Point, Point), - st: (Point, Point, Point), - ) { - 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 Path -where - T: Clone + Debug + Default + PartialEq + PartialOrd + Add + Sub, -{ - #[allow(unused)] - #[expect(missing_docs)] - pub fn clipped_bounds(&self) -> Bounds { - self.bounds.intersect(&self.content_mask.bounds) - } -} - -impl From> for Primitive { - fn from(path: Path) -> Self { - Primitive::Path(path) - } -} - -#[derive(Clone, Debug)] -#[repr(C)] -#[expect(missing_docs)] -pub struct PathVertex { - pub xy_position: Point

, - pub st_position: Point, - pub content_mask: ContentMask

, -} - -#[expect(missing_docs)] -impl PathVertex { - pub fn scale(&self, factor: f32) -> PathVertex { - PathVertex { - xy_position: self.xy_position.scale(factor), - st_position: self.st_position, - content_mask: self.content_mask.scale(factor), - } - } -} diff --git a/crates/gpui_pre_apple/vendor/gpui/src/window.rs b/crates/gpui_pre_apple/vendor/gpui/src/window.rs deleted file mode 100644 index 86ef6516..00000000 --- a/crates/gpui_pre_apple/vendor/gpui/src/window.rs +++ /dev/null @@ -1,8373 +0,0 @@ -#[cfg(feature = "profiler")] -use crate::profiler; -#[cfg(feature = "profiler")] -use crate::DebugFrameOverlayMode; -#[cfg(any(feature = "inspector", debug_assertions))] -use crate::Inspector; -use crate::{ - point, prelude::*, px, rems, size, transparent_black, Action, AnyDrag, AnyElement, - AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset, AsyncWindowContext, - AtlasTile, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Capslock, Context, - Corners, CursorHideMode, CursorStyle, Decorations, DevicePixels, DispatchActionListener, - DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, - FileDropEvent, FontId, Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler, IsZero, - KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId, - LineLayoutIndex, Modifiers, ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, - MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, - PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, Priority, PromptButton, - PromptLevel, Quad, Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, - Replay, ResizeEdge, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style, - SubpixelSprite, SubscriberSet, Subscription, SystemWindowTab, SystemWindowTabController, - TabStopMap, TaffyLayoutEngine, Task, TextInputConfiguration, TextInputStateChange, - TextRenderingMode, TextStyle, TextStyleRefinement, ThermalState, TransformationMatrix, - Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance, WindowBounds, - WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem, - SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS_X, SUBPIXEL_VARIANTS_Y, -}; - -use crate::gestures::{GestureTuning, RecognizedTouchGesture, TouchGestureRecognizer}; -use crate::interactive::TouchEvent; -use anyhow::{anyhow, Context as _, Result}; -use collections::{FxHashMap, FxHashSet}; -#[cfg(target_os = "macos")] -use core_video::pixel_buffer::CVPixelBuffer; -use derive_more::{Deref, DerefMut}; -use futures::channel::oneshot; -use futures::FutureExt; -use gpui_util::post_inc; -use gpui_util::{measure, ResultExt}; -use itertools::FoldWhile::{Continue, Done}; -use itertools::Itertools; -use parking_lot::RwLock; -use raw_window_handle::{HandleError, HasDisplayHandle, HasWindowHandle}; -use refineable::Refineable; -use scheduler::Instant; -use slotmap::SlotMap; -use smallvec::SmallVec; -use std::{ - any::{Any, TypeId}, - borrow::Cow, - cell::{Cell, RefCell}, - cmp, - fmt::{Debug, Display}, - hash::{Hash, Hasher}, - marker::PhantomData, - mem, - ops::{DerefMut, Range}, - rc::Rc, - sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}, - Arc, Weak, - }, - time::Duration, -}; -use uuid::Uuid; - -pub(crate) mod a11y; -mod prompts; - -pub use a11y::A11ySubtreeBuilder; - -use self::a11y::A11y; -#[cfg(not(target_family = "wasm"))] -use self::a11y::ROOT_NODE_ID; -use crate::util::{ - atomic_incr_if_not_zero, ceil_to_device_pixel, floor_to_device_pixel, round_half_toward_zero, - round_half_toward_zero_f64, round_stroke_to_device_pixel, round_to_device_pixel, -}; -pub use prompts::*; - -/// Default window size used when no explicit size is provided. -pub const DEFAULT_WINDOW_SIZE: Size = size(px(1536.), px(1095.)); - -/// A 6:5 aspect ratio minimum window size to be used for functional, -/// additional-to-main-Zed windows, like the settings and rules library windows. -pub const DEFAULT_ADDITIONAL_WINDOW_SIZE: Size = Size { - width: Pixels(900.), - height: Pixels(750.), -}; - -/// Represents the two different phases when dispatching events. -#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] -pub enum DispatchPhase { - /// After the capture phase comes the bubble phase, in which mouse event listeners are - /// invoked front to back and keyboard event listeners are invoked from the focused element - /// to the root of the element tree. This is the phase you'll most commonly want to use when - /// registering event listeners. - #[default] - Bubble, - /// During the initial capture phase, mouse event listeners are invoked back to front, and keyboard - /// listeners are invoked from the root of the tree downward toward the focused element. This phase - /// is used for special purposes such as clearing the "pressed" state for click events. If - /// you stop event propagation during this phase, you need to know what you're doing. Handlers - /// outside of the immediate region may rely on detecting non-local events during this phase. - Capture, -} - -impl DispatchPhase { - /// Returns true if this represents the "bubble" phase. - #[inline] - pub fn bubble(self) -> bool { - self == DispatchPhase::Bubble - } - - /// Returns true if this represents the "capture" phase. - #[inline] - pub fn capture(self) -> bool { - self == DispatchPhase::Capture - } -} - -struct WindowInvalidatorInner { - #[cfg(feature = "profiler")] - pub window_id: WindowId, - pub dirty: bool, - pub draw_phase: DrawPhase, - pub dirty_views: FxHashSet, - pub update_count: usize, - #[cfg(feature = "profiler")] - pub frame_dirty: FrameDirtyAccumulator, - pub platform_waker: Option>, -} - -/// Per-frame invalidation bookkeeping, drained at draw time and emitted to the -/// frame profiler. Tracks when the current frame first became dirty and how -/// many invalidations were coalesced into it, whenever the profiler is -/// compiled in. Retention of the resulting per-frame records is what -/// `profiler::trace_enabled()` controls, not this measurement. -#[cfg(feature = "profiler")] -#[derive(Default)] -struct FrameDirtyAccumulator { - dirty_at: Option, - invalidations: u64, -} - -#[derive(Clone)] -pub(crate) struct WindowInvalidator { - inner: Rc>, -} - -impl WindowInvalidator { - pub fn new(#[allow(unused_variables)] window_id: WindowId) -> Self { - WindowInvalidator { - inner: Rc::new(RefCell::new(WindowInvalidatorInner { - #[cfg(feature = "profiler")] - window_id, - dirty: true, - draw_phase: DrawPhase::None, - dirty_views: FxHashSet::default(), - update_count: 0, - #[cfg(feature = "profiler")] - frame_dirty: FrameDirtyAccumulator::default(), - platform_waker: None, - })), - } - } - - pub fn invalidate_view(&self, entity: EntityId, cx: &mut App) -> bool { - let mut inner = self.inner.borrow_mut(); - inner.update_count += 1; - inner.dirty_views.insert(entity); - if inner.draw_phase == DrawPhase::None { - #[cfg(feature = "profiler")] - let dirty_at = Self::record_frame_dirty(&mut inner); - let became_dirty = !inner.dirty; - inner.dirty = true; - let waker = became_dirty.then(|| inner.platform_waker.clone()).flatten(); - #[cfg(feature = "profiler")] - let window_id = inner.window_id; - drop(inner); - #[cfg(feature = "profiler")] - if became_dirty { - profiler::journal::record_frame_pending(window_id, dirty_at); - } - cx.push_effect(Effect::Notify { emitter: entity }); - if let Some(waker) = waker { - waker(); - } - true - } else { - false - } - } - - pub fn is_dirty(&self) -> bool { - self.inner.borrow().dirty - } - - pub fn set_dirty(&self, dirty: bool) { - let mut inner = self.inner.borrow_mut(); - let became_dirty = dirty && !inner.dirty; - inner.dirty = dirty; - if dirty { - inner.update_count += 1; - } - #[cfg(feature = "profiler")] - let dirty_at = dirty.then(|| Self::record_frame_dirty(&mut inner)); - let waker = became_dirty.then(|| inner.platform_waker.clone()).flatten(); - #[cfg(feature = "profiler")] - let window_id = inner.window_id; - drop(inner); - #[cfg(feature = "profiler")] - if became_dirty && let Some(dirty_at) = dirty_at { - profiler::journal::record_frame_pending(window_id, dirty_at); - } - if let Some(waker) = waker { - waker(); - } - } - - pub fn set_platform_waker(&self, waker: Option>) { - let mut inner = self.inner.borrow_mut(); - inner.platform_waker = waker; - let waker = inner.dirty.then(|| inner.platform_waker.clone()).flatten(); - drop(inner); - if let Some(waker) = waker { - waker(); - } - } - - /// Wakes the platform's frame-request source so a frame request is - /// delivered even if the platform stops requesting frames for idle - /// windows. No-op on platforms without a frame waker. - pub fn wake_platform(&self) { - let waker = self.inner.borrow().platform_waker.clone(); - if let Some(waker) = waker { - waker(); - } - } - - pub fn set_phase(&self, phase: DrawPhase) { - self.inner.borrow_mut().draw_phase = phase - } - - pub fn update_count(&self) -> usize { - self.inner.borrow().update_count - } - - #[cfg(feature = "profiler")] - fn record_frame_dirty(inner: &mut WindowInvalidatorInner) -> Instant { - let dirty_at = *inner.frame_dirty.dirty_at.get_or_insert_with(Instant::now); - inner.frame_dirty.invalidations += 1; - dirty_at - } - - #[cfg(feature = "profiler")] - fn take_frame_dirty(&self) -> FrameDirtyAccumulator { - mem::take(&mut self.inner.borrow_mut().frame_dirty) - } - - pub fn take_views(&self) -> FxHashSet { - mem::take(&mut self.inner.borrow_mut().dirty_views) - } - - pub fn replace_views(&self, views: FxHashSet) { - self.inner.borrow_mut().dirty_views = views; - } - - pub fn not_drawing(&self) -> bool { - self.inner.borrow().draw_phase == DrawPhase::None - } - - #[track_caller] - pub fn debug_assert_paint(&self) { - debug_assert!( - matches!(self.inner.borrow().draw_phase, DrawPhase::Paint), - "this method can only be called during paint" - ); - } - - #[track_caller] - pub fn debug_assert_prepaint(&self) { - debug_assert!( - matches!(self.inner.borrow().draw_phase, DrawPhase::Prepaint), - "this method can only be called during request_layout, or prepaint" - ); - } - - #[track_caller] - pub fn debug_assert_paint_or_prepaint(&self) { - debug_assert!( - matches!( - self.inner.borrow().draw_phase, - DrawPhase::Paint | DrawPhase::Prepaint - ), - "this method can only be called during request_layout, prepaint, or paint" - ); - } -} - -type AnyObserver = Box bool + 'static>; - -pub(crate) type AnyWindowFocusListener = - Box bool + 'static>; - -pub(crate) struct WindowFocusEvent { - pub(crate) previous_focus_path: SmallVec<[FocusId; 8]>, - pub(crate) current_focus_path: SmallVec<[FocusId; 8]>, -} - -impl WindowFocusEvent { - pub fn is_focus_in(&self, focus_id: FocusId) -> bool { - !self.previous_focus_path.contains(&focus_id) && self.current_focus_path.contains(&focus_id) - } - - pub fn is_focus_out(&self, focus_id: FocusId) -> bool { - self.previous_focus_path.contains(&focus_id) && !self.current_focus_path.contains(&focus_id) - } -} - -/// This is provided when subscribing for `Context::on_focus_out` events. -pub struct FocusOutEvent { - /// A weak focus handle representing what was blurred. - pub blurred: WeakFocusHandle, -} - -slotmap::new_key_type! { - /// A globally unique identifier for a focusable element. - pub struct FocusId; -} - -thread_local! { - /// Fallback arena used when no app-specific arena is active. - /// In production, each window draw sets CURRENT_ELEMENT_ARENA to the app's arena. - pub(crate) static ELEMENT_ARENA: RefCell = RefCell::new(Arena::new(1024 * 1024)); - - /// Points to the current App's element arena during draw operations. - /// This allows multiple test Apps to have isolated arenas, preventing - /// cross-session corruption when the scheduler interleaves their tasks. - static CURRENT_ELEMENT_ARENA: Cell>> = const { Cell::new(None) }; -} - -/// Whether a window draw is currently in progress on this thread. -/// -/// This holds exactly while an `ElementArenaScope` is active: nested scopes -/// restore the previous (still set) arena pointer, so `CURRENT_ELEMENT_ARENA` -/// is `Some` from the outermost draw's start to its end. -/// -/// The `on_request_frame` callback uses this to defer draw requests that -/// arrive re-entrantly while a draw is already on the stack (e.g. via nested -/// message pumping in the Windows window procedure), instead of running a -/// nested draw or panicking on the already-borrowed App. -fn draw_in_progress() -> bool { - CURRENT_ELEMENT_ARENA.with(|current| current.get().is_some()) -} - -/// Allocates an element in the current arena. Uses the app-specific arena if one -/// is active (during draw), otherwise falls back to the thread-local ELEMENT_ARENA. -pub(crate) fn with_element_arena(f: impl FnOnce(&mut Arena) -> R) -> R { - CURRENT_ELEMENT_ARENA.with(|current| { - if let Some(arena_ptr) = current.get() { - // SAFETY: The pointer is valid for the duration of the draw operation - // that set it, and we're being called during that same draw. - let arena_cell = unsafe { &*arena_ptr }; - f(&mut arena_cell.borrow_mut()) - } else { - ELEMENT_ARENA.with_borrow_mut(f) - } - }) -} - -/// Scope guard that sets CURRENT_ELEMENT_ARENA for the duration of a draw -/// operation and tracks the arena's scope depth, so that a nested draw's -/// `ArenaClearNeeded::clear` is deferred rather than freeing memory the outer -/// draw still references (see `Arena::clear`). -/// -/// Call [`ElementArenaScope::exit`] with the same arena that was entered to -/// obtain the [`ArenaClearNeeded`] token the draw now owes; requiring `exit` -/// makes it impossible to request a clear before the scope has ended. The -/// scope's teardown — restoring the thread-local and balancing `begin_scope` -/// with `end_scope` — happens in `Drop`, so the arena's scope depth stays -/// balanced on every path, including when a panic unwinds a draw before `exit` -/// is reached. (If teardown lived only in `exit`, such a panic would leave the -/// scope depth permanently elevated and defer every future clear, leaking -/// memory unboundedly.) -pub(crate) struct ElementArenaScope { - /// The entered arena: compared against the argument in `exit`, and - /// dereferenced in `Drop` to end its scope (see the SAFETY note there). - entered: *const RefCell, - previous: Option<*const RefCell>, - exited: bool, -} - -impl ElementArenaScope { - /// Enter a scope where element allocations use the given arena. - pub(crate) fn enter(arena: &RefCell) -> Self { - arena.borrow_mut().begin_scope(); - let previous = CURRENT_ELEMENT_ARENA.with(|current| { - let prev = current.get(); - current.set(Some(arena as *const RefCell)); - prev - }); - Self { - entered: arena as *const RefCell, - previous, - exited: false, - } - } - - /// End the scope: restores the previously-current arena and ends the - /// arena's clear-deferral scope. Returns the token for the arena clear the - /// draw now owes; producing it here makes it impossible to request a clear - /// before the scope has ended (which would be silently deferred forever). - /// - /// Panics if passed a different arena than was entered: ending the scope - /// of the wrong arena would unbalance two arenas' scope depths, allowing - /// one of them to clear while a draw still references its memory. - pub(crate) fn exit(mut self, arena: &RefCell) -> ArenaClearNeeded { - assert!( - std::ptr::eq(self.entered, arena), - "ElementArenaScope::exit called with a different arena than was entered" - ); - self.exited = true; - // Teardown (restoring the thread-local and ending the arena's - // clear-deferral scope) runs in `Drop`, which fires both here — `self` - // is dropped as `exit` returns, before the token reaches the caller — - // and when a panic unwinds the draw before `exit` is reached. - ArenaClearNeeded::new(arena) - } -} - -impl Drop for ElementArenaScope { - fn drop(&mut self) { - // Teardown lives here (rather than in `exit`) so it runs exactly once on - // every path: `exit` consumes and drops the guard on the normal path, - // and unwinding drops it on the panic path. Balancing `begin_scope` here - // keeps the arena's scope depth correct even when a draw panics; if this - // only happened in `exit`, a panic between `enter` and `exit` would leave - // the depth elevated and defer every future clear. - CURRENT_ELEMENT_ARENA.with(|current| { - current.set(self.previous); - }); - // SAFETY: `entered` came from a `&RefCell` in `enter`, and the - // arena (owned by the `App` being drawn) outlives this guard on both the - // normal and unwinding paths, since the guard is a local of the draw. - unsafe { &*self.entered }.borrow_mut().end_scope(); - if !self.exited && !std::thread::panicking() { - debug_assert!(false, "ElementArenaScope dropped without calling exit()"); - log::error!( - "ElementArenaScope dropped without calling exit(); \ - the arena clear for this draw was never requested" - ); - } - } -} - -/// Returned when the element arena has been used and so must be cleared before the next draw. -#[must_use] -pub struct ArenaClearNeeded { - /// Identity of the arena that was drawn into. Only ever compared against - /// another pointer in `clear`; never dereferenced. - arena: *const RefCell, -} - -impl ArenaClearNeeded { - /// Create a new ArenaClearNeeded token for the App whose arena was drawn - /// into. Private: the only way to obtain one is [`ElementArenaScope::exit`]. - fn new(arena: &RefCell) -> Self { - Self { - arena: arena as *const RefCell, - } - } - - /// Clear the element arena of the App the draw ran against. If an enclosing - /// draw is still in progress (this draw was nested inside it), the clear is - /// deferred to the enclosing draw's own `ArenaClearNeeded` so that its live - /// allocations aren't freed. - /// - /// Panics if passed a different App than the draw ran against, since - /// clearing another App's arena could free memory its draws still - /// reference. - pub fn clear(self, cx: &mut App) { - assert!( - std::ptr::eq(self.arena, &cx.element_arena), - "ArenaClearNeeded::clear called with a different App than the draw ran against" - ); - cx.element_arena.borrow_mut().clear(); - } -} - -pub(crate) type FocusMap = RwLock>; -pub(crate) struct FocusRef { - pub(crate) ref_count: AtomicUsize, - pub(crate) tab_index: isize, - pub(crate) tab_stop: bool, -} - -impl FocusId { - /// Obtains whether the element associated with this handle is currently focused. - pub fn is_focused(&self, window: &Window) -> bool { - window.focus == Some(*self) - } - - /// Obtains whether the element associated with this handle contains the focused - /// element or is itself focused. - pub fn contains_focused(&self, window: &Window, cx: &App) -> bool { - window - .focused(cx) - .is_some_and(|focused| self.contains(focused.id, window)) - } - - /// Obtains whether the element associated with this handle is contained within the - /// focused element or is itself focused. - pub fn within_focused(&self, window: &Window, cx: &App) -> bool { - let focused = window.focused(cx); - focused.is_some_and(|focused| focused.id.contains(*self, window)) - } - - /// Obtains whether this handle contains the given handle in the most recently rendered frame. - pub(crate) fn contains(&self, other: Self, window: &Window) -> bool { - window - .rendered_frame - .dispatch_tree - .focus_contains(*self, other) - } -} - -/// A handle which can be used to track and manipulate the focused element in a window. -pub struct FocusHandle { - pub(crate) id: FocusId, - handles: Arc, - /// The index of this element in the tab order. - pub tab_index: isize, - /// Whether this element can be focused by tab navigation. - pub tab_stop: bool, -} - -impl std::fmt::Debug for FocusHandle { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("FocusHandle({:?})", self.id)) - } -} - -impl FocusHandle { - pub(crate) fn new(handles: &Arc) -> Self { - let id = handles.write().insert(FocusRef { - ref_count: AtomicUsize::new(1), - tab_index: 0, - tab_stop: false, - }); - - Self { - id, - tab_index: 0, - tab_stop: false, - handles: handles.clone(), - } - } - - pub(crate) fn for_id(id: FocusId, handles: &Arc) -> Option { - let lock = handles.read(); - let focus = lock.get(id)?; - if atomic_incr_if_not_zero(&focus.ref_count) == 0 { - return None; - } - Some(Self { - id, - tab_index: focus.tab_index, - tab_stop: focus.tab_stop, - handles: handles.clone(), - }) - } - - /// Sets the tab index of the element associated with this handle. - pub fn tab_index(mut self, index: isize) -> Self { - self.tab_index = index; - if let Some(focus) = self.handles.write().get_mut(self.id) { - focus.tab_index = index; - } - self - } - - /// Sets whether the element associated with this handle is a tab stop. - /// - /// When `false`, the element will not be included in the tab order. - pub fn tab_stop(mut self, tab_stop: bool) -> Self { - self.tab_stop = tab_stop; - if let Some(focus) = self.handles.write().get_mut(self.id) { - focus.tab_stop = tab_stop; - } - self - } - - /// Converts this focus handle into a weak variant, which does not prevent it from being released. - pub fn downgrade(&self) -> WeakFocusHandle { - WeakFocusHandle { - id: self.id, - handles: Arc::downgrade(&self.handles), - } - } - - /// Moves the focus to the element associated with this handle. - pub fn focus(&self, window: &mut Window, cx: &mut App) { - window.focus(self, cx) - } - - /// Obtains whether the element associated with this handle is currently focused. - pub fn is_focused(&self, window: &Window) -> bool { - self.id.is_focused(window) - } - - /// Obtains whether the element associated with this handle contains the focused - /// element or is itself focused. - pub fn contains_focused(&self, window: &Window, cx: &App) -> bool { - self.id.contains_focused(window, cx) - } - - /// Obtains whether the element associated with this handle is contained within the - /// focused element or is itself focused. - pub fn within_focused(&self, window: &Window, cx: &mut App) -> bool { - self.id.within_focused(window, cx) - } - - /// Obtains whether this handle contains the given handle in the most recently rendered frame. - pub fn contains(&self, other: &Self, window: &Window) -> bool { - self.id.contains(other.id, window) - } - - /// Dispatch an action on the element that rendered this focus handle - pub fn dispatch_action(&self, action: &dyn Action, window: &mut Window, cx: &mut App) { - if let Some(node_id) = window - .rendered_frame - .dispatch_tree - .focusable_node_id(self.id) - { - window.dispatch_action_on_node(node_id, action, cx) - } - } -} - -impl Clone for FocusHandle { - fn clone(&self) -> Self { - Self::for_id(self.id, &self.handles).unwrap() - } -} - -impl PartialEq for FocusHandle { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl Eq for FocusHandle {} - -impl Drop for FocusHandle { - fn drop(&mut self) { - self.handles - .read() - .get(self.id) - .unwrap() - .ref_count - .fetch_sub(1, SeqCst); - } -} - -/// A weak reference to a focus handle. -#[derive(Clone, Debug)] -pub struct WeakFocusHandle { - pub(crate) id: FocusId, - pub(crate) handles: Weak, -} - -impl WeakFocusHandle { - /// Attempts to upgrade the [WeakFocusHandle] to a [FocusHandle]. - pub fn upgrade(&self) -> Option { - let handles = self.handles.upgrade()?; - FocusHandle::for_id(self.id, &handles) - } -} - -impl PartialEq for WeakFocusHandle { - fn eq(&self, other: &WeakFocusHandle) -> bool { - self.id == other.id - } -} - -impl Eq for WeakFocusHandle {} - -impl PartialEq for WeakFocusHandle { - fn eq(&self, other: &FocusHandle) -> bool { - self.id == other.id - } -} - -impl PartialEq for FocusHandle { - fn eq(&self, other: &WeakFocusHandle) -> bool { - self.id == other.id - } -} - -/// Focusable allows users of your view to easily -/// focus it (using window.focus_view(cx, view)) -pub trait Focusable: 'static { - /// Returns the focus handle associated with this view. - fn focus_handle(&self, cx: &App) -> FocusHandle; -} - -impl Focusable for Entity { - fn focus_handle(&self, cx: &App) -> FocusHandle { - self.read(cx).focus_handle(cx) - } -} - -/// ManagedView is a view (like a Modal, Popover, Menu, etc.) -/// where the lifecycle of the view is handled by another view. -pub trait ManagedView: Focusable + EventEmitter + Render {} - -impl + Render> ManagedView for M {} - -/// Emitted by implementers of [`ManagedView`] to indicate the view should be dismissed, such as when a view is presented as a modal. -pub struct DismissEvent; - -type FrameCallback = Box; - -pub(crate) type AnyMouseListener = - Box; - -#[derive(Clone)] -pub(crate) struct CursorStyleRequest { - pub(crate) hitbox_id: Option, - pub(crate) style: CursorStyle, -} - -#[derive(Default, Eq, PartialEq)] -pub(crate) struct HitTest { - pub(crate) ids: SmallVec<[HitboxId; 8]>, - pub(crate) hover_hitbox_count: usize, -} - -/// A type of window control area that corresponds to the platform window. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum WindowControlArea { - /// An area that allows dragging of the platform window. - Drag, - /// An area that allows closing of the platform window. - Close, - /// An area that allows maximizing of the platform window. - Max, - /// An area that allows minimizing of the platform window. - Min, -} - -/// An identifier for a [Hitbox] which also includes [HitboxBehavior]. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub struct HitboxId(u64); - -#[cfg(feature = "test-support")] -impl HitboxId { - /// A placeholder HitboxId exclusively for integration testing API's that - /// need a hitbox but where the value of the hitbox does not matter. The - /// alternative is to make the Hitbox optional but that complicates the - /// implementation. - pub const fn placeholder() -> Self { - Self(0) - } -} - -impl HitboxId { - /// Checks if the hitbox with this ID is currently hovered. Returns `false` during keyboard - /// input modality so that keyboard navigation suppresses hover highlights. Except when handling - /// `ScrollWheelEvent`, this is typically what you want when determining whether to handle mouse - /// events or paint hover styles. - /// - /// See [`Hitbox::is_hovered`] for details. - pub fn is_hovered(self, window: &Window) -> bool { - // If this hitbox has captured the pointer, it's always considered hovered - if window.captured_hitbox == Some(self) { - return true; - } - if window.last_input_was_keyboard() { - return false; - } - self.hit_test(window) - } - - /// Checks if the hitbox with this ID is currently hovered, regardless of the last - /// input modality used. - /// - /// See [`HitboxId::is_hovered`] for more details. - pub(crate) fn is_hovered_ignoring_last_input(self, window: &Window) -> bool { - // If this hitbox has captured the pointer, it's always considered hovered - if window.captured_hitbox == Some(self) { - return true; - } - self.hit_test(window) - } - - fn hit_test(self, window: &Window) -> bool { - let hit_test = &window.mouse_hit_test; - for id in hit_test.ids.iter().take(hit_test.hover_hitbox_count) { - if self == *id { - return true; - } - } - false - } - - /// Checks if the hitbox with this ID contains the mouse and should handle scroll events. - /// Typically this should only be used when handling `ScrollWheelEvent`, and otherwise - /// `is_hovered` should be used. See the documentation of `Hitbox::is_hovered` for details about - /// this distinction. - pub fn should_handle_scroll(self, window: &Window) -> bool { - window.mouse_hit_test.ids.contains(&self) - } - - fn next(mut self) -> HitboxId { - HitboxId(self.0.wrapping_add(1)) - } -} - -/// A rectangular region that potentially blocks hitboxes inserted prior. -/// See [Window::insert_hitbox] for more details. -#[derive(Clone, Debug, Deref)] -pub struct Hitbox { - /// A unique identifier for the hitbox. - pub id: HitboxId, - /// The bounds of the hitbox. - #[deref] - pub bounds: Bounds, - /// The content mask when the hitbox was inserted. - pub content_mask: crate::ClipRegion, - /// Flags that specify hitbox behavior. - pub behavior: HitboxBehavior, -} - -impl Hitbox { - /// Checks if the hitbox is currently hovered. Returns `false` during keyboard input modality - /// so that keyboard navigation suppresses hover highlights. Except when handling - /// `ScrollWheelEvent`, this is typically what you want when determining whether to handle mouse - /// events or paint hover styles. - /// - /// This can return `false` even when the hitbox contains the mouse, if a hitbox in front of - /// this sets `HitboxBehavior::BlockMouse` (`InteractiveElement::occlude`) or - /// `HitboxBehavior::BlockMouseExceptScroll` (`InteractiveElement::block_mouse_except_scroll`), - /// or if the current input modality is keyboard (see [`Window::last_input_was_keyboard`]). - /// - /// Handling of `ScrollWheelEvent` should typically use `should_handle_scroll` instead. - /// Concretely, this is due to use-cases like overlays that cause the elements under to be - /// non-interactive while still allowing scrolling. More abstractly, this is because - /// `is_hovered` is about element interactions directly under the mouse - mouse moves, clicks, - /// hover styling, etc. In contrast, scrolling is about finding the current outer scrollable - /// container. - pub fn is_hovered(&self, window: &Window) -> bool { - self.id.is_hovered(window) - } - - /// Checks whether this hitbox would be hovered at `position`, regardless of the current input - /// modality or mouse position. - pub fn is_hovered_at(&self, position: Point, window: &Window) -> bool { - let hit_test = window.rendered_frame.hit_test(position); - hit_test - .ids - .iter() - .take(hit_test.hover_hitbox_count) - .any(|id| self.id == *id) - } - - /// Checks if the hitbox contains the mouse and should handle scroll events. Typically this - /// should only be used when handling `ScrollWheelEvent`, and otherwise `is_hovered` should be - /// used. See the documentation of `Hitbox::is_hovered` for details about this distinction. - /// - /// This can return `false` even when the hitbox contains the mouse, if a hitbox in front of - /// this sets `HitboxBehavior::BlockMouse` (`InteractiveElement::occlude`). - pub fn should_handle_scroll(&self, window: &Window) -> bool { - self.id.should_handle_scroll(window) - } -} - -/// How the hitbox affects mouse behavior. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub enum HitboxBehavior { - /// Normal hitbox mouse behavior, doesn't affect mouse handling for other hitboxes. - #[default] - Normal, - - /// All hitboxes behind this hitbox will be ignored and so will have `hitbox.is_hovered() == - /// false` and `hitbox.should_handle_scroll() == false`. Typically for elements this causes - /// skipping of all mouse events, hover styles, and tooltips. This flag is set by - /// [`InteractiveElement::occlude`]. - /// - /// For mouse handlers that check those hitboxes, this behaves the same as registering a - /// bubble-phase handler for every mouse event type: - /// - /// ```ignore - /// window.on_mouse_event(move |_: &EveryMouseEventTypeHere, phase, window, cx| { - /// if phase == DispatchPhase::Capture && hitbox.is_hovered(window) { - /// cx.stop_propagation(); - /// } - /// }) - /// ``` - /// - /// This has effects beyond event handling - any use of hitbox checking, such as hover - /// styles and tooltips. These other behaviors are the main point of this mechanism. An - /// alternative might be to not affect mouse event handling - but this would allow - /// inconsistent UI where clicks and moves interact with elements that are not considered to - /// be hovered. - BlockMouse, - - /// All hitboxes behind this hitbox will have `hitbox.is_hovered() == false`, even when - /// `hitbox.should_handle_scroll() == true`. Typically for elements this causes all mouse - /// interaction except scroll events to be ignored - see the documentation of - /// [`Hitbox::is_hovered`] for details. This flag is set by - /// [`InteractiveElement::block_mouse_except_scroll`]. - /// - /// For mouse handlers that check those hitboxes, this behaves the same as registering a - /// bubble-phase handler for every mouse event type **except** `ScrollWheelEvent`: - /// - /// ```ignore - /// window.on_mouse_event(move |_: &EveryMouseEventTypeExceptScroll, phase, window, cx| { - /// if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) { - /// cx.stop_propagation(); - /// } - /// }) - /// ``` - /// - /// See the documentation of [`Hitbox::is_hovered`] for details of why `ScrollWheelEvent` is - /// handled differently than other mouse events. If also blocking these scroll events is - /// desired, then a `cx.stop_propagation()` handler like the one above can be used. - /// - /// This has effects beyond event handling - this affects any use of `is_hovered`, such as - /// hover styles and tooltips. These other behaviors are the main point of this mechanism. - /// An alternative might be to not affect mouse event handling - but this would allow - /// inconsistent UI where clicks and moves interact with elements that are not considered to - /// be hovered. - BlockMouseExceptScroll, -} - -/// An identifier for a tooltip. -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] -pub struct TooltipId(usize); - -impl TooltipId { - /// Checks if the tooltip is currently hovered. - pub fn is_hovered(&self, window: &Window) -> bool { - window - .tooltip_bounds - .as_ref() - .is_some_and(|tooltip_bounds| { - tooltip_bounds.id == *self - && tooltip_bounds.bounds.contains(&window.mouse_position()) - }) - } -} - -pub(crate) struct TooltipBounds { - id: TooltipId, - bounds: Bounds, -} - -#[derive(Clone)] -pub(crate) struct TooltipRequest { - id: TooltipId, - tooltip: AnyTooltip, -} - -pub(crate) struct DeferredDraw { - current_view: EntityId, - priority: usize, - parent_node: DispatchNodeId, - element_id_stack: SmallVec<[ElementId; 32]>, - text_style_stack: Vec, - content_mask: Option, - rem_size: Pixels, - element: Option, - absolute_offset: Point, - prepaint_range: Range, - paint_range: Range, -} - -pub(crate) struct Frame { - pub(crate) focus: Option, - pub(crate) window_active: bool, - pub(crate) element_states: FxHashMap<(GlobalElementId, TypeId), ElementStateBox>, - accessed_element_states: Vec<(GlobalElementId, TypeId)>, - pub(crate) mouse_listeners: Vec>, - pub(crate) dispatch_tree: DispatchTree, - pub(crate) scene: Scene, - pub(crate) hitboxes: Vec, - pub(crate) window_control_hitboxes: Vec<(WindowControlArea, Hitbox)>, - pub(crate) deferred_draws: Vec, - pub(crate) input_handlers: Vec>, - pub(crate) tooltip_requests: Vec>, - pub(crate) cursor_styles: Vec, - #[cfg(any(test, feature = "test-support"))] - pub(crate) debug_bounds: FxHashMap>, - #[cfg(any(feature = "inspector", debug_assertions))] - pub(crate) next_inspector_instance_ids: FxHashMap, usize>, - #[cfg(any(feature = "inspector", debug_assertions))] - pub(crate) inspector_hitboxes: FxHashMap, - pub(crate) tab_stops: TabStopMap, -} - -#[derive(Clone, Default)] -pub(crate) struct PrepaintStateIndex { - hitboxes_index: usize, - tooltips_index: usize, - deferred_draws_index: usize, - dispatch_tree_index: usize, - accessed_element_states_index: usize, - line_layout_index: LineLayoutIndex, -} - -#[derive(Clone, Default)] -pub(crate) struct PaintIndex { - scene_index: usize, - mouse_listeners_index: usize, - input_handlers_index: usize, - cursor_styles_index: usize, - accessed_element_states_index: usize, - tab_handle_index: usize, - line_layout_index: LineLayoutIndex, -} - -impl Frame { - pub(crate) fn new(dispatch_tree: DispatchTree) -> Self { - Frame { - focus: None, - window_active: false, - element_states: FxHashMap::default(), - accessed_element_states: Vec::new(), - mouse_listeners: Vec::new(), - dispatch_tree, - scene: Scene::default(), - hitboxes: Vec::new(), - window_control_hitboxes: Vec::new(), - deferred_draws: Vec::new(), - input_handlers: Vec::new(), - tooltip_requests: Vec::new(), - cursor_styles: Vec::new(), - - #[cfg(any(test, feature = "test-support"))] - debug_bounds: FxHashMap::default(), - - #[cfg(any(feature = "inspector", debug_assertions))] - next_inspector_instance_ids: FxHashMap::default(), - - #[cfg(any(feature = "inspector", debug_assertions))] - inspector_hitboxes: FxHashMap::default(), - tab_stops: TabStopMap::default(), - } - } - - pub(crate) fn clear(&mut self) { - self.element_states.clear(); - self.accessed_element_states.clear(); - self.mouse_listeners.clear(); - self.dispatch_tree.clear(); - self.scene.clear(); - self.input_handlers.clear(); - self.tooltip_requests.clear(); - self.cursor_styles.clear(); - self.hitboxes.clear(); - self.window_control_hitboxes.clear(); - self.deferred_draws.clear(); - self.tab_stops.clear(); - self.focus = None; - - #[cfg(any(test, feature = "test-support"))] - { - self.debug_bounds.clear(); - } - - #[cfg(any(feature = "inspector", debug_assertions))] - { - self.next_inspector_instance_ids.clear(); - self.inspector_hitboxes.clear(); - } - } - - pub(crate) fn cursor_style(&self, window: &Window) -> Option { - self.cursor_styles - .iter() - .rev() - .fold_while(None, |style, request| match request.hitbox_id { - None => Done(Some(request.style)), - Some(hitbox_id) => Continue(style.or_else(|| { - hitbox_id - .is_hovered_ignoring_last_input(window) - .then_some(request.style) - })), - }) - .into_inner() - } - - pub(crate) fn hit_test(&self, position: Point) -> HitTest { - let mut set_hover_hitbox_count = false; - let mut hit_test = HitTest::default(); - for hitbox in self.hitboxes.iter().rev() { - let bounds = hitbox.bounds.intersect(&hitbox.content_mask.bounds); - if bounds.contains(&position) { - hit_test.ids.push(hitbox.id); - if !set_hover_hitbox_count - && hitbox.behavior == HitboxBehavior::BlockMouseExceptScroll - { - hit_test.hover_hitbox_count = hit_test.ids.len(); - set_hover_hitbox_count = true; - } - if hitbox.behavior == HitboxBehavior::BlockMouse { - break; - } - } - } - if !set_hover_hitbox_count { - hit_test.hover_hitbox_count = hit_test.ids.len(); - } - hit_test - } - - pub(crate) fn focus_path(&self) -> SmallVec<[FocusId; 8]> { - self.focus - .map(|focus_id| self.dispatch_tree.focus_path(focus_id)) - .unwrap_or_default() - } - - pub(crate) fn finish(&mut self, prev_frame: &mut Self) { - for element_state_key in &self.accessed_element_states { - if let Some((element_state_key, element_state)) = - prev_frame.element_states.remove_entry(element_state_key) - { - self.element_states.insert(element_state_key, element_state); - } - } - - self.scene.finish(); - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -enum InputModality { - Mouse, - Keyboard, - Touch, -} - -/// Holds the state for a specific window. -pub struct Window { - pub(crate) handle: AnyWindowHandle, - pub(crate) invalidator: WindowInvalidator, - pub(crate) removed: bool, - pub(crate) platform_window: Box, - display_id: Option, - is_resizable: bool, - is_minimizable: bool, - sprite_atlas: Arc, - text_system: Arc, - text_rendering_mode: Rc>, - rem_size: Pixels, - /// The stack of override values for the window's rem size. - /// - /// This is used by `with_rem_size` to allow rendering an element tree with - /// a given rem size. - rem_size_override_stack: SmallVec<[Pixels; 8]>, - pub(crate) viewport_size: Size, - layout_engine: Option, - pub(crate) root: Option, - pub(crate) element_id_stack: SmallVec<[ElementId; 32]>, - pub(crate) text_style_stack: Vec, - pub(crate) rendered_entity_stack: Vec, - pub(crate) element_offset_stack: Vec>, - pub(crate) element_opacity: f32, - pub(crate) content_mask_stack: Vec, - pub(crate) requested_autoscroll: Option>, - /// The [`TextInputConfiguration`] most recently forwarded to the platform - /// window, so that only actual changes are forwarded (reconfiguring a live - /// input session can restart the IME connection). - last_text_input_configuration: Option, - focused_text_input_active: bool, - pub(crate) image_cache_stack: Vec, - pub(crate) rendered_frame: Frame, - pub(crate) next_frame: Frame, - next_hitbox_id: HitboxId, - pub(crate) next_tooltip_id: TooltipId, - pub(crate) tooltip_bounds: Option, - pub(crate) next_frame_callbacks: Rc>>, - pub(crate) dirty_views: FxHashSet, - focus_listeners: SubscriberSet<(), AnyWindowFocusListener>, - pub(crate) focus_lost_listeners: SubscriberSet<(), AnyObserver>, - focus_lost_path: SmallVec<[FocusId; 8]>, - default_prevented: bool, - mouse_position: Point, - mouse_hit_test: HitTest, - modifiers: Modifiers, - capslock: Capslock, - scale_factor: f32, - pub(crate) bounds_observers: SubscriberSet<(), AnyObserver>, - appearance: WindowAppearance, - pub(crate) appearance_observers: SubscriberSet<(), AnyObserver>, - pub(crate) button_layout_observers: SubscriberSet<(), AnyObserver>, - active: Rc>, - hovered: Rc>, - pub(crate) needs_present: Rc>, - /// Tracks recent input event timestamps to determine if input is arriving at a high rate. - /// Used to selectively enable VRR optimization only when input rate exceeds 60fps. - pub(crate) input_rate_tracker: Rc>, - #[cfg(feature = "profiler")] - window_profiler: profiler::WindowProfiler, - last_input_modality: InputModality, - touch_gestures: TouchGestureRecognizer, - touch_prediction_enabled: bool, - long_press_timer: Option>, - long_press_capture: Option, - pub(crate) refreshing: bool, - pub(crate) activation_observers: SubscriberSet<(), AnyObserver>, - pub(crate) focus: Option, - focus_enabled: bool, - /// Incremented every time focus moves. Used to invalidate a - /// pending keyboard activation state when focus changes. - pub(crate) focus_generation: u64, - pending_input: Option, - pending_modifier: ModifierState, - pub(crate) pending_input_observers: SubscriberSet<(), AnyObserver>, - prompt: Option, - pub(crate) client_inset: Option, - /// The hitbox that has captured the pointer, if any. - /// While captured, mouse events route to this hitbox regardless of hit testing. - captured_hitbox: Option, - #[cfg(any(feature = "inspector", debug_assertions))] - inspector: Option>, - #[cfg(feature = "profiler")] - debug_frame_overlay: crate::debug_overlay::DebugFrameOverlay, - pub(crate) a11y: A11y, -} - -#[derive(Clone, Debug, Default)] -struct ModifierState { - modifiers: Modifiers, - saw_other_input: bool, -} - -/// Tracks input event timestamps to determine if input is arriving at a high rate. -/// Used for selective VRR (Variable Refresh Rate) optimization. -#[derive(Clone, Debug)] -pub(crate) struct InputRateTracker { - timestamps: Vec, - window: Duration, - inputs_per_second: u32, - sustain_until: Instant, - sustain_duration: Duration, -} - -impl Default for InputRateTracker { - fn default() -> Self { - Self { - timestamps: Vec::new(), - window: Duration::from_millis(100), - inputs_per_second: 60, - sustain_until: Instant::now(), - sustain_duration: Duration::from_secs(1), - } - } -} - -impl InputRateTracker { - pub fn record_input(&mut self) { - let now = Instant::now(); - self.timestamps.push(now); - self.prune_old_timestamps(now); - - let min_events = self.inputs_per_second as u128 * self.window.as_millis() / 1000; - if self.timestamps.len() as u128 >= min_events { - self.sustain_until = now + self.sustain_duration; - } - } - - pub fn is_high_rate(&self) -> bool { - Instant::now() < self.sustain_until - } - - fn prune_old_timestamps(&mut self, now: Instant) { - self.timestamps - .retain(|&t| now.duration_since(t) <= self.window); - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub(crate) enum DrawPhase { - None, - Prepaint, - Paint, - Focus, -} - -pub(crate) const PENDING_INPUT_TIMEOUT: Duration = Duration::from_secs(1); - -/// Pending input for a potential multi-stroke key binding. -pub struct PendingInputStatus<'a> { - keystrokes: &'a [Keystroke], - timeout: Option, -} - -impl<'a> PendingInputStatus<'a> { - /// Returns the keystrokes entered so far. - pub fn keystrokes(&self) -> &'a [Keystroke] { - self.keystrokes - } - - /// Returns the timeout state for flushing this input, if it needs a timeout. - pub fn timeout(&self) -> Option { - self.timeout - } -} - -/// The timeout state for pending input. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct PendingInputTimeoutStatus { - duration: Duration, - remaining: Duration, - started_at: Option, - paused: bool, -} - -impl PendingInputTimeoutStatus { - /// Returns the full timeout duration. - pub fn duration(&self) -> Duration { - self.duration - } - - /// Returns the duration remaining before pending input is flushed. - pub fn remaining(&self, cx: &App) -> Duration { - self.started_at - .map(|started_at| { - self.remaining - .saturating_sub(cx.background_executor().now() - started_at) - }) - .unwrap_or(self.remaining) - } - - /// Returns whether the timeout is paused. - pub fn is_paused(&self) -> bool { - self.paused - } -} - -#[derive(Debug)] -struct PendingInputTimeout { - duration: Duration, - remaining: Duration, - state: PendingInputTimeoutState, -} - -#[derive(Debug)] -enum PendingInputTimeoutState { - Running { started_at: Instant, task: Task<()> }, - Paused { pause: PendingInputTimeoutPause }, -} - -#[derive(Debug)] -struct PendingInputTimeoutPause { - owner_id: EntityId, - _release_subscription: Subscription, -} - -impl PendingInputTimeout { - fn is_paused(&self) -> bool { - matches!(&self.state, PendingInputTimeoutState::Paused { .. }) - } - - fn pause(&mut self, pause: PendingInputTimeoutPause, now: Instant) -> bool { - match std::mem::replace(&mut self.state, PendingInputTimeoutState::Paused { pause }) { - PendingInputTimeoutState::Running { started_at, task } => { - self.remaining = self.remaining.saturating_sub(now - started_at); - drop(task); - true - } - previous_state @ PendingInputTimeoutState::Paused { .. } => { - self.state = previous_state; - false - } - } - } - - fn pause_owner_id(&self) -> Option { - match &self.state { - PendingInputTimeoutState::Running { .. } => None, - PendingInputTimeoutState::Paused { pause } => Some(pause.owner_id), - } - } - - fn resume(&mut self, owner_id: EntityId, started_at: Instant, task: Task<()>) -> bool { - match std::mem::replace( - &mut self.state, - PendingInputTimeoutState::Running { started_at, task }, - ) { - PendingInputTimeoutState::Paused { pause } if pause.owner_id == owner_id => true, - previous_state => { - self.state = previous_state; - false - } - } - } - - fn reset_duration(&mut self, duration: Duration) { - self.duration = duration; - self.remaining = duration; - } - - fn status(&self) -> PendingInputTimeoutStatus { - let (started_at, paused) = match &self.state { - PendingInputTimeoutState::Running { started_at, .. } => (Some(*started_at), false), - PendingInputTimeoutState::Paused { .. } => (None, true), - }; - PendingInputTimeoutStatus { - duration: self.duration, - remaining: self.remaining, - started_at, - paused, - } - } -} - -#[derive(Default, Debug)] -struct PendingInput { - keystrokes: SmallVec<[Keystroke; 1]>, - focus: Option, - timeout: Option, -} - -pub(crate) struct ElementStateBox { - pub(crate) inner: Box, - #[cfg(debug_assertions)] - pub(crate) type_name: &'static str, -} - -fn default_bounds(display_id: Option, cx: &mut App) -> WindowBounds { - // TODO, BUG: if you open a window with the currently active window - // on the stack, this will erroneously fallback to `None` - // - // TODO these should be the initial window bounds not considering maximized/fullscreen - let active_window_bounds = cx - .active_window() - .and_then(|w| w.update(cx, |_, window, _| window.window_bounds()).ok()); - - const CASCADE_OFFSET: f32 = 25.0; - - let display = display_id - .map(|id| cx.find_display(id)) - .unwrap_or_else(|| cx.primary_display()); - - let default_placement = || Bounds::new(point(px(0.), px(0.)), DEFAULT_WINDOW_SIZE); - - // Use visible_bounds to exclude taskbar/dock areas - let display_bounds = display - .as_ref() - .map(|d| d.visible_bounds()) - .unwrap_or_else(default_placement); - - let ( - Bounds { - origin: base_origin, - size: base_size, - }, - window_bounds_ctor, - ): (_, fn(Bounds) -> WindowBounds) = match active_window_bounds { - Some(bounds) => match bounds { - WindowBounds::Windowed(bounds) => (bounds, WindowBounds::Windowed), - WindowBounds::Maximized(bounds) => (bounds, WindowBounds::Maximized), - WindowBounds::Fullscreen(bounds) => (bounds, WindowBounds::Fullscreen), - }, - None => ( - display - .as_ref() - .map(|d| d.default_bounds()) - .unwrap_or_else(default_placement), - WindowBounds::Windowed, - ), - }; - - let cascade_offset = point(px(CASCADE_OFFSET), px(CASCADE_OFFSET)); - let proposed_origin = base_origin + cascade_offset; - let proposed_bounds = Bounds::new(proposed_origin, base_size); - - let display_right = display_bounds.origin.x + display_bounds.size.width; - let display_bottom = display_bounds.origin.y + display_bounds.size.height; - let window_right = proposed_bounds.origin.x + proposed_bounds.size.width; - let window_bottom = proposed_bounds.origin.y + proposed_bounds.size.height; - - let fits_horizontally = window_right <= display_right; - let fits_vertically = window_bottom <= display_bottom; - - let final_origin = match (fits_horizontally, fits_vertically) { - (true, true) => proposed_origin, - (false, true) => point(display_bounds.origin.x, base_origin.y), - (true, false) => point(base_origin.x, display_bounds.origin.y), - (false, false) => display_bounds.origin, - }; - window_bounds_ctor(Bounds::new(final_origin, base_size)) -} - -impl Window { - pub(crate) fn new( - handle: AnyWindowHandle, - options: WindowOptions, - cx: &mut App, - ) -> Result { - let WindowOptions { - window_bounds, - titlebar, - focus, - show, - kind, - is_movable, - app_owns_titlebar_drag, - inactive_frame_interval, - is_resizable, - is_minimizable, - display_id, - window_background, - app_id, - window_min_size, - window_decorations, - #[cfg_attr( - not(any(target_os = "linux", target_os = "freebsd")), - allow(unused_variables) - )] - icon, - #[cfg_attr(not(target_os = "macos"), allow(unused_variables))] - tabbing_identifier, - } = options; - - let initial_window_title = titlebar - .as_ref() - .and_then(|titlebar| titlebar.title.clone()); - - let window_bounds = window_bounds.unwrap_or_else(|| default_bounds(display_id, cx)); - let mut platform_window = cx.platform.open_window( - handle, - WindowParams { - bounds: window_bounds.get_bounds(), - titlebar, - kind, - is_movable, - app_owns_titlebar_drag, - is_resizable, - is_minimizable, - focus, - show, - display_id, - window_min_size, - app_id: app_id.clone(), - icon, - #[cfg(target_os = "macos")] - tabbing_identifier, - }, - )?; - - let tab_bar_visible = platform_window.tab_bar_visible(); - SystemWindowTabController::init_visible(cx, tab_bar_visible); - if let Some(tabs) = platform_window.tabbed_windows() { - SystemWindowTabController::add_tab(cx, handle.window_id(), tabs); - } - - let display_id = platform_window.display().map(|display| display.id()); - let sprite_atlas = platform_window.sprite_atlas(); - let mouse_position = platform_window.mouse_position(); - let modifiers = platform_window.modifiers(); - let capslock = platform_window.capslock(); - let content_size = platform_window.content_size(); - let scale_factor = platform_window.scale_factor(); - let appearance = platform_window.appearance(); - let text_system = Arc::new(WindowTextSystem::new(cx.text_system().clone())); - let invalidator = WindowInvalidator::new(handle.window_id()); - let active = Rc::new(Cell::new(platform_window.is_active())); - let hovered = Rc::new(Cell::new(platform_window.is_hovered())); - let needs_present = Rc::new(Cell::new(false)); - let next_frame_callbacks: Rc>> = Default::default(); - let input_rate_tracker = Rc::new(RefCell::new(InputRateTracker::default())); - let last_frame_time = Rc::new(Cell::new(None)); - - platform_window - .request_decorations(window_decorations.unwrap_or(WindowDecorations::Server)); - platform_window.set_background_appearance(window_background); - - match window_bounds { - WindowBounds::Fullscreen(_) => platform_window.toggle_fullscreen(), - WindowBounds::Maximized(_) => platform_window.zoom(), - WindowBounds::Windowed(_) => {} - } - - let accessibility_force_disabled = cx.accessibility_force_disabled; - let a11y_active_flag = Arc::new(AtomicBool::new(false)); - - #[cfg(not(target_family = "wasm"))] - if !accessibility_force_disabled { - let mut initial_root_node = accesskit::Node::new(accesskit::Role::Window); - if let Some(title) = &initial_window_title { - initial_root_node.set_label(title.to_string()); - } - let initial_tree = accesskit::TreeUpdate { - nodes: vec![(ROOT_NODE_ID, initial_root_node)], - tree: Some(accesskit::Tree::new(ROOT_NODE_ID)), - tree_id: accesskit::TreeId::ROOT, - focus: ROOT_NODE_ID, - }; - let (activation_sender, activation_receiver) = async_channel::unbounded::<()>(); - let (deactivation_sender, deactivation_receiver) = async_channel::unbounded::<()>(); - let (action_sender, action_receiver) = - async_channel::unbounded::(); - - platform_window.a11y_init(crate::A11yCallbacks { - activation: { - let active_flag = a11y_active_flag.clone(); - Box::new(move || { - log::info!("Accessibility activated"); - active_flag.store(true, SeqCst); - activation_sender.send_blocking(()).log_err(); - Some(initial_tree.clone()) - }) - }, - action: Box::new(move |request| { - action_sender.send_blocking(request).log_err(); - }), - deactivation: { - let active_flag = a11y_active_flag.clone(); - Box::new(move || { - log::info!("Accessibility deactivated"); - active_flag.store(false, SeqCst); - deactivation_sender.send_blocking(()).log_err(); - }) - }, - }); - - // A11y can be activated at any time, and so we cannot compute a - // correct `TreeUpdate` on-demand. When this happens, we return a - // default empty `TreeUpdate`. - // - // So we force a new frame, which will then send a correct `TreeUpdate`. - let mut async_cx = cx.to_async(); - cx.foreground_executor() - .spawn(async move { - while activation_receiver.recv().await.is_ok() { - handle - .update(&mut async_cx, |_, window, _| window.refresh()) - .log_err(); - } - }) - .detach(); - - let mut async_cx = cx.to_async(); - cx.foreground_executor() - .spawn(async move { - while deactivation_receiver.recv().await.is_ok() { - handle - .update(&mut async_cx, |_, window, _| window.refresh()) - .log_err(); - } - }) - .detach(); - - let mut async_cx = cx.to_async(); - cx.foreground_executor() - .spawn(async move { - while let Ok(request) = action_receiver.recv().await { - handle - .update(&mut async_cx, |_, window, cx| { - window.handle_a11y_action(request, cx); - }) - .log_err(); - } - }) - .detach(); - } - - platform_window.on_close(Box::new({ - let window_id = handle.window_id(); - let mut cx = cx.to_async(); - move || { - let _ = handle.update(&mut cx, |_, window, _| window.remove_window()); - let _ = cx.update(|cx| { - SystemWindowTabController::remove_tab(cx, window_id); - }); - } - })); - platform_window.on_request_frame(Box::new({ - let mut cx = cx.to_async(); - let invalidator = invalidator.clone(); - let active = active.clone(); - let needs_present = needs_present.clone(); - let next_frame_callbacks = next_frame_callbacks.clone(); - let input_rate_tracker = input_rate_tracker.clone(); - let mut deferred_force_render = false; - move |request_frame_options| { - #[cfg(feature = "profiler")] - let _foreground_turn = profiler::journal::foreground_turn(); - // This must be checked before anything else: if this request - // arrived re-entrantly while a draw is on this thread's stack - // (e.g. via a nested message pump in the Windows window - // procedure), drawing would nest draws, and even touching the - // App would panic on its already-mutable borrow. Skip instead; - // the platform leaves the window invalidated (or re-invalidates - // it), so a fresh request arrives once the in-progress draw - // unwinds. Remember force_render so the deferred frame still - // bypasses the view cache. - // - // Returning here skips `complete_frame`, which on Wayland would - // stall the window's frame callbacks (no `surface.commit()`) — - // but calling it would hit the App borrow panic above, and this - // branch is unreachable there in practice: only Windows pumps - // platform events (and thus requests frames) mid-draw. - if draw_in_progress() { - log::debug!("deferring re-entrant window draw request"); - deferred_force_render |= request_frame_options.force_render; - return; - } - // Take the deferred flag first: `||` short-circuits, and leaving - // the flag set when this request already forces a render would - // force a second, redundant render on the next frame. - let force_render = - mem::take(&mut deferred_force_render) || request_frame_options.force_render; - - let thermal_state = handle - .update(&mut cx, |_, _, cx| cx.thermal_state()) - .log_err(); - - // Throttle frame rate based on conditions: - // - Thermal pressure (Serious/Critical): cap to ~60fps - // - Inactive window (not focused): cap to ~30fps to save energy - let min_frame_interval = if request_frame_options.require_presentation - || (!request_frame_options.force_render - && next_frame_callbacks.borrow().is_empty()) - { - None - } else if !active.get() && !input_rate_tracker.borrow_mut().is_high_rate() { - inactive_frame_interval - } else if let Some(ThermalState::Critical | ThermalState::Serious) = thermal_state { - Some(Duration::from_micros(16667)) - } else { - None - }; - - let now = Instant::now(); - if let Some(min_interval) = min_frame_interval { - if let Some(last_frame) = last_frame_time.get() - && now.duration_since(last_frame) < min_interval - { - // Don't lose a pending forced render to throttling. - deferred_force_render |= force_render; - // Deferred by throttling: ask demand-driven platforms to retry. - handle - .update(&mut cx, |_, window, _| { - window.platform_window.schedule_frame(); - }) - .log_err(); - // The demand that entered this branch (a deferred forced - // render or pending next-frame callbacks) is still - // unserved; platforms that stop requesting frames for - // idle windows need a wakeup to deliver the retry. - invalidator.wake_platform(); - return; - } - } - last_frame_time.set(Some(now)); - - let pending_next_frame_callbacks = next_frame_callbacks.take(); - if !pending_next_frame_callbacks.is_empty() { - handle - .update(&mut cx, |_, window, cx| { - for callback in pending_next_frame_callbacks { - callback(window, cx); - } - }) - .log_err(); - } - - // Keep presenting if input was recently arriving at a high rate (>= 60fps). - // Once high-rate input is detected, we sustain presentation for 1 second - // to prevent display underclocking during active input. - let needs_present = request_frame_options.require_presentation - || needs_present.get() - || input_rate_tracker.borrow_mut().is_high_rate(); - - if invalidator.is_dirty() || force_render { - measure("frame duration", || { - handle - .update(&mut cx, |_, window, cx| { - if force_render { - // Bypass cached view reuse so we don't replay stale - // atlas tile references after a GPU device recovery. - window.refresh(); - } - let arena_clear_needed = window.draw(cx); - window.present(); - arena_clear_needed.clear(cx); - }) - .log_err(); - }) - } else if needs_present { - handle - .update(&mut cx, |_, window, _| window.present()) - .log_err(); - } - - handle - .update(&mut cx, |_, window, _| { - if window.invalidator.is_dirty() - || !window.next_frame_callbacks.borrow().is_empty() - { - window.platform_window.schedule_frame(); - } - }) - .log_err(); - - // Platforms that stop requesting frames for idle windows only - // deliver another request after a wakeup. If demand remains - // after this frame (the window was re-invalidated mid-draw, or - // animations scheduled next-frame callbacks), re-arm the frame - // source explicitly. - if invalidator.is_dirty() || !next_frame_callbacks.borrow().is_empty() { - invalidator.wake_platform(); - } - } - })); - invalidator.set_platform_waker(platform_window.frame_waker()); - platform_window.on_resize(Box::new({ - let mut cx = cx.to_async(); - move |_, _| { - handle - .update(&mut cx, |_, window, cx| window.bounds_changed(cx)) - .log_err(); - } - })); - platform_window.on_moved(Box::new({ - let mut cx = cx.to_async(); - move || { - handle - .update(&mut cx, |_, window, cx| window.bounds_changed(cx)) - .log_err(); - } - })); - platform_window.on_appearance_changed(Box::new({ - let cx = cx.to_async(); - let foreground_executor = cx.foreground_executor().clone(); - move || { - let mut cx = cx.clone(); - // Defer the update because changing the AppKit appearance may - // synchronously invoke this callback while App is already borrowed. - foreground_executor - .spawn(async move { - handle - .update(&mut cx, |_, window, cx| window.appearance_changed(cx)) - .log_err(); - }) - .detach(); - } - })); - platform_window.on_button_layout_changed(Box::new({ - let mut cx = cx.to_async(); - move || { - handle - .update(&mut cx, |_, window, cx| window.button_layout_changed(cx)) - .log_err(); - } - })); - platform_window.on_active_status_change(Box::new({ - let mut cx = cx.to_async(); - move |active| { - handle - .update(&mut cx, |_, window, cx| { - window.active.set(active); - window.modifiers = window.platform_window.modifiers(); - window.capslock = window.platform_window.capslock(); - window - .activation_observers - .clone() - .retain(&(), |callback| callback(window, cx)); - - window.bounds_changed(cx); - window.refresh(); - - SystemWindowTabController::update_last_active(cx, window.handle.id); - }) - .log_err(); - } - })); - platform_window.on_hover_status_change(Box::new({ - let mut cx = cx.to_async(); - move |active| { - handle - .update(&mut cx, |_, window, _| { - window.hovered.set(active); - window.refresh(); - }) - .log_err(); - } - })); - platform_window.on_input({ - let mut cx = cx.to_async(); - Box::new(move |event| { - handle - .update(&mut cx, |_, window, cx| window.dispatch_event(event, cx)) - .log_err() - .unwrap_or(DispatchEventResult::default()) - }) - }); - platform_window.on_hit_test_window_control({ - let mut cx = cx.to_async(); - Box::new(move || { - handle - .update(&mut cx, |_, window, _cx| { - for (area, hitbox) in &window.rendered_frame.window_control_hitboxes { - if window.mouse_hit_test.ids.contains(&hitbox.id) { - return Some(*area); - } - } - None - }) - .log_err() - .unwrap_or(None) - }) - }); - platform_window.on_move_tab_to_new_window({ - let mut cx = cx.to_async(); - Box::new(move || { - handle - .update(&mut cx, |_, _window, cx| { - SystemWindowTabController::move_tab_to_new_window(cx, handle.window_id()); - }) - .log_err(); - }) - }); - platform_window.on_merge_all_windows({ - let mut cx = cx.to_async(); - Box::new(move || { - handle - .update(&mut cx, |_, _window, cx| { - SystemWindowTabController::merge_all_windows(cx, handle.window_id()); - }) - .log_err(); - }) - }); - platform_window.on_select_next_tab({ - let mut cx = cx.to_async(); - Box::new(move || { - handle - .update(&mut cx, |_, _window, cx| { - SystemWindowTabController::select_next_tab(cx, handle.window_id()); - }) - .log_err(); - }) - }); - platform_window.on_select_previous_tab({ - let mut cx = cx.to_async(); - Box::new(move || { - handle - .update(&mut cx, |_, _window, cx| { - SystemWindowTabController::select_previous_tab(cx, handle.window_id()) - }) - .log_err(); - }) - }); - platform_window.on_toggle_tab_bar({ - let mut cx = cx.to_async(); - Box::new(move || { - handle - .update(&mut cx, |_, window, cx| { - let tab_bar_visible = window.platform_window.tab_bar_visible(); - SystemWindowTabController::set_visible(cx, tab_bar_visible); - }) - .log_err(); - }) - }); - - if let Some(app_id) = app_id { - platform_window.set_app_id(&app_id); - } - - platform_window.map_window().unwrap(); - - Ok(Window { - handle, - invalidator, - removed: false, - platform_window, - display_id, - is_resizable, - is_minimizable, - sprite_atlas, - text_system, - text_rendering_mode: cx.text_rendering_mode.clone(), - rem_size: px(16.), - rem_size_override_stack: SmallVec::new(), - viewport_size: content_size, - layout_engine: Some(TaffyLayoutEngine::new()), - root: None, - element_id_stack: SmallVec::default(), - text_style_stack: Vec::new(), - rendered_entity_stack: Vec::new(), - element_offset_stack: Vec::new(), - content_mask_stack: Vec::new(), - element_opacity: 1.0, - requested_autoscroll: None, - last_text_input_configuration: None, - focused_text_input_active: false, - rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), - next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), - next_frame_callbacks, - next_hitbox_id: HitboxId(0), - next_tooltip_id: TooltipId::default(), - tooltip_bounds: None, - dirty_views: FxHashSet::default(), - focus_listeners: SubscriberSet::new(), - focus_lost_listeners: SubscriberSet::new(), - focus_lost_path: SmallVec::new(), - default_prevented: true, - mouse_position, - mouse_hit_test: HitTest::default(), - modifiers, - capslock, - scale_factor, - bounds_observers: SubscriberSet::new(), - appearance, - appearance_observers: SubscriberSet::new(), - button_layout_observers: SubscriberSet::new(), - active, - hovered, - needs_present, - input_rate_tracker, - #[cfg(feature = "profiler")] - window_profiler: profiler::WindowProfiler::new(handle.window_id())?, - last_input_modality: InputModality::Mouse, - touch_gestures: TouchGestureRecognizer::new( - cx.platform - .gestures() - .map_or_else(GestureTuning::default, |gestures| gestures.tuning()), - ), - touch_prediction_enabled: true, - long_press_timer: None, - long_press_capture: None, - refreshing: false, - activation_observers: SubscriberSet::new(), - focus: None, - focus_enabled: true, - focus_generation: 0, - pending_input: None, - pending_modifier: ModifierState::default(), - pending_input_observers: SubscriberSet::new(), - prompt: None, - client_inset: None, - image_cache_stack: Vec::new(), - captured_hitbox: None, - #[cfg(any(feature = "inspector", debug_assertions))] - inspector: None, - #[cfg(feature = "profiler")] - debug_frame_overlay: crate::debug_overlay::DebugFrameOverlay::new(), - a11y: A11y::new( - a11y_active_flag, - accessibility_force_disabled, - initial_window_title, - ), - }) - } - - pub(crate) fn new_focus_listener( - &self, - value: AnyWindowFocusListener, - ) -> (Subscription, impl FnOnce() + use<>) { - self.focus_listeners.insert((), value) - } -} - -#[derive(Clone, Debug, Default, PartialEq, Eq)] -#[expect(missing_docs)] -pub struct DispatchEventResult { - pub propagate: bool, - pub default_prevented: bool, -} - -/// Indicates which region of the window is visible. Content falling outside of -/// this mask will not be rendered. A mask carries both its rectangular cull -/// bounds and optional rounded corners; the bounds keep scene culling cheap, -/// while the renderer applies the corner shape to every primitive. -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct ContentMask { - /// The bounds - pub bounds: Bounds

, - /// The corner radii of the visible region. - pub corner_radii: Corners

, _keymap: &Keymap) { - *self.inner.state.menus.borrow_mut() = menus.into_iter().map(|menu| menu.owned()).collect(); - } - - fn get_menus(&self) -> Option> { - Some(self.inner.state.menus.borrow().clone()) - } - - fn set_dock_menu(&self, menus: Vec, _keymap: &Keymap) { - self.set_dock_menus(menus); - } - - fn on_app_menu_action(&self, callback: Box) { - self.inner - .state - .callbacks - .app_menu_action - .set(Some(callback)); - } - - fn on_will_open_app_menu(&self, callback: Box) { - self.inner - .state - .callbacks - .will_open_app_menu - .set(Some(callback)); - } - - fn on_validate_app_menu_command(&self, callback: Box bool>) { - self.inner - .state - .callbacks - .validate_app_menu_command - .set(Some(callback)); - } - - fn app_path(&self) -> Result { - Ok(std::env::current_exe()?) - } - - // todo(windows) - fn path_for_auxiliary_executable(&self, _name: &str) -> Result { - anyhow::bail!("not yet implemented"); - } - - fn set_cursor_style(&self, style: CursorStyle) { - let hcursor = load_cursor(style); - if self.inner.state.current_cursor.get().map(|c| c.0) != hcursor.map(|c| c.0) { - self.post_message( - WM_GPUI_CURSOR_STYLE_CHANGED, - WPARAM(0), - LPARAM(hcursor.map_or(0, |c| c.0 as isize)), - ); - self.inner.state.current_cursor.set(hcursor); - } - } - - fn hide_cursor_until_mouse_moves(&self) { - if !self - .inner - .state - .cursor_visible - .swap(false, Ordering::Relaxed) - { - return; - } - - for handle in self.raw_window_handles.read().iter() { - let Some(window) = window_from_hwnd(handle.as_raw()) else { - continue; - }; - if window.state.hovered.get() { - unsafe { SetCursor(None) }; - break; - } - } - } - - fn is_cursor_visible(&self) -> bool { - self.inner.state.cursor_visible.load(Ordering::Relaxed) - } - - fn should_auto_hide_scrollbars(&self) -> bool { - should_auto_hide_scrollbars().log_err().unwrap_or(false) - } - - fn write_to_clipboard(&self, item: ClipboardItem) { - write_to_clipboard(item); - } - - fn read_from_clipboard(&self) -> Option { - read_from_clipboard() - } - - fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task> { - // CredWriteW rejects larger blobs with the opaque RPC error - // 0x800706F7 "The stub received bad data", so fail with a clear - // message instead. - if password.len() > CRED_MAX_CREDENTIAL_BLOB_SIZE as usize { - return Task::ready(Err(anyhow!( - "credential for {url} is {} bytes, which exceeds the Windows Credential Manager limit of {CRED_MAX_CREDENTIAL_BLOB_SIZE} bytes", - password.len() - ))); - } - let password = password.to_vec(); - let mut username = username.encode_utf16().chain(Some(0)).collect_vec(); - let mut target_name = windows_credentials_target_name(url) - .encode_utf16() - .chain(Some(0)) - .collect_vec(); - self.foreground_executor().spawn(async move { - let credentials = CREDENTIALW { - LastWritten: unsafe { GetSystemTimeAsFileTime() }, - Flags: CRED_FLAGS(0), - Type: CRED_TYPE_GENERIC, - TargetName: PWSTR::from_raw(target_name.as_mut_ptr()), - CredentialBlobSize: password.len() as u32, - CredentialBlob: password.as_ptr() as *mut _, - Persist: CRED_PERSIST_LOCAL_MACHINE, - UserName: PWSTR::from_raw(username.as_mut_ptr()), - ..CREDENTIALW::default() - }; - unsafe { - CredWriteW(&credentials, 0).map_err(|err| { - anyhow!( - "Failed to write credentials to Windows Credential Manager: {}", - err, - ) - })?; - } - Ok(()) - }) - } - - fn read_credentials(&self, url: &str) -> Task)>>> { - let target_name = windows_credentials_target_name(url) - .encode_utf16() - .chain(Some(0)) - .collect_vec(); - self.foreground_executor().spawn(async move { - let mut credentials: *mut CREDENTIALW = std::ptr::null_mut(); - let result = unsafe { - CredReadW( - PCWSTR::from_raw(target_name.as_ptr()), - CRED_TYPE_GENERIC, - None, - &mut credentials, - ) - }; - - if let Err(err) = result { - // ERROR_NOT_FOUND means the credential doesn't exist. - // Return Ok(None) to match macOS and Linux behavior. - if err.code() == ERROR_NOT_FOUND.to_hresult() { - return Ok(None); - } - return Err(err.into()); - } - - if credentials.is_null() { - Ok(None) - } else { - let username: String = unsafe { (*credentials).UserName.to_string()? }; - let credential_blob = unsafe { - std::slice::from_raw_parts( - (*credentials).CredentialBlob, - (*credentials).CredentialBlobSize as usize, - ) - }; - let password = credential_blob.to_vec(); - unsafe { CredFree(credentials as *const _ as _) }; - Ok(Some((username, password))) - } - }) - } - - fn delete_credentials(&self, url: &str) -> Task> { - let target_name = windows_credentials_target_name(url) - .encode_utf16() - .chain(Some(0)) - .collect_vec(); - self.foreground_executor().spawn(async move { - unsafe { - CredDeleteW( - PCWSTR::from_raw(target_name.as_ptr()), - CRED_TYPE_GENERIC, - None, - )? - }; - Ok(()) - }) - } - - fn register_url_scheme(&self, _: &str) -> Task> { - Task::ready(Err(anyhow!("register_url_scheme unimplemented"))) - } - - fn perform_dock_menu_action(&self, action: usize) { - unsafe { - PostMessageW( - Some(self.handle), - WM_GPUI_DOCK_MENU_ACTION, - WPARAM(self.inner.validation_number), - LPARAM(action as isize), - ) - .log_err(); - } - } - - fn update_jump_list( - &self, - menus: Vec, - entries: Vec>, - ) -> Task>> { - self.update_jump_list(menus, entries) - } -} - -impl WindowsPlatformInner { - fn new(context: &mut PlatformWindowCreateContext) -> Result> { - let state = WindowsPlatformState::new(context.directx_devices.take()); - Ok(Rc::new(Self { - state, - raw_window_handles: context.raw_window_handles.clone(), - dispatcher: context - .dispatcher - .as_ref() - .context("missing dispatcher")? - .clone(), - validation_number: context.validation_number, - main_receiver: context - .main_receiver - .take() - .context("missing main receiver")?, - })) - } - - /// Calls `project` to project to the corresponding callback field, removes it from callbacks, calls `f` with the callback and then puts the callback back. - fn with_callback( - &self, - project: impl Fn(&PlatformCallbacks) -> &Cell>, - f: impl FnOnce(&mut T), - ) { - let callback = project(&self.state.callbacks).take(); - if let Some(mut callback) = callback { - f(&mut callback); - project(&self.state.callbacks).set(Some(callback)); - } - } - - fn handle_msg( - self: &Rc, - handle: HWND, - msg: u32, - wparam: WPARAM, - lparam: LPARAM, - ) -> LRESULT { - let handled = match msg { - WM_GPUI_CLOSE_ONE_WINDOW - | WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD - | WM_GPUI_DOCK_MENU_ACTION - | WM_GPUI_KEYBOARD_LAYOUT_CHANGED - | WM_GPUI_GPU_DEVICE_LOST - | WM_GPUI_END_SESSION => self.handle_gpui_events(msg, wparam, lparam), - WM_POWERBROADCAST => self.handle_power_broadcast(wparam), - _ => None, - }; - if let Some(result) = handled { - LRESULT(result) - } else { - unsafe { DefWindowProcW(handle, msg, wparam, lparam) } - } - } - - fn handle_gpui_events(&self, message: u32, wparam: WPARAM, lparam: LPARAM) -> Option { - if wparam.0 != self.validation_number { - log::error!("Wrong validation number while processing message: {message}"); - return None; - } - match message { - WM_GPUI_CLOSE_ONE_WINDOW => { - self.close_one_window(HWND(lparam.0 as _)); - Some(0) - } - WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD => self.run_foreground_task(), - WM_GPUI_DOCK_MENU_ACTION => self.handle_dock_action_event(lparam.0 as _), - WM_GPUI_KEYBOARD_LAYOUT_CHANGED => self.handle_keyboard_layout_change(), - WM_GPUI_GPU_DEVICE_LOST => self.handle_device_lost(lparam), - WM_GPUI_END_SESSION => self.handle_end_session(), - _ => unreachable!(), - } - } - - fn handle_end_session(&self) -> Option { - let mut shutdown_completed = false; - self.with_callback( - |callbacks| &callbacks.quit, - |callback| shutdown_completed = callback(), - ); - log::logger().flush(); - if shutdown_completed { - std::process::exit(0); - } - - // Shutdown couldn't run synchronously, since the AppCell is already borrowed. - // Windows may terminate the application as soon as we return from this handler, but if we post a WM_QUIT message now, - // we may get to gracefully shut down the app before we're terminated by the OS. - unsafe { PostQuitMessage(0) }; - Some(0) - } - - fn close_one_window(&self, target_window: HWND) -> bool { - let Some(all_windows) = self.raw_window_handles.upgrade() else { - log::error!("Failed to upgrade raw window handles"); - return false; - }; - let mut lock = all_windows.write(); - let index = lock - .iter() - .position(|handle| handle.as_raw() == target_window) - .unwrap(); - lock.remove(index); - - lock.is_empty() - } - - #[inline] - fn run_foreground_task(&self) -> Option { - const MAIN_TASK_TIMEOUT: u128 = 10; - - let start = std::time::Instant::now(); - 'tasks: loop { - 'timeout_loop: loop { - if start.elapsed().as_millis() >= MAIN_TASK_TIMEOUT { - log::debug!("foreground task timeout reached"); - // we spent our budget on gpui tasks, we likely have a lot of work queued so drain system events first to stay responsive - // then quit out of foreground work to allow us to process other gpui events first before returning back to foreground task work - // if we don't we might not for example process window quit events - let mut msg = MSG::default(); - let process_message = |msg: &_| { - if translate_accelerator(msg).is_none() { - _ = unsafe { TranslateMessage(msg) }; - unsafe { DispatchMessageW(msg) }; - } - }; - let peek_msg = |msg: &mut _, msg_kind| unsafe { - PeekMessageW(msg, None, 0, 0, PM_REMOVE | msg_kind).as_bool() - }; - // We need to process a paint message here as otherwise we will re-enter `run_foreground_task` before painting if we have work remaining. - // The reason for this is that windows prefers custom application message processing over system messages. - if peek_msg(&mut msg, PM_QS_PAINT) { - process_message(&msg); - } - while peek_msg(&mut msg, PM_QS_INPUT) { - process_message(&msg); - } - // Allow the main loop to process other gpui events before going back into `run_foreground_task` - unsafe { - if let Err(_) = PostMessageW( - Some(self.dispatcher.platform_window_handle.as_raw()), - WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD, - WPARAM(self.validation_number), - LPARAM(0), - ) { - self.dispatcher.wake_posted.store(false, Ordering::Release); - }; - } - break 'tasks; - } - let mut main_receiver = self.main_receiver.clone(); - match main_receiver.try_pop() { - Ok(Some(runnable)) => WindowsDispatcher::execute_runnable(runnable), - _ => break 'timeout_loop, - } - } - - // Someone could enqueue a Runnable here. The flag is still true, so they will not PostMessage. - // We need to check for those Runnables after we clear the flag. - self.dispatcher.wake_posted.store(false, Ordering::Release); - let mut main_receiver = self.main_receiver.clone(); - match main_receiver.try_pop() { - Ok(Some(runnable)) => { - self.dispatcher.wake_posted.store(true, Ordering::Release); - - WindowsDispatcher::execute_runnable(runnable); - } - _ => break 'tasks, - } - } - - Some(0) - } - - fn handle_dock_action_event(&self, action_idx: usize) -> Option { - let Some(action) = self - .state - .jump_list - .borrow() - .dock_menus - .get(action_idx) - .map(|dock_menu| dock_menu.action.boxed_clone()) - else { - log::error!("Dock menu for index {action_idx} not found"); - return Some(1); - }; - self.with_callback( - |callbacks| &callbacks.app_menu_action, - |callback| callback(&*action), - ); - Some(0) - } - - fn handle_keyboard_layout_change(&self) -> Option { - self.with_callback( - |callbacks| &callbacks.keyboard_layout_change, - |callback| callback(), - ); - Some(0) - } - - fn handle_power_broadcast(&self, wparam: WPARAM) -> Option { - if wparam.0 as u32 == PBT_APMRESUMEAUTOMATIC { - self.with_callback(|callbacks| &callbacks.system_wake, |callback| callback()); - } - Some(1) - } - - fn handle_device_lost(&self, lparam: LPARAM) -> Option { - let directx_devices = lparam.0 as *const DirectXDevices; - let directx_devices = unsafe { &*directx_devices }; - self.state.directx_devices.borrow_mut().take(); - *self.state.directx_devices.borrow_mut() = Some(directx_devices.clone()); - - Some(0) - } -} - -impl Drop for WindowsPlatform { - fn drop(&mut self) { - unsafe { - if let Some(notification) = self.suspend_resume_notification.borrow_mut().take() { - // SAFETY: notification was returned by RegisterSuspendResumeNotification. - UnregisterSuspendResumeNotification(notification).log_err(); - } - DestroyWindow(self.handle) - .context("Destroying platform window") - .log_err(); - OleUninitialize(); - } - } -} - -pub(crate) struct WindowCreationInfo { - pub(crate) icon: HICON, - pub(crate) executor: ForegroundExecutor, - pub(crate) current_cursor: Option, - pub(crate) cursor_visible: Arc, - pub(crate) drop_target_helper: IDropTargetHelper, - pub(crate) validation_number: usize, - pub(crate) main_receiver: PriorityQueueReceiver, - pub(crate) platform_window_handle: HWND, - pub(crate) disable_direct_composition: bool, - pub(crate) directx_devices: DirectXDevices, - /// Flag to instruct the `VSyncProvider` thread to invalidate the directx devices - /// as resizing them has failed, causing us to have lost at least the render target. - pub(crate) invalidate_devices: Arc, - /// Shared with [`WindowsPlatformState::draw_coordinator`] and every other window. - pub(crate) draw_coordinator: Rc, -} - -struct PlatformWindowCreateContext { - inner: Option>>, - raw_window_handles: std::sync::Weak>>, - validation_number: usize, - main_sender: Option>, - main_receiver: Option>, - directx_devices: Option, - dispatcher: Option>, -} - -fn has_package_identity() -> bool { - let mut package_full_name_length = 0; - let result = unsafe { - windows::Win32::Storage::Packaging::Appx::GetCurrentPackageFullName( - &mut package_full_name_length, - None, - ) - }; - if result == ERROR_INSUFFICIENT_BUFFER { - true - } else if result == APPMODEL_ERROR_NO_PACKAGE { - false - } else { - log::warn!("failed to determine whether the process has package identity: {result:?}"); - false - } -} - -fn open_target(target: impl AsRef) -> Result<()> { - let target = target.as_ref(); - let ret = unsafe { - ShellExecuteW( - None, - windows::core::w!("open"), - &HSTRING::from(target), - None, - None, - SW_SHOWDEFAULT, - ) - }; - if ret.0 as isize <= 32 { - Err(anyhow::anyhow!( - "Unable to open target: {}", - std::io::Error::last_os_error() - )) - } else { - Ok(()) - } -} - -fn open_target_in_explorer(target: &Path) -> Result<()> { - let dir = target.parent().context("No parent folder found")?; - let desktop = unsafe { SHGetDesktopFolder()? }; - - let mut dir_item = std::ptr::null_mut(); - unsafe { - desktop.ParseDisplayName( - HWND::default(), - None, - &HSTRING::from(dir), - None, - &mut dir_item, - std::ptr::null_mut(), - )?; - } - - let mut file_item = std::ptr::null_mut(); - unsafe { - desktop.ParseDisplayName( - HWND::default(), - None, - &HSTRING::from(target), - None, - &mut file_item, - std::ptr::null_mut(), - )?; - } - - let highlight = [file_item as *const _]; - unsafe { SHOpenFolderAndSelectItems(dir_item as _, Some(&highlight), 0) }.or_else(|err| { - if err.code().0 == ERROR_FILE_NOT_FOUND.0 as i32 { - // On some systems, the above call mysteriously fails with "file not - // found" even though the file is there. In these cases, ShellExecute() - // seems to work as a fallback (although it won't select the file). - open_target(dir).context("Opening target parent folder") - } else { - Err(anyhow::anyhow!("Can not open target path: {}", err)) - } - }) -} - -fn file_open_dialog( - options: PathPromptOptions, - window: Option, -) -> Result>> { - let folder_dialog: IFileOpenDialog = - unsafe { CoCreateInstance(&FileOpenDialog, None, CLSCTX_ALL)? }; - - let mut dialog_options = FOS_FILEMUSTEXIST; - if options.multiple { - dialog_options |= FOS_ALLOWMULTISELECT; - } - if options.directories { - dialog_options |= FOS_PICKFOLDERS; - } - - unsafe { - folder_dialog.SetOptions(dialog_options)?; - - if let Some(prompt) = options.prompt { - let prompt: &str = &prompt; - folder_dialog.SetOkButtonLabel(&HSTRING::from(prompt))?; - } - - if folder_dialog.Show(window).is_err() { - // User cancelled - return Ok(None); - } - } - - let results = unsafe { folder_dialog.GetResults()? }; - let file_count = unsafe { results.GetCount()? }; - if file_count == 0 { - return Ok(None); - } - - let mut paths = Vec::with_capacity(file_count as usize); - for i in 0..file_count { - let item = unsafe { results.GetItemAt(i)? }; - let path = unsafe { item.GetDisplayName(SIGDN_FILESYSPATH)?.to_string()? }; - paths.push(PathBuf::from(path)); - } - - Ok(Some(paths)) -} - -fn file_save_dialog( - directory: PathBuf, - suggested_name: Option, - window: Option, -) -> Result> { - let dialog: IFileSaveDialog = unsafe { CoCreateInstance(&FileSaveDialog, None, CLSCTX_ALL)? }; - if !directory.to_string_lossy().is_empty() - && let Some(full_path) = directory - .canonicalize() - .context("failed to canonicalize directory") - .log_err() - { - let full_path = dunce::simplified(&full_path); - let full_path_string = full_path.display().to_string(); - let path_item: IShellItem = - unsafe { SHCreateItemFromParsingName(&HSTRING::from(full_path_string), None)? }; - unsafe { - dialog - .SetFolder(&path_item) - .context("failed to set dialog folder") - .log_err() - }; - } - - if let Some(suggested_name) = suggested_name { - unsafe { - dialog - .SetFileName(&HSTRING::from(suggested_name)) - .context("failed to set file name") - .log_err() - }; - } - - unsafe { - dialog.SetFileTypes(&[Common::COMDLG_FILTERSPEC { - pszName: windows::core::w!("All files"), - pszSpec: windows::core::w!("*.*"), - }])?; - if dialog.Show(window).is_err() { - // User cancelled - return Ok(None); - } - } - let shell_item = unsafe { dialog.GetResult()? }; - let file_path_string = unsafe { - let pwstr = shell_item.GetDisplayName(SIGDN_FILESYSPATH)?; - let string = pwstr.to_string()?; - CoTaskMemFree(Some(pwstr.0 as _)); - string - }; - Ok(Some(PathBuf::from(file_path_string))) -} - -fn load_icon() -> Result { - let module = unsafe { GetModuleHandleW(None).context("unable to get module handle")? }; - let handle = unsafe { - LoadImageW( - Some(module.into()), - windows::core::PCWSTR(1 as _), - IMAGE_ICON, - 0, - 0, - LR_DEFAULTSIZE | LR_SHARED, - ) - .context("unable to load icon file")? - }; - Ok(HICON(handle.0)) -} - -#[inline] -fn should_auto_hide_scrollbars() -> Result { - let ui_settings = UISettings::new()?; - Ok(ui_settings.AutoHideScrollBars()?) -} - -fn check_device_lost(device: &ID3D11Device) -> bool { - let device_state = unsafe { device.GetDeviceRemovedReason() }; - match device_state { - Ok(_) => false, - Err(err) => { - log::error!("DirectX device lost detected: {:?}", err); - true - } - } -} - -fn handle_gpu_device_lost( - directx_devices: &mut DirectXDevices, - platform_window: HWND, - validation_number: usize, - all_windows: &std::sync::Weak>>, - text_system: &std::sync::Weak, -) -> Result<()> { - // Here we wait a bit to ensure the system has time to recover from the device lost state. - // If we don't wait, the final drawing result will be blank. - std::thread::sleep(std::time::Duration::from_millis(350)); - - *directx_devices = try_to_recover_from_device_lost(|| { - DirectXDevices::new().context("Failed to recreate new DirectX devices after device lost") - })?; - log::info!("DirectX devices successfully recreated."); - - let lparam = LPARAM(directx_devices as *const _ as _); - unsafe { - SendMessageW( - platform_window, - WM_GPUI_GPU_DEVICE_LOST, - Some(WPARAM(validation_number)), - Some(lparam), - ); - } - - if let Some(text_system) = text_system.upgrade() { - text_system.handle_gpu_lost(&directx_devices)?; - } - if let Some(all_windows) = all_windows.upgrade() { - for window in all_windows.read().iter() { - unsafe { - SendMessageW( - window.as_raw(), - WM_GPUI_GPU_DEVICE_LOST, - Some(WPARAM(validation_number)), - Some(lparam), - ); - } - } - std::thread::sleep(std::time::Duration::from_millis(200)); - for window in all_windows.read().iter() { - unsafe { - SendMessageW( - window.as_raw(), - WM_GPUI_FORCE_UPDATE_WINDOW, - Some(WPARAM(validation_number)), - None, - ); - } - } - } - Ok(()) -} - -const PLATFORM_WINDOW_CLASS_NAME: PCWSTR = w!("Zed::PlatformWindow"); - -fn register_platform_window_class() { - let wc = WNDCLASSW { - lpfnWndProc: Some(window_procedure), - lpszClassName: PCWSTR(PLATFORM_WINDOW_CLASS_NAME.as_ptr()), - ..Default::default() - }; - unsafe { RegisterClassW(&wc) }; -} - -unsafe extern "system" fn window_procedure( - hwnd: HWND, - msg: u32, - wparam: WPARAM, - lparam: LPARAM, -) -> LRESULT { - if msg == WM_NCCREATE { - let params = unsafe { &*(lparam.0 as *const CREATESTRUCTW) }; - let creation_context = params.lpCreateParams as *mut PlatformWindowCreateContext; - let creation_context = unsafe { &mut *creation_context }; - - let Some(main_sender) = creation_context.main_sender.take() else { - creation_context.inner = Some(Err(anyhow!("missing main sender"))); - return LRESULT(0); - }; - creation_context.dispatcher = Some(Arc::new(WindowsDispatcher::new( - main_sender, - hwnd, - creation_context.validation_number, - ))); - - return match WindowsPlatformInner::new(creation_context) { - Ok(inner) => { - let weak = Box::new(Rc::downgrade(&inner)); - unsafe { set_window_long(hwnd, GWLP_USERDATA, Box::into_raw(weak) as isize) }; - creation_context.inner = Some(Ok(inner)); - unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) } - } - Err(error) => { - creation_context.inner = Some(Err(error)); - LRESULT(0) - } - }; - } - - let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak; - if ptr.is_null() { - return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }; - } - let inner = unsafe { &*ptr }; - let result = if let Some(inner) = inner.upgrade() { - if cfg!(debug_assertions) { - let inner = std::panic::AssertUnwindSafe(inner); - match std::panic::catch_unwind(|| { inner }.handle_msg(hwnd, msg, wparam, lparam)) { - Ok(result) => result, - Err(_) => std::process::abort(), - } - } else { - inner.handle_msg(hwnd, msg, wparam, lparam) - } - } else { - unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) } - }; - - if msg == WM_NCDESTROY { - unsafe { set_window_long(hwnd, GWLP_USERDATA, 0) }; - unsafe { drop(Box::from_raw(ptr)) }; - } - - result -} - -#[cfg(test)] -mod tests { - use std::ffi::{OsStr, OsString}; - - use crate::{read_from_clipboard, write_to_clipboard}; - use gpui::ClipboardItem; - - use super::encode_restart_arguments; - - #[test] - fn test_encode_restart_arguments() { - assert_eq!(encode_restart_arguments(&[]), OsStr::new("")); - assert_eq!( - encode_restart_arguments(&[ - OsString::from("--user-data-dir"), - OsString::from(r"C:\Zed Data"), - ]), - OsStr::new(r#""--user-data-dir" "C:\Zed Data""#) - ); - assert_eq!( - encode_restart_arguments(&[OsString::from(r"C:\")]), - OsStr::new(r#""C:\\""#) - ); - } - - #[test] - fn test_clipboard() { - let item = ClipboardItem::new_string("你好,我是张小白".to_string()); - write_to_clipboard(item.clone()); - assert_eq!(read_from_clipboard(), Some(item)); - - let item = ClipboardItem::new_string("12345".to_string()); - write_to_clipboard(item.clone()); - assert_eq!(read_from_clipboard(), Some(item)); - - let item = ClipboardItem::new_string_with_json_metadata("abcdef".to_string(), vec![3, 4]); - write_to_clipboard(item.clone()); - assert_eq!(read_from_clipboard(), Some(item)); - } -} diff --git a/crates/gpui_pre_windows/src/shaders.hlsl b/crates/gpui_pre_windows/src/shaders.hlsl deleted file mode 100644 index 4e9089db..00000000 --- a/crates/gpui_pre_windows/src/shaders.hlsl +++ /dev/null @@ -1,1379 +0,0 @@ -#include "alpha_correction.hlsl" - -cbuffer GlobalParams: register(b0) { - float4 gamma_ratios; - float2 global_viewport_size; - float grayscale_enhanced_contrast; - float subpixel_enhanced_contrast; - uint is_bgr; - uint3 global_pad; -}; - -cbuffer BatchParams: register(b1) { - uint batch_start_index; - uint3 batch_pad; -}; - -Texture2D t_sprite: register(t0); -SamplerState s_sprite: register(s0); - -struct SubpixelSpriteFragmentOutput { - float4 foreground : SV_Target0; - float4 alpha : SV_Target1; -}; - -struct Bounds { - float2 origin; - float2 size; -}; - -struct Corners { - float top_left; - float top_right; - float bottom_right; - float bottom_left; -}; - -struct ContentMask { - Bounds bounds; - Corners corner_radii; - uint clip_index; - uint clip_padding; -}; - -struct RoundedClip { - Bounds bounds; - Corners radii_x; - Corners radii_y; - uint parent; - uint padding; -}; -StructuredBuffer rounded_clips: register(t2); - -struct Edges { - float top; - float right; - float bottom; - float left; -}; - -struct Hsla { - float h; - float s; - float l; - float a; -}; - -struct LinearColorStop { - Hsla color; - float percentage; -}; - -struct Background { - // 0u is Solid - // 1u is LinearGradient - // 2u is PatternSlash - uint tag; - // 0u is sRGB linear color - // 1u is Oklab color - uint color_space; - Hsla solid; - float gradient_angle_or_pattern_height; - LinearColorStop colors[2]; - uint pad; -}; - -struct GradientColor { - float4 solid; - float4 color0; - float4 color1; -}; - -struct AtlasTextureId { - uint index; - uint kind; -}; - -struct AtlasBounds { - int2 origin; - int2 size; -}; - -struct AtlasTile { - AtlasTextureId texture_id; - uint tile_id; - uint padding; - AtlasBounds bounds; -}; - -struct TransformationMatrix { - float2x2 rotation_scale; - float2 translation; -}; - -static const float M_PI_F = 3.141592653f; -static const float3 GRAYSCALE_FACTORS = float3(0.2126f, 0.7152f, 0.0722f); - -float4 to_device_position_impl(float2 position) { - float2 device_position = position / global_viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0); - return float4(device_position, 0., 1.); -} - -float4 to_device_position(float2 unit_vertex, Bounds bounds) { - float2 position = unit_vertex * bounds.size + bounds.origin; - return to_device_position_impl(position); -} - -float4 distance_from_clip_rect_impl(float2 position, ContentMask mask) { - float2 tl = position - mask.bounds.origin; - float2 br = mask.bounds.origin + mask.bounds.size - position; - return float4(tl.x, br.x, tl.y, br.y); -} - -// Path sprites are rasterized into an intermediate texture with their mask -// already applied; this overload keeps their existing rectangular clip. -float4 distance_from_clip_rect_impl(float2 position, Bounds clip_bounds) { - float2 tl = position - clip_bounds.origin; - float2 br = clip_bounds.origin + clip_bounds.size - position; - return float4(tl.x, br.x, tl.y, br.y); -} - -float4 distance_from_clip_rect(float2 unit_vertex, Bounds bounds, ContentMask mask) { - float2 position = unit_vertex * bounds.size + bounds.origin; - return distance_from_clip_rect_impl(position, mask); -} - -float4 distance_from_clip_rect_transformed(float2 unit_vertex, Bounds bounds, ContentMask mask, TransformationMatrix transformation) { - float2 position = unit_vertex * bounds.size + bounds.origin; - float2 transformed = mul(position, transformation.rotation_scale) + transformation.translation; - return distance_from_clip_rect_impl(transformed, mask); -} - -float clip_corner_distance(float2 point, float2 radii) { - if (any(radii <= float2(0.0, 0.0)) || any(point >= radii)) return -1e20; - float2 p = point - radii; - float k0 = length(p / radii); - float k1 = length(p / (radii * radii)); - if (k1 == 0.0) return -min(radii.x, radii.y); - return k0 * (k0 - 1.0) / k1; -} - -float rounded_clip_distance(float2 position, RoundedClip clip) { - float2 tl = position - clip.bounds.origin; - float2 br = clip.bounds.size - tl; - float distance = max(max(-tl.x, -tl.y), max(-br.x, -br.y)); - distance = max(distance, clip_corner_distance(tl, float2(clip.radii_x.top_left, clip.radii_y.top_left))); - distance = max(distance, clip_corner_distance(float2(br.x, tl.y), float2(clip.radii_x.top_right, clip.radii_y.top_right))); - distance = max(distance, clip_corner_distance(br, float2(clip.radii_x.bottom_right, clip.radii_y.bottom_right))); - distance = max(distance, clip_corner_distance(float2(tl.x, br.y), float2(clip.radii_x.bottom_left, clip.radii_y.bottom_left))); - return distance; -} - -float content_mask_coverage(float2 position, ContentMask mask) { - float distance = -1e20; - if (mask.clip_index == 0) { - RoundedClip clip; - clip.bounds = mask.bounds; - clip.radii_x = mask.corner_radii; - clip.radii_y = mask.corner_radii; - distance = rounded_clip_distance(position, clip); - } else { - uint index = mask.clip_index; - do { - RoundedClip clip = rounded_clips[index - 1]; - distance = max(distance, rounded_clip_distance(position, clip)); - index = clip.parent; - } while (index != 0); - } - return saturate(0.5 - distance); -} - -// Convert linear RGB to sRGB -float3 linear_to_srgb(float3 color) { - return pow(color, float3(2.2, 2.2, 2.2)); -} - -// Convert sRGB to linear RGB -float3 srgb_to_linear(float3 color) { - return pow(color, float3(1.0 / 2.2, 1.0 / 2.2, 1.0 / 2.2)); -} - -/// Hsla to linear RGBA conversion. -float4 hsla_to_rgba(Hsla hsla) { - float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range - float s = hsla.s; - float l = hsla.l; - float a = hsla.a; - - float c = (1.0 - abs(2.0 * l - 1.0)) * s; - float x = c * (1.0 - abs(fmod(h, 2.0) - 1.0)); - float m = l - c / 2.0; - - float r = 0.0; - float g = 0.0; - float b = 0.0; - - if (h >= 0.0 && h < 1.0) { - r = c; - g = x; - b = 0.0; - } else if (h >= 1.0 && h < 2.0) { - r = x; - g = c; - b = 0.0; - } else if (h >= 2.0 && h < 3.0) { - r = 0.0; - g = c; - b = x; - } else if (h >= 3.0 && h < 4.0) { - r = 0.0; - g = x; - b = c; - } else if (h >= 4.0 && h < 5.0) { - r = x; - g = 0.0; - b = c; - } else { - r = c; - g = 0.0; - b = x; - } - - float4 rgba; - rgba.x = (r + m); - rgba.y = (g + m); - rgba.z = (b + m); - rgba.w = a; - return rgba; -} - -// Converts a sRGB color to the Oklab color space. -// Reference: https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab -float4 srgb_to_oklab(float4 color) { - // Convert non-linear sRGB to linear sRGB - color = float4(srgb_to_linear(color.rgb), color.a); - - float l = 0.4122214708 * color.r + 0.5363325363 * color.g + 0.0514459929 * color.b; - float m = 0.2119034982 * color.r + 0.6806995451 * color.g + 0.1073969566 * color.b; - float s = 0.0883024619 * color.r + 0.2817188376 * color.g + 0.6299787005 * color.b; - - float l_ = pow(l, 1.0/3.0); - float m_ = pow(m, 1.0/3.0); - float s_ = pow(s, 1.0/3.0); - - return float4( - 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_, - 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_, - 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_, - color.a - ); -} - -// Converts an Oklab color to the sRGB color space. -float4 oklab_to_srgb(float4 color) { - float l_ = color.r + 0.3963377774 * color.g + 0.2158037573 * color.b; - float m_ = color.r - 0.1055613458 * color.g - 0.0638541728 * color.b; - float s_ = color.r - 0.0894841775 * color.g - 1.2914855480 * color.b; - - float l = l_ * l_ * l_; - float m = m_ * m_ * m_; - float s = s_ * s_ * s_; - - float3 linear_rgb = float3( - 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s, - -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s, - -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s - ); - - // Convert linear sRGB to non-linear sRGB - return float4(linear_to_srgb(linear_rgb), color.a); -} - -// 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 half_size) { - float delta = min(half_size.y - corner - abs(y), 0.); - float curved = half_size.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; -} - -// 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); -} - -float4 over(float4 below, float4 above) { - float4 result; - float alpha = above.a + below.a * (1.0 - above.a); - result.rgb = (above.rgb * above.a + below.rgb * below.a * (1.0 - above.a)) / alpha; - result.a = alpha; - return result; -} - -float2 to_tile_position(float2 unit_vertex, AtlasTile tile) { - float2 atlas_size; - t_sprite.GetDimensions(atlas_size.x, atlas_size.y); - return (float2(tile.bounds.origin) + unit_vertex * float2(tile.bounds.size)) / atlas_size; -} - -// Selects corner radius based on quadrant. -float pick_corner_radius(float2 center_to_point, Corners corner_radii) { - if (center_to_point.x < 0.) { - if (center_to_point.y < 0.) { - return corner_radii.top_left; - } else { - return corner_radii.bottom_left; - } - } else { - if (center_to_point.y < 0.) { - return corner_radii.top_right; - } else { - return corner_radii.bottom_right; - } - } -} - -float4 to_device_position_transformed(float2 unit_vertex, Bounds bounds, - TransformationMatrix transformation) { - float2 position = unit_vertex * bounds.size + bounds.origin; - float2 transformed = mul(position, transformation.rotation_scale) + transformation.translation; - float2 device_position = transformed / global_viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0); - return float4(device_position, 0.0, 1.0); -} - -// Implementation of quad signed distance field -float quad_sdf_impl(float2 corner_center_to_point, float corner_radius) { - if (corner_radius == 0.0) { - // Fast path for unrounded corners - return max(corner_center_to_point.x, corner_center_to_point.y); - } else { - // Signed distance of the point from a quad that is inset by corner_radius - // It is negative inside this quad, and positive outside - float signed_distance_to_inset_quad = - // 0 inside the inset quad, and positive outside - length(max(float2(0.0, 0.0), corner_center_to_point)) + - // 0 outside the inset quad, and negative inside - min(0.0, max(corner_center_to_point.x, corner_center_to_point.y)); - - return signed_distance_to_inset_quad - corner_radius; - } -} - -float quad_sdf(float2 pt, Bounds bounds, Corners corner_radii) { - float2 half_size = bounds.size / 2.; - float2 center = bounds.origin + half_size; - float2 center_to_point = pt - center; - float corner_radius = pick_corner_radius(center_to_point, corner_radii); - float2 corner_to_point = abs(center_to_point) - half_size; - float2 corner_center_to_point = corner_to_point + corner_radius; - return quad_sdf_impl(corner_center_to_point, corner_radius); -} - -GradientColor prepare_gradient_color(uint tag, uint color_space, Hsla solid, LinearColorStop colors[2]) { - GradientColor output; - if (tag == 0 || tag == 2 || tag == 3) { - output.solid = hsla_to_rgba(solid); - } else if (tag == 1) { - output.color0 = hsla_to_rgba(colors[0].color); - output.color1 = hsla_to_rgba(colors[1].color); - - // Prepare color space in vertex for avoid conversion - // in fragment shader for performance reasons - if (color_space == 1) { - // Oklab - output.color0 = srgb_to_oklab(output.color0); - output.color1 = srgb_to_oklab(output.color1); - } - } - - return output; -} - -float2x2 rotate2d(float angle) { - float s = sin(angle); - float c = cos(angle); - return float2x2(c, -s, s, c); -} - -float4 gradient_color(Background background, - float2 position, - Bounds bounds, - float4 solid_color, float4 color0, float4 color1) { - float4 color; - - switch (background.tag) { - case 0: - color = solid_color; - break; - case 1: { - // -90 degrees to match the CSS gradient angle. - float gradient_angle = background.gradient_angle_or_pattern_height; - float radians = (fmod(gradient_angle, 360.0) - 90.0) * (M_PI_F / 180.0); - float2 direction = float2(cos(radians), sin(radians)); - - // Expand the short side to be the same as the long side - if (bounds.size.x > bounds.size.y) { - direction.y *= bounds.size.y / bounds.size.x; - } else { - direction.x *= bounds.size.x / bounds.size.y; - } - - // Get the t value for the linear gradient with the color stop percentages. - float2 half_size = bounds.size * 0.5; - float2 center = bounds.origin + half_size; - float2 center_to_point = position - center; - float t = dot(center_to_point, direction) / length(direction); - // Check the direct to determine the use x or y - if (abs(direction.x) > abs(direction.y)) { - t = (t + half_size.x) / bounds.size.x; - } else { - t = (t + half_size.y) / bounds.size.y; - } - - // Adjust t based on the stop percentages - t = (t - background.colors[0].percentage) - / (background.colors[1].percentage - - background.colors[0].percentage); - t = clamp(t, 0.0, 1.0); - - switch (background.color_space) { - case 0: - color = lerp(color0, color1, t); - break; - case 1: { - float4 oklab_color = lerp(color0, color1, t); - color = oklab_to_srgb(oklab_color); - break; - } - } - - // Dither to reduce banding in gradients (especially dark/alpha). - // Triangular-distributed noise breaks up 8-bit quantization steps. - // ±2/255 for RGB (enough for dark-on-dark compositing), - // ±3/255 for alpha (needs more because alpha × dark color = tiny steps). - { - float2 seed = position * 0.6180339887; // golden ratio spread - float r1 = frac(sin(dot(seed, float2(12.9898, 78.233))) * 43758.5453); - float r2 = frac(sin(dot(seed, float2(39.3460, 11.135))) * 24634.6345); - float tri = r1 + r2 - 1.0; // triangular PDF, range [-1, +1] - color.rgb += tri * 2.0 / 255.0; - color.a += tri * 3.0 / 255.0; - } - - break; - } - case 2: { - float gradient_angle_or_pattern_height = background.gradient_angle_or_pattern_height; - float pattern_width = (gradient_angle_or_pattern_height / 65535.0f) / 255.0f; - float pattern_interval = fmod(gradient_angle_or_pattern_height, 65535.0f) / 255.0f; - float pattern_height = pattern_width + pattern_interval; - float stripe_angle = M_PI_F / 4.0; - float pattern_period = pattern_height * sin(stripe_angle); - float2x2 rotation = rotate2d(stripe_angle); - float2 relative_position = position - bounds.origin; - float2 rotated_point = mul(relative_position, rotation); - float pattern = fmod(rotated_point.x, pattern_period); - float distance = min(pattern, pattern_period - pattern) - pattern_period * (pattern_width / pattern_height) / 2.0f; - color = solid_color; - color.a *= saturate(0.5 - distance); - break; - } - case 3: { - // checkerboard - float size = background.gradient_angle_or_pattern_height; - float2 relative_position = position - bounds.origin; - - float x_index = floor(relative_position.x / size); - float y_index = floor(relative_position.y / size); - float should_be_colored = (x_index + y_index) % 2.0; - - color = solid_color; - color.a *= saturate(should_be_colored); - break; - } - } - - return color; -} - -// Returns the dash velocity of a corner given the dash velocity of the two -// sides, by returning the slower velocity (larger dashes). -// -// Since 0 is used for dash velocity when the border width is 0 (instead of -// +inf), this returns the other dash velocity in that case. -// -// An alternative to this might be to appropriately interpolate the dash -// velocity around the corner, but that seems overcomplicated. -float corner_dash_velocity(float dv1, float dv2) { - if (dv1 == 0.0) { - return dv2; - } else if (dv2 == 0.0) { - return dv1; - } else { - return min(dv1, dv2); - } -} - -// Returns alpha used to render antialiased dashes. -// `t` is within the dash when `fmod(t, period) < length`. -float dash_alpha( - float t, float period, float length, float dash_velocity, - float antialias_threshold -) { - float half_period = period / 2.0; - float half_length = length / 2.0; - // Value in [-half_period, half_period] - // The dash is in [-half_length, half_length] - float centered = fmod(t + half_period - half_length, period) - half_period; - // Signed distance for the dash, negative values are inside the dash - float signed_distance = abs(centered) - half_length; - // Antialiased alpha based on the signed distance - return saturate(antialias_threshold - signed_distance / dash_velocity); -} - -// This approximates distance to the nearest point to a quarter ellipse in a way -// that is sufficient for anti-aliasing when the ellipse is not very eccentric. -// The components of `point` are expected to be positive. -// -// Negative on the outside and positive on the inside. -float quarter_ellipse_sdf(float2 pt, float2 radii) { - // Scale the space to treat the ellipse like a unit circle - float2 circle_vec = pt / radii; - float unit_circle_sdf = length(circle_vec) - 1.0; - // Approximate up-scaling of the length by using the average of the radii. - // - // TODO: A better solution would be to use the gradient of the implicit - // function for an ellipse to approximate a scaling factor. - return unit_circle_sdf * (radii.x + radii.y) * -0.5; -} - -/* -** -** Quads -** -*/ - -struct Quad { - uint order; - uint border_style; - Bounds bounds; - ContentMask content_mask; - Background background; - Hsla border_color; - Corners corner_radii; - Edges border_widths; -}; - -struct QuadVertexOutput { - nointerpolation uint quad_id: TEXCOORD0; - float4 position: SV_Position; - nointerpolation float4 border_color: COLOR0; - nointerpolation float4 background_solid: COLOR1; - nointerpolation float4 background_color0: COLOR2; - nointerpolation float4 background_color1: COLOR3; - nointerpolation float4 clip_mask_bounds: COLOR4; - nointerpolation float4 clip_mask_radii: COLOR5; - float4 clip_distance: SV_ClipDistance; -}; - -struct QuadFragmentInput { - nointerpolation uint quad_id: TEXCOORD0; - float4 position: SV_Position; - nointerpolation float4 border_color: COLOR0; - nointerpolation float4 background_solid: COLOR1; - nointerpolation float4 background_color0: COLOR2; - nointerpolation float4 background_color1: COLOR3; - nointerpolation float4 clip_mask_bounds: COLOR4; - nointerpolation float4 clip_mask_radii: COLOR5; -}; - -StructuredBuffer quads: register(t1); - -QuadVertexOutput quad_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_InstanceID) { - float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); - uint quad_id = batch_start_index + instance_id; - Quad quad = quads[quad_id]; - float4 device_position = to_device_position(unit_vertex, quad.bounds); - - GradientColor gradient = prepare_gradient_color( - quad.background.tag, - quad.background.color_space, - quad.background.solid, - quad.background.colors - ); - float4 clip_distance = distance_from_clip_rect(unit_vertex, quad.bounds, quad.content_mask); - float4 border_color = hsla_to_rgba(quad.border_color); - - QuadVertexOutput output; - output.position = device_position; - output.border_color = border_color; - output.quad_id = quad_id; - output.background_solid = gradient.solid; - output.background_color0 = gradient.color0; - output.background_color1 = gradient.color1; - output.clip_mask_bounds = float4( - quad.content_mask.bounds.origin, quad.content_mask.bounds.size); - output.clip_mask_radii = float4( - quad.content_mask.corner_radii.top_left, - quad.content_mask.corner_radii.top_right, - quad.content_mask.corner_radii.bottom_right, - quad.content_mask.corner_radii.bottom_left); - output.clip_distance = clip_distance; - return output; -} - -float4 quad_fragment(QuadFragmentInput input): SV_Target { - Quad quad = quads[input.quad_id]; - float coverage = content_mask_coverage(input.position.xy, quad.content_mask); - if (coverage <= 0.0) { - return float4(0.0, 0.0, 0.0, 0.0); - } - float4 background_color = gradient_color(quad.background, input.position.xy, quad.bounds, - input.background_solid, input.background_color0, input.background_color1); - - bool unrounded = quad.corner_radii.top_left == 0.0 && - quad.corner_radii.top_right == 0.0 && - quad.corner_radii.bottom_left == 0.0 && - quad.corner_radii.bottom_right == 0.0; - - // Fast path when the quad is not rounded and doesn't have any border - if (quad.border_widths.top == 0.0 && - quad.border_widths.left == 0.0 && - quad.border_widths.right == 0.0 && - quad.border_widths.bottom == 0.0 && - unrounded) { - return background_color * float4(1.0, 1.0, 1.0, coverage); - } - - float2 size = quad.bounds.size; - float2 half_size = size / 2.; - float2 the_point = input.position.xy - quad.bounds.origin; - float2 center_to_point = the_point - half_size; - - // Signed distance field threshold for inclusion of pixels. 0.5 is the - // minimum distance between the center of the pixel and the edge. - const float antialias_threshold = 0.5; - - // Radius of the nearest corner - float corner_radius = pick_corner_radius(center_to_point, quad.corner_radii); - - float2 border = float2( - center_to_point.x < 0.0 ? quad.border_widths.left : quad.border_widths.right, - center_to_point.y < 0.0 ? quad.border_widths.top : quad.border_widths.bottom - ); - - // 0-width borders are reduced so that `inner_sdf >= antialias_threshold`. - // The purpose of this is to not draw antialiasing pixels in this case. - float2 reduced_border = float2( - border.x == 0.0 ? -antialias_threshold : border.x, - border.y == 0.0 ? -antialias_threshold : border.y - ); - - // Vector from the corner of the quad bounds to the point, after mirroring - // the point into the bottom right quadrant. Both components are <= 0. - float2 corner_to_point = abs(center_to_point) - half_size; - - // Vector from the point to the center of the rounded corner's circle, also - // mirrored into bottom right quadrant. - float2 corner_center_to_point = corner_to_point + corner_radius; - - // Whether the nearest point on the border is rounded - bool is_near_rounded_corner = - corner_center_to_point.x >= 0.0 && - corner_center_to_point.y >= 0.0; - - // Vector from straight border inner corner to point. - // - // 0-width borders are turned into width -1 so that inner_sdf is > 1.0 near - // the border. Without this, antialiasing pixels would be drawn. - float2 straight_border_inner_corner_to_point = corner_to_point + reduced_border; - - // Whether the point is beyond the inner edge of the straight border - bool is_beyond_inner_straight_border = - straight_border_inner_corner_to_point.x > 0.0 || - straight_border_inner_corner_to_point.y > 0.0; - - // Whether the point is far enough inside the quad, such that the pixels are - // not affected by the straight border. - bool is_within_inner_straight_border = - straight_border_inner_corner_to_point.x < -antialias_threshold && - straight_border_inner_corner_to_point.y < -antialias_threshold; - - // Fast path for points that must be part of the background - if (is_within_inner_straight_border && !is_near_rounded_corner) { - return background_color * float4(1.0, 1.0, 1.0, coverage); - } - - // Signed distance of the point to the outside edge of the quad's border - float outer_sdf = quad_sdf_impl(corner_center_to_point, corner_radius); - - // Approximate signed distance of the point to the inside edge of the quad's - // border. It is negative outside this edge (within the border), and - // positive inside. - // - // This is not always an accurate signed distance: - // * The rounded portions with varying border width use an approximation of - // nearest-point-on-ellipse. - // * When it is quickly known to be outside the edge, -1.0 is used. - float inner_sdf = 0.0; - if (corner_center_to_point.x <= 0.0 || corner_center_to_point.y <= 0.0) { - // Fast paths for straight borders - inner_sdf = -max(straight_border_inner_corner_to_point.x, - straight_border_inner_corner_to_point.y); - } else if (is_beyond_inner_straight_border) { - // Fast path for points that must be outside the inner edge - inner_sdf = -1.0; - } else if (reduced_border.x == reduced_border.y) { - // Fast path for circular inner edge. - inner_sdf = -(outer_sdf + reduced_border.x); - } else { - float2 ellipse_radii = max(float2(0.0, 0.0), float2(corner_radius, corner_radius) - reduced_border); - inner_sdf = quarter_ellipse_sdf(corner_center_to_point, ellipse_radii); - } - - // Negative when inside the border - float border_sdf = max(inner_sdf, outer_sdf); - - float4 color = background_color; - if (border_sdf < antialias_threshold) { - float4 border_color = input.border_color; - // Dashed border logic when border_style == 1 - if (quad.border_style == 1) { - // Position along the perimeter in "dash space", where each dash - // period has length 1 - float t = 0.0; - - // Total number of dash periods, so that the dash spacing can be - // adjusted to evenly divide it - float max_t = 0.0; - - // Border width is proportional to dash size. This is the behavior - // used by browsers, but also avoids dashes from different segments - // overlapping when dash size is smaller than the border width. - // - // Dash pattern: (2 * border width) dash, (1 * border width) gap - const float dash_length_per_width = 2.0; - const float dash_gap_per_width = 1.0; - const float dash_period_per_width = dash_length_per_width + dash_gap_per_width; - - // Since the dash size is determined by border width, the density of - // dashes varies. Multiplying a pixel distance by this returns a - // position in dash space - it has units (dash period / pixels). So - // a dash velocity of (1 / 10) is 1 dash every 10 pixels. - float dash_velocity = 0.0; - - // Dividing this by the border width gives the dash velocity - const float dv_numerator = 1.0 / dash_period_per_width; - - if (unrounded) { - // When corners aren't rounded, the dashes are separately laid - // out on each straight line, rather than around the whole - // perimeter. This way each line starts and ends with a dash. - bool is_horizontal = corner_center_to_point.x < corner_center_to_point.y; - // Choosing the right border width for dashed borders. - // TODO: A better solution exists taking a look at the whole file. - // this does not fix single dashed borders at the corners - float2 dashed_border = float2( - max(quad.border_widths.bottom, quad.border_widths.top), - max(quad.border_widths.right, quad.border_widths.left) - ); - float border_width = is_horizontal ? dashed_border.x : dashed_border.y; - dash_velocity = dv_numerator / border_width; - t = is_horizontal ? the_point.x : the_point.y; - t *= dash_velocity; - max_t = is_horizontal ? size.x : size.y; - max_t *= dash_velocity; - } else { - // When corners are rounded, the dashes are laid out clockwise - // around the whole perimeter. - - float r_tr = quad.corner_radii.top_right; - float r_br = quad.corner_radii.bottom_right; - float r_bl = quad.corner_radii.bottom_left; - float r_tl = quad.corner_radii.top_left; - - float w_t = quad.border_widths.top; - float w_r = quad.border_widths.right; - float w_b = quad.border_widths.bottom; - float w_l = quad.border_widths.left; - - // Straight side dash velocities - float dv_t = w_t <= 0.0 ? 0.0 : dv_numerator / w_t; - float dv_r = w_r <= 0.0 ? 0.0 : dv_numerator / w_r; - float dv_b = w_b <= 0.0 ? 0.0 : dv_numerator / w_b; - float dv_l = w_l <= 0.0 ? 0.0 : dv_numerator / w_l; - - // Straight side lengths in dash space - float s_t = (size.x - r_tl - r_tr) * dv_t; - float s_r = (size.y - r_tr - r_br) * dv_r; - float s_b = (size.x - r_br - r_bl) * dv_b; - float s_l = (size.y - r_bl - r_tl) * dv_l; - - float corner_dash_velocity_tr = corner_dash_velocity(dv_t, dv_r); - float corner_dash_velocity_br = corner_dash_velocity(dv_b, dv_r); - float corner_dash_velocity_bl = corner_dash_velocity(dv_b, dv_l); - float corner_dash_velocity_tl = corner_dash_velocity(dv_t, dv_l); - - // Corner lengths in dash space - float c_tr = r_tr * (M_PI_F / 2.0) * corner_dash_velocity_tr; - float c_br = r_br * (M_PI_F / 2.0) * corner_dash_velocity_br; - float c_bl = r_bl * (M_PI_F / 2.0) * corner_dash_velocity_bl; - float c_tl = r_tl * (M_PI_F / 2.0) * corner_dash_velocity_tl; - - // Cumulative dash space upto each segment - float upto_tr = s_t; - float upto_r = upto_tr + c_tr; - float upto_br = upto_r + s_r; - float upto_b = upto_br + c_br; - float upto_bl = upto_b + s_b; - float upto_l = upto_bl + c_bl; - float upto_tl = upto_l + s_l; - max_t = upto_tl + c_tl; - - if (is_near_rounded_corner) { - float radians = atan2(corner_center_to_point.y, corner_center_to_point.x); - float corner_t = radians * corner_radius; - - if (center_to_point.x >= 0.0) { - if (center_to_point.y < 0.0) { - dash_velocity = corner_dash_velocity_tr; - // Subtracted because radians is pi/2 to 0 when - // going clockwise around the top right corner, - // since the y axis has been flipped - t = upto_r - corner_t * dash_velocity; - } else { - dash_velocity = corner_dash_velocity_br; - // Added because radians is 0 to pi/2 when going - // clockwise around the bottom-right corner - t = upto_br + corner_t * dash_velocity; - } - } else { - if (center_to_point.y >= 0.0) { - dash_velocity = corner_dash_velocity_bl; - // Subtracted because radians is pi/1 to 0 when - // going clockwise around the bottom-left corner, - // since the x axis has been flipped - t = upto_l - corner_t * dash_velocity; - } else { - dash_velocity = corner_dash_velocity_tl; - // Added because radians is 0 to pi/2 when going - // clockwise around the top-left corner, since both - // axis were flipped - t = upto_tl + corner_t * dash_velocity; - } - } - } else { - // Straight borders - bool is_horizontal = corner_center_to_point.x < corner_center_to_point.y; - if (is_horizontal) { - if (center_to_point.y < 0.0) { - dash_velocity = dv_t; - t = (the_point.x - r_tl) * dash_velocity; - } else { - dash_velocity = dv_b; - t = upto_bl - (the_point.x - r_bl) * dash_velocity; - } - } else { - if (center_to_point.x < 0.0) { - dash_velocity = dv_l; - t = upto_tl - (the_point.y - r_tl) * dash_velocity; - } else { - dash_velocity = dv_r; - t = upto_r + (the_point.y - r_tr) * dash_velocity; - } - } - } - } - float dash_length = dash_length_per_width / dash_period_per_width; - float desired_dash_gap = dash_gap_per_width / dash_period_per_width; - - // Straight borders should start and end with a dash, so max_t is - // reduced to cause this. - max_t -= unrounded ? dash_length : 0.0; - if (max_t >= 1.0) { - // Adjust dash gap to evenly divide max_t - float dash_count = floor(max_t); - float dash_period = max_t / dash_count; - border_color.a *= dash_alpha(t, dash_period, dash_length, dash_velocity, antialias_threshold); - } else if (unrounded) { - // When there isn't enough space for the full gap between the - // two start / end dashes of a straight border, reduce gap to - // make them fit. - float dash_gap = max_t - dash_length; - if (dash_gap > 0.0) { - float dash_period = dash_length + dash_gap; - border_color.a *= dash_alpha(t, dash_period, dash_length, dash_velocity, antialias_threshold); - } - } - } - - // Blend the border on top of the background and then linearly interpolate - // between the two as we slide inside the background. - float4 blended_border = over(background_color, border_color); - color = lerp(background_color, blended_border, - saturate(antialias_threshold - inner_sdf)); - } - - return color * float4(1.0, 1.0, 1.0, min(coverage, saturate(antialias_threshold - outer_sdf))); -} - -/* -** -** Shadows -** -*/ - -struct Shadow { - uint order; - float blur_radius; - Bounds bounds; - Corners corner_radii; - ContentMask content_mask; - Hsla color; - Bounds element_bounds; - Corners element_corner_radii; - uint inset; - uint pad; // align to 8 bytes -}; - -struct ShadowVertexOutput { - nointerpolation uint shadow_id: TEXCOORD0; - float4 position: SV_Position; - nointerpolation float4 color: COLOR; - float4 clip_distance: SV_ClipDistance; -}; - -struct ShadowFragmentInput { - nointerpolation uint shadow_id: TEXCOORD0; - float4 position: SV_Position; - nointerpolation float4 color: COLOR; -}; - -StructuredBuffer shadows: register(t1); - -ShadowVertexOutput shadow_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_InstanceID) { - float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); - uint shadow_id = batch_start_index + instance_id; - Shadow shadow = shadows[shadow_id]; - - Bounds bounds; - if (shadow.inset != 0u) { - bounds = shadow.element_bounds; - } else { - // Leave room for the gaussian tail outside the shadow rect. - float margin = 3.0 * shadow.blur_radius; - bounds = shadow.bounds; - bounds.origin -= margin; - bounds.size += 2.0 * margin; - } - - float4 device_position = to_device_position(unit_vertex, bounds); - float4 clip_distance = distance_from_clip_rect(unit_vertex, bounds, shadow.content_mask); - float4 color = hsla_to_rgba(shadow.color); - - ShadowVertexOutput output; - output.position = device_position; - output.color = color; - output.shadow_id = shadow_id; - output.clip_distance = clip_distance; - - return output; -} - -float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { - Shadow shadow = shadows[input.shadow_id]; - float coverage = content_mask_coverage(input.position.xy, shadow.content_mask); - if (coverage <= 0.0) { - return float4(0.0, 0.0, 0.0, 0.0); - } - - float2 half_size = shadow.bounds.size / 2.; - float2 center = shadow.bounds.origin + half_size; - float2 point0 = input.position.xy - center; - float corner_radius = pick_corner_radius(point0, shadow.corner_radii); - - float alpha; - if (shadow.blur_radius == 0.) { - float distance = quad_sdf(input.position.xy, shadow.bounds, shadow.corner_radii); - alpha = saturate(0.5 - distance); - } else { - // The signal is only non-zero in a limited range, so don't waste samples - float low = point0.y - half_size.y; - float high = point0.y + half_size.y; - float start = clamp(-3. * shadow.blur_radius, low, high); - float end = clamp(3. * shadow.blur_radius, low, high); - - // Accumulate samples (we can get away with surprisingly few samples) - float step = (end - start) / 4.; - float y = start + step * 0.5; - alpha = 0.; - for (int i = 0; i < 4; i++) { - alpha += blur_along_x(point0.x, point0.y - y, shadow.blur_radius, - corner_radius, half_size) * - gaussian(y, shadow.blur_radius) * step; - y += step; - } - } - - if (shadow.inset != 0u) { - // The inset shadow is the complement of the (blurred) hole rect, clipped to the element. - // `saturate(0.5 - d)` gives a 1-pixel antialiased edge: d <= -0.5 -> 1, d >= 0.5 -> 0. - alpha = 1.0 - alpha; - float element_distance = quad_sdf(input.position.xy, shadow.element_bounds, - shadow.element_corner_radii); - alpha *= saturate(0.5 - element_distance); - } - - return input.color * float4(1., 1., 1., alpha * coverage); -} - -/* -** -** Path Rasterization -** -*/ - -struct PathRasterizationSprite { - float2 xy_position; - float2 st_position; - Background color; - Bounds bounds; - ContentMask content_mask; -}; - -StructuredBuffer path_rasterization_sprites: register(t1); - -struct PathVertexOutput { - float4 position: SV_Position; - float2 st_position: TEXCOORD0; - nointerpolation uint vertex_id: TEXCOORD1; - float4 clip_distance: SV_ClipDistance; -}; - -struct PathFragmentInput { - float4 position: SV_Position; - float2 st_position: TEXCOORD0; - nointerpolation uint vertex_id: TEXCOORD1; -}; - -PathVertexOutput path_rasterization_vertex(uint vertex_id: SV_VertexID) { - PathRasterizationSprite sprite = path_rasterization_sprites[vertex_id]; - - PathVertexOutput output; - output.position = to_device_position_impl(sprite.xy_position); - output.st_position = sprite.st_position; - output.vertex_id = vertex_id; - output.clip_distance = distance_from_clip_rect_impl(sprite.xy_position, sprite.bounds); - - return output; -} - -float4 path_rasterization_fragment(PathFragmentInput input): SV_Target { - float2 dx = ddx(input.st_position); - float2 dy = ddy(input.st_position); - PathRasterizationSprite sprite = path_rasterization_sprites[input.vertex_id]; - float coverage = content_mask_coverage(input.position.xy, sprite.content_mask); - if (coverage <= 0.0) { - return float4(0.0, 0.0, 0.0, 0.0); - } - - Background background = sprite.color; - Bounds bounds = sprite.bounds; - - float alpha; - if (length(float2(dx.x, dy.x))) { - alpha = 1.0; - } else { - float2 gradient = 2.0 * input.st_position.xx * float2(dx.x, dy.x) - float2(dx.y, dy.y); - float f = input.st_position.x * input.st_position.x - input.st_position.y; - float distance = f / length(gradient); - alpha = saturate(0.5 - distance); - } - - GradientColor gradient = prepare_gradient_color( - background.tag, background.color_space, background.solid, background.colors); - - float4 color = gradient_color(background, input.position.xy, bounds, - gradient.solid, gradient.color0, gradient.color1); - return float4(color.rgb * color.a * alpha, alpha * color.a) * coverage; -} - -/* -** -** Path Sprites -** -*/ - -struct PathSprite { - Bounds bounds; -}; - -struct PathSpriteVertexOutput { - float4 position: SV_Position; - float2 texture_coords: TEXCOORD0; -}; - -StructuredBuffer path_sprites: register(t1); - -PathSpriteVertexOutput path_sprite_vertex(uint vertex_id: SV_VertexID, uint sprite_id: SV_InstanceID) { - float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); - PathSprite sprite = path_sprites[sprite_id]; - - // Don't apply content mask because it was already accounted for when rasterizing the path - float4 device_position = to_device_position(unit_vertex, sprite.bounds); - - float2 screen_position = sprite.bounds.origin + unit_vertex * sprite.bounds.size; - float2 texture_coords = screen_position / global_viewport_size; - - PathSpriteVertexOutput output; - output.position = device_position; - output.texture_coords = texture_coords; - return output; -} - -float4 path_sprite_fragment(PathSpriteVertexOutput input): SV_Target { - return t_sprite.Sample(s_sprite, input.texture_coords); -} - -/* -** -** Underlines -** -*/ - -struct Underline { - uint order; - uint pad; - Bounds bounds; - ContentMask content_mask; - Hsla color; - float thickness; - uint wavy; -}; - -struct UnderlineVertexOutput { - nointerpolation uint underline_id: TEXCOORD0; - float4 position: SV_Position; - nointerpolation float4 color: COLOR; - float4 clip_distance: SV_ClipDistance; -}; - -struct UnderlineFragmentInput { - nointerpolation uint underline_id: TEXCOORD0; - float4 position: SV_Position; - nointerpolation float4 color: COLOR; -}; - -StructuredBuffer underlines: register(t1); - -UnderlineVertexOutput underline_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_InstanceID) { - float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); - uint underline_id = batch_start_index + instance_id; - Underline underline = underlines[underline_id]; - float4 device_position = to_device_position(unit_vertex, underline.bounds); - float4 clip_distance = distance_from_clip_rect(unit_vertex, underline.bounds, - underline.content_mask); - float4 color = hsla_to_rgba(underline.color); - - UnderlineVertexOutput output; - output.position = device_position; - output.color = color; - output.underline_id = underline_id; - output.clip_distance = clip_distance; - return output; -} - -float4 underline_fragment(UnderlineFragmentInput input): SV_Target { - const float WAVE_FREQUENCY = 2.0; - const float WAVE_HEIGHT_RATIO = 0.8; - - Underline underline = underlines[input.underline_id]; - float coverage = content_mask_coverage(input.position.xy, underline.content_mask); - if (coverage <= 0.0) { - return float4(0.0, 0.0, 0.0, 0.0); - } - if (underline.wavy) { - float half_thickness = underline.thickness * 0.5; - float2 origin = underline.bounds.origin; - - float2 st = ((input.position.xy - origin) / underline.bounds.size.y) - float2(0., 0.5); - float frequency = (M_PI_F * WAVE_FREQUENCY * underline.thickness) / underline.bounds.size.y; - float amplitude = (underline.thickness * WAVE_HEIGHT_RATIO) / underline.bounds.size.y; - - 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 * underline.bounds.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 * coverage); - } else { - return input.color * float4(1., 1., 1., coverage); - } -} - -/* -** -** Monochrome sprites -** -*/ - -struct MonochromeSprite { - uint order; - uint pad; - Bounds bounds; - ContentMask content_mask; - Hsla color; - AtlasTile tile; - TransformationMatrix transformation; -}; - -struct MonochromeSpriteVertexOutput { - nointerpolation uint sprite_id: TEXCOORD0; - float4 position: SV_Position; - float2 tile_position: POSITION0; - nointerpolation float4 color: COLOR; - float4 clip_distance: SV_ClipDistance; -}; - -struct MonochromeSpriteFragmentInput { - nointerpolation uint sprite_id: TEXCOORD0; - float4 position: SV_Position; - float2 tile_position: POSITION0; - nointerpolation float4 color: COLOR; - float4 clip_distance: SV_ClipDistance; -}; - -StructuredBuffer mono_sprites: register(t1); - -MonochromeSpriteVertexOutput monochrome_sprite_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_InstanceID) { - float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); - uint sprite_id = batch_start_index + instance_id; - MonochromeSprite sprite = mono_sprites[sprite_id]; - float4 device_position = - to_device_position_transformed(unit_vertex, sprite.bounds, sprite.transformation); - float4 clip_distance = distance_from_clip_rect_transformed(unit_vertex, sprite.bounds, sprite.content_mask, sprite.transformation); - float2 tile_position = to_tile_position(unit_vertex, sprite.tile); - float4 color = hsla_to_rgba(sprite.color); - - MonochromeSpriteVertexOutput output; - output.position = device_position; - output.sprite_id = sprite_id; - output.tile_position = tile_position; - output.color = color; - output.clip_distance = clip_distance; - return output; -} - -float4 monochrome_sprite_fragment(MonochromeSpriteFragmentInput input): SV_Target { - MonochromeSprite sprite = mono_sprites[input.sprite_id]; - float coverage = content_mask_coverage(input.position.xy, sprite.content_mask); - if (coverage <= 0.0) { - return float4(0.0, 0.0, 0.0, 0.0); - } - float sample = t_sprite.Sample(s_sprite, input.tile_position).r; - float alpha_corrected = apply_contrast_and_gamma_correction(sample, input.color.rgb, grayscale_enhanced_contrast, gamma_ratios); - return float4(input.color.rgb, input.color.a * alpha_corrected * coverage); -} - -MonochromeSpriteVertexOutput subpixel_sprite_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_InstanceID) { - return monochrome_sprite_vertex(vertex_id, instance_id); -} - -SubpixelSpriteFragmentOutput subpixel_sprite_fragment(MonochromeSpriteFragmentInput input) { - MonochromeSprite sprite = mono_sprites[input.sprite_id]; - float coverage = content_mask_coverage(input.position.xy, sprite.content_mask); - if (coverage <= 0.0) { - SubpixelSpriteFragmentOutput empty; - empty.foreground = float4(0.0, 0.0, 0.0, 0.0); - empty.alpha = float4(0.0, 0.0, 0.0, 0.0); - return empty; - } - float3 sample = t_sprite.Sample(s_sprite, input.tile_position).rgb; - if (is_bgr) { - sample = sample.bgr; - } - float3 alpha_corrected = apply_contrast_and_gamma_correction3(sample, input.color.rgb, subpixel_enhanced_contrast, gamma_ratios); - - SubpixelSpriteFragmentOutput output; - output.foreground = float4(input.color.rgb, 1.0f); - output.alpha = float4(input.color.a * alpha_corrected * coverage, coverage); - return output; -} - -/* -** -** Polychrome sprites -** -*/ - -struct PolychromeSprite { - uint order; - uint pad; - uint grayscale; - float opacity; - Bounds bounds; - ContentMask content_mask; - Corners corner_radii; - AtlasTile tile; -}; - -struct PolychromeSpriteVertexOutput { - nointerpolation uint sprite_id: TEXCOORD0; - float4 position: SV_Position; - float2 tile_position: POSITION; - float4 clip_distance: SV_ClipDistance; -}; - -struct PolychromeSpriteFragmentInput { - nointerpolation uint sprite_id: TEXCOORD0; - float4 position: SV_Position; - float2 tile_position: POSITION; -}; - -StructuredBuffer poly_sprites: register(t1); - -PolychromeSpriteVertexOutput polychrome_sprite_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_InstanceID) { - float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); - uint sprite_id = batch_start_index + instance_id; - PolychromeSprite sprite = poly_sprites[sprite_id]; - float4 device_position = to_device_position(unit_vertex, sprite.bounds); - float4 clip_distance = distance_from_clip_rect(unit_vertex, sprite.bounds, - sprite.content_mask); - float2 tile_position = to_tile_position(unit_vertex, sprite.tile); - - PolychromeSpriteVertexOutput output; - output.position = device_position; - output.tile_position = tile_position; - output.sprite_id = sprite_id; - output.clip_distance = clip_distance; - return output; -} - -float4 polychrome_sprite_fragment(PolychromeSpriteFragmentInput input): SV_Target { - PolychromeSprite sprite = poly_sprites[input.sprite_id]; - float coverage = content_mask_coverage(input.position.xy, sprite.content_mask); - if (coverage <= 0.0) { - return float4(0.0, 0.0, 0.0, 0.0); - } - float4 sample = t_sprite.Sample(s_sprite, input.tile_position); - float distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii); - - float4 color = sample; - if (sprite.grayscale != 0u) { - float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS); - color = float4(grayscale, sample.a); - } - color.a *= sprite.opacity * min(coverage, saturate(0.5 - distance)); - return color; -} diff --git a/crates/gpui_pre_windows/src/system_notifications.rs b/crates/gpui_pre_windows/src/system_notifications.rs deleted file mode 100644 index b0075f0e..00000000 --- a/crates/gpui_pre_windows/src/system_notifications.rs +++ /dev/null @@ -1,198 +0,0 @@ -//! System notifications as Windows toast notifications. - -use std::cell::RefCell; -use std::collections::{HashMap, hash_map::DefaultHasher}; -use std::hash::{Hash as _, Hasher as _}; -use std::rc::Rc; - -use futures::StreamExt as _; -use futures::channel::mpsc; -use gpui::{ - ForegroundExecutor, SharedString, SystemNotification, SystemNotificationResponse, Task, -}; -use windows::Data::Xml::Dom::XmlDocument; -use windows::Foundation::TypedEventHandler; -use windows::UI::Notifications::{ - ToastActivatedEventArgs, ToastNotification, ToastNotificationManager, ToastNotifier, -}; -use windows::core::{IInspectable, Interface as _, h}; - -type ResponseCallback = Rc>>>; - -pub(crate) struct SystemNotificationState { - notifier: Option, - active_toasts: HashMap, - response_sender: mpsc::UnboundedSender, - response_receiver: Option>, - callback: ResponseCallback, - _response_task: Option>, -} - -impl SystemNotificationState { - pub(crate) fn new() -> Self { - let (response_sender, response_receiver) = mpsc::unbounded(); - Self { - notifier: None, - active_toasts: HashMap::new(), - response_sender, - response_receiver: Some(response_receiver), - callback: Rc::new(RefCell::new(None)), - _response_task: None, - } - } - - pub(crate) fn show( - &mut self, - has_package_identity: bool, - app_identity: Option<(&str, &str)>, - notification: SystemNotification, - ) -> windows::core::Result<()> { - let Some(notifier) = self.notifier(has_package_identity, app_identity)? else { - return Ok(()); - }; - - let document = toast_document(¬ification)?; - let toast = ToastNotification::CreateToastNotification(&document)?; - // Windows caps toast tags at 64 characters (post-Creators Update), - // so hash the arbitrary GPUI tag down to a fixed-width value. - let tag = { - let mut hasher = DefaultHasher::new(); - notification.tag.hash(&mut hasher); - format!("{:016x}", hasher.finish()) - }; - toast.SetTag(&tag.into())?; - - let sender = self.response_sender.clone(); - let response_tag = notification.tag.clone(); - toast.Activated(&TypedEventHandler::::new( - move |_sender, arguments| { - let action_id = arguments - .as_ref() - .and_then(|arguments| arguments.cast::().ok()) - .and_then(|arguments| arguments.Arguments().ok()) - .filter(|arguments| !arguments.is_empty()) - .map(|arguments| SharedString::from(arguments.to_string())); - sender - .unbounded_send(SystemNotificationResponse { - tag: response_tag.clone(), - action_id, - }) - .ok(); - Ok(()) - }, - ))?; - - if let Some(previous) = self.active_toasts.remove(¬ification.tag) { - notifier.Hide(&previous)?; - } - notifier.Show(&toast)?; - self.active_toasts.insert(notification.tag, toast); - Ok(()) - } - - pub(crate) fn dismiss(&mut self, tag: &str) { - let Some(toast) = self.active_toasts.remove(tag) else { - return; - }; - let Some(notifier) = &self.notifier else { - return; - }; - if let Err(error) = notifier.Hide(&toast) { - log::warn!("failed to dismiss system notification: {error}"); - } - } - - pub(crate) fn on_response( - &mut self, - executor: &ForegroundExecutor, - callback: Box, - ) { - *self.callback.borrow_mut() = Some(callback); - - if let Some(mut receiver) = self.response_receiver.take() { - let callback = self.callback.clone(); - self._response_task = Some(executor.spawn(async move { - while let Some(response) = receiver.next().await { - // Take the callback out for the call: it may re-enter the - // platform (e.g. to dismiss the notification it was told - // about) or replace itself. - let taken = callback.borrow_mut().take(); - if let Some(mut taken) = taken { - taken(response); - callback.borrow_mut().get_or_insert(taken); - } - } - })); - } - } - - fn notifier( - &mut self, - has_package_identity: bool, - app_identity: Option<(&str, &str)>, - ) -> windows::core::Result> { - if let Some(notifier) = &self.notifier { - return Ok(Some(notifier.clone())); - } - - let notifier = if has_package_identity { - ToastNotificationManager::CreateToastNotifier()? - } else { - let Some((app_identifier, app_name)) = app_identity else { - log::warn!( - "cannot show a system notification without an app identity; \ - call `App::set_app_identity` during startup" - ); - return Ok(None); - }; - register_app_user_model_id(app_identifier, app_name); - ToastNotificationManager::CreateToastNotifierWithId(&app_identifier.into())? - }; - - self.notifier = Some(notifier.clone()); - Ok(Some(notifier)) - } -} - -fn toast_document(notification: &SystemNotification) -> windows::core::Result { - let document = XmlDocument::new()?; - let toast = document.CreateElement(h!("toast"))?; - document.AppendChild(&toast)?; - - let visual = document.CreateElement(h!("visual"))?; - toast.AppendChild(&visual)?; - let binding = document.CreateElement(h!("binding"))?; - binding.SetAttribute(h!("template"), h!("ToastGeneric"))?; - visual.AppendChild(&binding)?; - for text in [¬ification.title, ¬ification.body] { - let element = document.CreateElement(h!("text"))?; - element.SetInnerText(&text.as_ref().into())?; - binding.AppendChild(&element)?; - } - - if !notification.actions.is_empty() { - let actions = document.CreateElement(h!("actions"))?; - toast.AppendChild(&actions)?; - for action in ¬ification.actions { - let action_element = document.CreateElement(h!("action"))?; - action_element.SetAttribute(h!("content"), &action.label.as_ref().into())?; - action_element.SetAttribute(h!("arguments"), &action.id.as_ref().into())?; - actions.AppendChild(&action_element)?; - } - } - - let audio = document.CreateElement(h!("audio"))?; - audio.SetAttribute(h!("silent"), h!("true"))?; - toast.AppendChild(&audio)?; - Ok(document) -} - -/// Registers the app's AUMID so toasts display correctly for an unpackaged app without a Start Menu shortcut. -fn register_app_user_model_id(app_identifier: &str, app_name: &str) { - let result = windows_registry::CURRENT_USER - .create(format!(r"Software\Classes\AppUserModelId\{app_identifier}")) - .and_then(|key| key.set_string("DisplayName", app_name)); - if let Err(error) = result { - log::warn!("failed to register AppUserModelID; notifications may not display: {error}"); - } -} diff --git a/crates/gpui_pre_windows/src/system_settings.rs b/crates/gpui_pre_windows/src/system_settings.rs deleted file mode 100644 index 8cce544b..00000000 --- a/crates/gpui_pre_windows/src/system_settings.rs +++ /dev/null @@ -1,87 +0,0 @@ -use std::{ - cell::Cell, - ffi::{c_uint, c_void}, -}; - -use gpui_util::ResultExt; -use windows::Win32::UI::WindowsAndMessaging::{ - SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SYSTEM_PARAMETERS_INFO_ACTION, - SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, SystemParametersInfoW, -}; - -/// Windows settings pulled from SystemParametersInfo -/// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow -#[derive(Default, Debug, Clone)] -pub(crate) struct WindowsSystemSettings { - pub(crate) mouse_wheel_settings: MouseWheelSettings, -} - -#[derive(Default, Debug, Clone)] -pub(crate) struct MouseWheelSettings { - /// SEE: SPI_GETWHEELSCROLLCHARS - pub(crate) wheel_scroll_chars: Cell, - /// SEE: SPI_GETWHEELSCROLLLINES - pub(crate) wheel_scroll_lines: Cell, -} - -impl WindowsSystemSettings { - pub(crate) fn new() -> Self { - let mut settings = Self::default(); - settings.init(); - settings - } - - fn init(&mut self) { - self.mouse_wheel_settings.update(); - } - - pub(crate) fn update(&self, wparam: usize) { - match SYSTEM_PARAMETERS_INFO_ACTION(wparam as u32) { - SPI_GETWHEELSCROLLLINES | SPI_GETWHEELSCROLLCHARS => self.update_mouse_wheel_settings(), - _ => {} - } - } - - fn update_mouse_wheel_settings(&self) { - self.mouse_wheel_settings.update(); - } -} - -impl MouseWheelSettings { - fn update(&self) { - self.update_wheel_scroll_chars(); - self.update_wheel_scroll_lines(); - } - - fn update_wheel_scroll_chars(&self) { - let mut value = c_uint::default(); - let result = unsafe { - SystemParametersInfoW( - SPI_GETWHEELSCROLLCHARS, - 0, - Some((&mut value) as *mut c_uint as *mut c_void), - SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS::default(), - ) - }; - - if result.log_err() != None && self.wheel_scroll_chars.get() != value { - self.wheel_scroll_chars.set(value); - } - } - - fn update_wheel_scroll_lines(&self) { - let mut value = c_uint::default(); - let result = unsafe { - SystemParametersInfoW( - SPI_GETWHEELSCROLLLINES, - 0, - Some((&mut value) as *mut c_uint as *mut c_void), - SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS::default(), - ) - }; - - if result.log_err() != None && self.wheel_scroll_lines.get() != value { - self.wheel_scroll_lines.set(value); - } - } -} diff --git a/crates/gpui_pre_windows/src/util.rs b/crates/gpui_pre_windows/src/util.rs deleted file mode 100644 index b3badd84..00000000 --- a/crates/gpui_pre_windows/src/util.rs +++ /dev/null @@ -1,191 +0,0 @@ -use std::sync::OnceLock; - -use anyhow::Context; -use gpui_util::ResultExt; -use windows::{ - UI::{ - Color, - ViewManagement::{UIColorType, UISettings}, - }, - Win32::{ - Foundation::*, Graphics::Dwm::*, System::LibraryLoader::LoadLibraryA, - UI::WindowsAndMessaging::*, - }, - core::{BOOL, PCSTR}, -}; - -use crate::*; -use gpui::*; - -pub(crate) trait HiLoWord { - fn hiword(&self) -> u16; - fn loword(&self) -> u16; - fn signed_hiword(&self) -> i16; - fn signed_loword(&self) -> i16; -} - -impl HiLoWord for WPARAM { - fn hiword(&self) -> u16 { - ((self.0 >> 16) & 0xFFFF) as u16 - } - - fn loword(&self) -> u16 { - (self.0 & 0xFFFF) as u16 - } - - fn signed_hiword(&self) -> i16 { - ((self.0 >> 16) & 0xFFFF) as i16 - } - - fn signed_loword(&self) -> i16 { - (self.0 & 0xFFFF) as i16 - } -} - -impl HiLoWord for LPARAM { - fn hiword(&self) -> u16 { - ((self.0 >> 16) & 0xFFFF) as u16 - } - - fn loword(&self) -> u16 { - (self.0 & 0xFFFF) as u16 - } - - fn signed_hiword(&self) -> i16 { - ((self.0 >> 16) & 0xFFFF) as i16 - } - - fn signed_loword(&self) -> i16 { - (self.0 & 0xFFFF) as i16 - } -} - -pub(crate) unsafe fn get_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX) -> isize { - #[cfg(target_pointer_width = "64")] - unsafe { - GetWindowLongPtrW(hwnd, nindex) - } - #[cfg(target_pointer_width = "32")] - unsafe { - GetWindowLongW(hwnd, nindex) as isize - } -} - -pub(crate) unsafe fn set_window_long( - hwnd: HWND, - nindex: WINDOW_LONG_PTR_INDEX, - dwnewlong: isize, -) -> isize { - #[cfg(target_pointer_width = "64")] - unsafe { - SetWindowLongPtrW(hwnd, nindex, dwnewlong) - } - #[cfg(target_pointer_width = "32")] - unsafe { - SetWindowLongW(hwnd, nindex, dwnewlong as i32) as isize - } -} - -pub(crate) fn windows_credentials_target_name(url: &str) -> String { - format!("zed:url={}", url) -} - -pub(crate) fn load_cursor(style: CursorStyle) -> Option { - static ARROW: OnceLock = OnceLock::new(); - static IBEAM: OnceLock = OnceLock::new(); - static CROSS: OnceLock = OnceLock::new(); - static HAND: OnceLock = OnceLock::new(); - static SIZEWE: OnceLock = OnceLock::new(); - static SIZENS: OnceLock = OnceLock::new(); - static SIZENWSE: OnceLock = OnceLock::new(); - static SIZENESW: OnceLock = OnceLock::new(); - static NO: OnceLock = OnceLock::new(); - let (lock, name) = match style { - CursorStyle::IBeam | CursorStyle::IBeamCursorForVerticalLayout => (&IBEAM, IDC_IBEAM), - CursorStyle::Crosshair => (&CROSS, IDC_CROSS), - CursorStyle::PointingHand | CursorStyle::DragLink => (&HAND, IDC_HAND), - CursorStyle::ResizeLeft - | CursorStyle::ResizeRight - | CursorStyle::ResizeLeftRight - | CursorStyle::ResizeColumn => (&SIZEWE, IDC_SIZEWE), - CursorStyle::ResizeUp - | CursorStyle::ResizeDown - | CursorStyle::ResizeUpDown - | CursorStyle::ResizeRow => (&SIZENS, IDC_SIZENS), - CursorStyle::ResizeUpLeftDownRight => (&SIZENWSE, IDC_SIZENWSE), - CursorStyle::ResizeUpRightDownLeft => (&SIZENESW, IDC_SIZENESW), - CursorStyle::OperationNotAllowed => (&NO, IDC_NO), - _ => (&ARROW, IDC_ARROW), - }; - Some( - *(*lock.get_or_init(|| { - HCURSOR( - unsafe { LoadImageW(None, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED) } - .log_err() - .unwrap_or_default() - .0, - ) - .into() - })), - ) -} - -/// This function is used to configure the dark mode for the window built-in title bar. -pub(crate) fn configure_dwm_dark_mode(hwnd: HWND, appearance: WindowAppearance) { - let dark_mode_enabled: BOOL = match appearance { - WindowAppearance::Dark | WindowAppearance::VibrantDark => true.into(), - WindowAppearance::Light | WindowAppearance::VibrantLight => false.into(), - }; - unsafe { - DwmSetWindowAttribute( - hwnd, - DWMWA_USE_IMMERSIVE_DARK_MODE, - &dark_mode_enabled as *const _ as _, - std::mem::size_of::() as u32, - ) - .log_err(); - } -} - -#[inline] -pub(crate) fn logical_point(x: f32, y: f32, scale_factor: f32) -> Point { - Point { - x: px(x / scale_factor), - y: px(y / scale_factor), - } -} - -// https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-windows-themes -#[inline] -pub(crate) fn system_appearance() -> Result { - let ui_settings = UISettings::new()?; - let foreground_color = ui_settings.GetColorValue(UIColorType::Foreground)?; - // If the foreground is light, then is_color_light will evaluate to true, - // meaning Dark mode is enabled. - if is_color_light(&foreground_color) { - Ok(WindowAppearance::Dark) - } else { - Ok(WindowAppearance::Light) - } -} - -#[inline(always)] -fn is_color_light(color: &Color) -> bool { - ((5 * color.G as u32) + (2 * color.R as u32) + color.B as u32) > (8 * 128) -} - -pub(crate) fn with_dll_library(dll_name: PCSTR, f: F) -> Result -where - F: FnOnce(HMODULE) -> Result, -{ - let library = unsafe { - LoadLibraryA(dll_name).with_context(|| format!("Loading dll: {}", dll_name.display()))? - }; - let result = f(library); - unsafe { - FreeLibrary(library) - .with_context(|| format!("Freeing dll: {}", dll_name.display())) - .log_err(); - } - result -} diff --git a/crates/gpui_pre_windows/src/vsync.rs b/crates/gpui_pre_windows/src/vsync.rs deleted file mode 100644 index 51fbdf6c..00000000 --- a/crates/gpui_pre_windows/src/vsync.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::{ - sync::LazyLock, - time::{Duration, Instant}, -}; - -use anyhow::{Context, Result}; -use gpui_util::ResultExt; -use windows::Win32::{ - Foundation::HWND, - Graphics::Dwm::{DWM_TIMING_INFO, DwmFlush, DwmGetCompositionTimingInfo}, - System::Performance::QueryPerformanceFrequency, -}; - -static QPC_TICKS_PER_SECOND: LazyLock = LazyLock::new(|| { - let mut frequency = 0; - // On systems that run Windows XP or later, the function will always succeed and - // will thus never return zero. - unsafe { QueryPerformanceFrequency(&mut frequency).unwrap() }; - frequency as u64 -}); - -const VSYNC_INTERVAL_THRESHOLD: Duration = Duration::from_millis(1); -const DEFAULT_VSYNC_INTERVAL: Duration = Duration::from_micros(16_666); // ~60Hz - -pub(crate) struct VSyncProvider { - interval: Duration, - f: Box bool>, -} - -impl VSyncProvider { - pub(crate) fn new() -> Self { - let interval = get_dwm_interval() - .context("Failed to get DWM interval") - .log_err() - .unwrap_or(DEFAULT_VSYNC_INTERVAL); - let f = Box::new(|| unsafe { DwmFlush().is_ok() }); - Self { interval, f } - } - - pub(crate) fn wait_for_vsync(&self) { - let vsync_start = Instant::now(); - let wait_succeeded = (self.f)(); - let elapsed = vsync_start.elapsed(); - // DwmFlush and DCompositionWaitForCompositorClock returns very early - // instead of waiting until vblank when the monitor goes to sleep or is - // unplugged (nothing to present due to desktop occlusion). We use 1ms as - // a threshold for the duration of the wait functions and fallback to - // Sleep() if it returns before that. This could happen during normal - // operation for the first call after the vsync thread becomes non-idle, - // but it shouldn't happen often. - if !wait_succeeded || elapsed < VSYNC_INTERVAL_THRESHOLD { - log::trace!("VSyncProvider::wait_for_vsync() took less time than expected"); - std::thread::sleep(self.interval); - } - } -} - -fn get_dwm_interval() -> Result { - let mut timing_info = DWM_TIMING_INFO { - cbSize: std::mem::size_of::() as u32, - ..Default::default() - }; - unsafe { DwmGetCompositionTimingInfo(HWND::default(), &mut timing_info) }?; - let interval = retrieve_duration(timing_info.qpcRefreshPeriod, *QPC_TICKS_PER_SECOND); - // Check for interval values that are impossibly low. A 29 microsecond - // interval was seen (from a qpcRefreshPeriod of 60). - if interval < VSYNC_INTERVAL_THRESHOLD { - Ok(retrieve_duration( - timing_info.rateRefresh.uiDenominator as u64, - timing_info.rateRefresh.uiNumerator as u64, - )) - } else { - Ok(interval) - } -} - -#[inline] -fn retrieve_duration(counts: u64, ticks_per_second: u64) -> Duration { - let ticks_per_microsecond = ticks_per_second / 1_000_000; - Duration::from_micros(counts / ticks_per_microsecond) -} diff --git a/crates/gpui_pre_windows/src/window.rs b/crates/gpui_pre_windows/src/window.rs deleted file mode 100644 index 37e9d9a1..00000000 --- a/crates/gpui_pre_windows/src/window.rs +++ /dev/null @@ -1,1687 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] - -use std::{ - cell::{Cell, RefCell}, - num::NonZeroIsize, - path::PathBuf, - rc::{Rc, Weak}, - str::FromStr, - sync::{Arc, Once, atomic::AtomicBool}, - time::{Duration, Instant}, -}; - -use anyhow::{Context as _, Result}; -use futures::channel::oneshot::{self, Receiver}; -use gpui_util::ResultExt; -use raw_window_handle as rwh; -use smallvec::SmallVec; -use windows::{ - Win32::{ - Foundation::*, - Graphics::Dwm::*, - Graphics::Gdi::*, - System::{ - Com::*, Diagnostics::Debug::MessageBeep, LibraryLoader::*, Ole::*, SystemServices::*, - }, - UI::{Controls::*, HiDpi::*, Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*}, - }, - core::*, -}; - -use crate::direct_manipulation::DirectManipulationHandler; -use crate::*; -use gpui::*; - -pub(crate) struct WindowsWindow(pub Rc); - -impl std::ops::Deref for WindowsWindow { - type Target = WindowsWindowInner; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -pub struct WindowsWindowState { - pub origin: Cell>, - pub logical_size: Cell>, - pub min_size: Option>, - pub fullscreen_restore_bounds: Cell>, - pub border_offset: WindowBorderOffset, - pub appearance: Cell, - pub background_appearance: Cell, - pub scale_factor: Cell, - pub restore_from_minimized: Cell>>, - - pub callbacks: Callbacks, - pub input_handler: Cell>, - pub ime_enabled: Cell, - pub pending_surrogate: Cell>, - pub last_reported_modifiers: Cell>, - pub last_reported_capslock: Cell>, - pub hovered: Cell, - pub direct_manipulation: DirectManipulationHandler, - - pub renderer: RefCell, - /// Set when the next `draw_window` call must be treated as a forced - /// render. Used after a GPU device-lost recovery, where the next frame - /// must both re-enable drawing (via `mark_drawable`) and bypass the GPUI - /// view cache (which would otherwise replay stale atlas tile references - /// from the previous frame and panic in `DirectXAtlasState::texture`), - /// and when a forced render was requested while another draw was in - /// progress and had to be deferred. - pub force_render_pending: Cell, - - pub click_state: ClickState, - pub current_cursor: Cell>, - /// Shared with [`WindowsPlatformState::cursor_visible`]. - pub cursor_visible: Arc, - pub nc_button_pressed: Cell>, - - pub display: Cell, - /// Flag to instruct the `VSyncProvider` thread to invalidate the directx devices - /// as resizing them has failed, causing us to have lost at least the render target. - pub invalidate_devices: Arc, - /// Shared with [`WindowsPlatformState::draw_coordinator`] and every other window. - pub(crate) draw_coordinator: Rc, - fullscreen: Cell>, - initial_placement: Cell>, - hwnd: HWND, - pub(crate) a11y: RefCell>, -} - -pub(crate) struct WindowsWindowInner { - hwnd: HWND, - drop_target_helper: IDropTargetHelper, - pub(crate) state: WindowsWindowState, - system_settings: WindowsSystemSettings, - pub(crate) handle: AnyWindowHandle, - pub(crate) hide_title_bar: bool, - pub(crate) is_movable: bool, - pub(crate) is_resizable: bool, - pub(crate) is_minimizable: bool, - pub(crate) executor: ForegroundExecutor, - pub(crate) validation_number: usize, - pub(crate) main_receiver: PriorityQueueReceiver, - pub(crate) platform_window_handle: HWND, - pub(crate) parent_hwnd: Option, -} - -impl WindowsWindowState { - fn new( - hwnd: HWND, - directx_devices: &DirectXDevices, - window_params: &CREATESTRUCTW, - current_cursor: Option, - cursor_visible: Arc, - display: WindowsDisplay, - min_size: Option>, - appearance: WindowAppearance, - disable_direct_composition: bool, - invalidate_devices: Arc, - draw_coordinator: Rc, - ) -> Result { - let scale_factor = { - let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32; - monitor_dpi / USER_DEFAULT_SCREEN_DPI as f32 - }; - let origin = logical_point(window_params.x as f32, window_params.y as f32, scale_factor); - let logical_size = { - let physical_size = size( - DevicePixels(window_params.cx), - DevicePixels(window_params.cy), - ); - physical_size.to_pixels(scale_factor) - }; - let fullscreen_restore_bounds = Bounds { - origin, - size: logical_size, - }; - let border_offset = WindowBorderOffset::default(); - let restore_from_minimized = None; - let renderer = DirectXRenderer::new(hwnd, directx_devices, disable_direct_composition) - .context("Creating DirectX renderer")?; - let callbacks = Callbacks::default(); - let input_handler = None; - let pending_surrogate = None; - let last_reported_modifiers = None; - let last_reported_capslock = None; - let hovered = false; - let click_state = ClickState::new(); - let nc_button_pressed = None; - let fullscreen = None; - let initial_placement = None; - - let direct_manipulation = DirectManipulationHandler::new(hwnd, scale_factor) - .context("initializing Direct Manipulation")?; - - Ok(Self { - origin: Cell::new(origin), - logical_size: Cell::new(logical_size), - fullscreen_restore_bounds: Cell::new(fullscreen_restore_bounds), - border_offset, - appearance: Cell::new(appearance), - background_appearance: Cell::new(WindowBackgroundAppearance::Opaque), - scale_factor: Cell::new(scale_factor), - restore_from_minimized: Cell::new(restore_from_minimized), - min_size, - callbacks, - input_handler: Cell::new(input_handler), - ime_enabled: Cell::new(true), - pending_surrogate: Cell::new(pending_surrogate), - last_reported_modifiers: Cell::new(last_reported_modifiers), - last_reported_capslock: Cell::new(last_reported_capslock), - hovered: Cell::new(hovered), - renderer: RefCell::new(renderer), - force_render_pending: Cell::new(false), - click_state, - current_cursor: Cell::new(current_cursor), - cursor_visible, - nc_button_pressed: Cell::new(nc_button_pressed), - display: Cell::new(display), - fullscreen: Cell::new(fullscreen), - initial_placement: Cell::new(initial_placement), - hwnd, - invalidate_devices, - draw_coordinator, - direct_manipulation, - a11y: RefCell::new(None), - }) - } - - #[inline] - pub(crate) fn is_fullscreen(&self) -> bool { - self.fullscreen.get().is_some() - } - - pub(crate) fn is_maximized(&self) -> bool { - !self.is_fullscreen() && unsafe { IsZoomed(self.hwnd) }.as_bool() - } - - fn bounds(&self) -> Bounds { - Bounds { - origin: self.origin.get(), - size: self.logical_size.get(), - } - } - - // Calculate the bounds used for saving and whether the window is maximized. - fn calculate_window_bounds(&self) -> (Bounds, bool) { - let placement = unsafe { - let mut placement = WINDOWPLACEMENT { - length: std::mem::size_of::() as u32, - ..Default::default() - }; - GetWindowPlacement(self.hwnd, &mut placement) - .context("failed to get window placement") - .log_err(); - placement - }; - ( - calculate_client_rect( - placement.rcNormalPosition, - &self.border_offset, - self.scale_factor.get(), - ), - placement.showCmd == SW_SHOWMAXIMIZED.0 as u32, - ) - } - - fn window_bounds(&self) -> WindowBounds { - let (bounds, maximized) = self.calculate_window_bounds(); - - if self.is_fullscreen() { - WindowBounds::Fullscreen(self.fullscreen_restore_bounds.get()) - } else if maximized { - WindowBounds::Maximized(bounds) - } else { - WindowBounds::Windowed(bounds) - } - } - - /// get the logical size of the app's drawable area. - /// - /// Currently, GPUI uses the logical size of the app to handle mouse interactions (such as - /// whether the mouse collides with other elements of GPUI). - fn content_size(&self) -> Size { - self.logical_size.get() - } -} - -impl WindowsWindowInner { - fn new(context: &mut WindowCreateContext, hwnd: HWND, cs: &CREATESTRUCTW) -> Result> { - let state = WindowsWindowState::new( - hwnd, - &context.directx_devices, - cs, - context.current_cursor, - context.cursor_visible.clone(), - context.display, - context.min_size, - context.appearance, - context.disable_direct_composition, - context.invalidate_devices.clone(), - context.draw_coordinator.clone(), - )?; - - Ok(Rc::new(Self { - hwnd, - drop_target_helper: context.drop_target_helper.clone(), - state, - handle: context.handle, - hide_title_bar: context.hide_title_bar, - is_movable: context.is_movable, - is_resizable: context.is_resizable, - is_minimizable: context.is_minimizable, - executor: context.executor.clone(), - validation_number: context.validation_number, - main_receiver: context.main_receiver.clone(), - platform_window_handle: context.platform_window_handle, - system_settings: WindowsSystemSettings::new(), - parent_hwnd: context.parent_hwnd, - })) - } - - fn toggle_fullscreen(self: &Rc) { - let this = self.clone(); - self.executor - .spawn(async move { - let StyleAndBounds { - style, - x, - y, - cx, - cy, - } = match this.state.fullscreen.take() { - Some(state) => state, - None => { - let (window_bounds, _) = this.state.calculate_window_bounds(); - this.state.fullscreen_restore_bounds.set(window_bounds); - - let style = - WINDOW_STYLE(unsafe { get_window_long(this.hwnd, GWL_STYLE) } as _); - let mut rc = RECT::default(); - unsafe { GetWindowRect(this.hwnd, &mut rc) } - .context("failed to get window rect") - .log_err(); - let _ = this.state.fullscreen.set(Some(StyleAndBounds { - style, - x: rc.left, - y: rc.top, - cx: rc.right - rc.left, - cy: rc.bottom - rc.top, - })); - let style = style - & !(WS_THICKFRAME - | WS_SYSMENU - | WS_MAXIMIZEBOX - | WS_MINIMIZEBOX - | WS_CAPTION); - let physical_bounds = this.state.display.get().physical_bounds(); - StyleAndBounds { - style, - x: physical_bounds.left().0, - y: physical_bounds.top().0, - cx: physical_bounds.size.width.0, - cy: physical_bounds.size.height.0, - } - } - }; - set_non_rude_hwnd(this.hwnd, !this.state.is_fullscreen()); - unsafe { set_window_long(this.hwnd, GWL_STYLE, style.0 as isize) }; - unsafe { - SetWindowPos( - this.hwnd, - None, - x, - y, - cx, - cy, - SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER, - ) - } - .log_err(); - }) - .detach(); - } - - fn set_window_placement(self: &Rc) -> Result<()> { - let Some(open_status) = self.state.initial_placement.take() else { - return Ok(()); - }; - match open_status.state { - WindowOpenState::Maximized => unsafe { - SetWindowPlacement(self.hwnd, &open_status.placement) - .context("failed to set window placement")?; - ShowWindowAsync(self.hwnd, SW_MAXIMIZE).ok()?; - }, - WindowOpenState::Fullscreen => { - unsafe { - SetWindowPlacement(self.hwnd, &open_status.placement) - .context("failed to set window placement")? - }; - self.toggle_fullscreen(); - } - WindowOpenState::Windowed => unsafe { - SetWindowPlacement(self.hwnd, &open_status.placement) - .context("failed to set window placement")?; - }, - } - Ok(()) - } - - pub(crate) fn system_settings(&self) -> &WindowsSystemSettings { - &self.system_settings - } -} - -#[derive(Default)] -pub(crate) struct Callbacks { - pub(crate) request_frame: Cell>>, - pub(crate) input: Cell DispatchEventResult>>>, - pub(crate) active_status_change: Cell>>, - pub(crate) hovered_status_change: Cell>>, - pub(crate) resize: Cell, f32)>>>, - pub(crate) moved: Cell>>, - pub(crate) should_close: Cell bool>>>, - pub(crate) close: Cell>>, - pub(crate) hit_test_window_control: Cell Option>>>, - pub(crate) appearance_changed: Cell>>, -} - -struct WindowCreateContext { - inner: Option>>, - handle: AnyWindowHandle, - hide_title_bar: bool, - display: WindowsDisplay, - is_movable: bool, - is_resizable: bool, - is_minimizable: bool, - min_size: Option>, - executor: ForegroundExecutor, - current_cursor: Option, - cursor_visible: Arc, - drop_target_helper: IDropTargetHelper, - validation_number: usize, - main_receiver: PriorityQueueReceiver, - platform_window_handle: HWND, - appearance: WindowAppearance, - disable_direct_composition: bool, - directx_devices: DirectXDevices, - invalidate_devices: Arc, - draw_coordinator: Rc, - parent_hwnd: Option, -} - -impl WindowsWindow { - pub(crate) fn new( - handle: AnyWindowHandle, - params: WindowParams, - creation_info: WindowCreationInfo, - ) -> Result { - // Native popups are not implemented on Windows yet. Rejecting lets callers fall back to - // gpui's in-window popovers. - if let WindowKind::AnchoredPopup(_) = params.kind { - return Err(popup::PopupNotSupportedError.into()); - } - - let WindowCreationInfo { - icon, - executor, - current_cursor, - cursor_visible, - drop_target_helper, - validation_number, - main_receiver, - platform_window_handle, - disable_direct_composition, - directx_devices, - invalidate_devices, - draw_coordinator, - } = creation_info; - register_window_class(icon); - let parent_hwnd = if params.kind == WindowKind::Dialog { - let parent_window = unsafe { GetActiveWindow() }; - if parent_window.is_invalid() { - None - } else { - // Disable the parent window to make this dialog modal - unsafe { - EnableWindow(parent_window, false).as_bool(); - }; - Some(parent_window) - } - } else { - None - }; - let hide_title_bar = params - .titlebar - .as_ref() - .map(|titlebar| titlebar.appears_transparent) - .unwrap_or(true); - let window_name = HSTRING::from( - params - .titlebar - .as_ref() - .and_then(|titlebar| titlebar.title.as_ref()) - .map(|title| title.as_ref()) - .unwrap_or(""), - ); - - let (mut dwexstyle, dwstyle) = if params.kind == WindowKind::PopUp { - (WS_EX_TOOLWINDOW | WS_EX_TOPMOST, WINDOW_STYLE(0x0)) - } else { - let mut dwstyle = WS_SYSMENU; - - if params.is_resizable { - dwstyle |= WS_THICKFRAME | WS_MAXIMIZEBOX; - } - - if params.is_minimizable { - dwstyle |= WS_MINIMIZEBOX; - } - let dwexstyle = if params.kind == WindowKind::Dialog { - dwstyle |= WS_POPUP | WS_CAPTION; - WS_EX_DLGMODALFRAME - } else { - WS_EX_APPWINDOW - }; - - (dwexstyle, dwstyle) - }; - if !disable_direct_composition { - dwexstyle |= WS_EX_NOREDIRECTIONBITMAP; - } - - let hinstance = get_module_handle(); - let display = if let Some(display_id) = params.display_id { - WindowsDisplay::new(display_id) - } else { - None - } - .or_else(WindowsDisplay::primary_monitor) - .context("failed to find any monitor")?; - let appearance = system_appearance().unwrap_or_default(); - let mut context = WindowCreateContext { - inner: None, - handle, - hide_title_bar, - display, - is_movable: params.is_movable, - is_resizable: params.is_resizable, - is_minimizable: params.is_minimizable, - min_size: params.window_min_size, - executor, - current_cursor, - cursor_visible, - drop_target_helper, - validation_number, - main_receiver, - platform_window_handle, - appearance, - disable_direct_composition, - directx_devices, - invalidate_devices, - draw_coordinator, - parent_hwnd, - }; - let creation_result = unsafe { - CreateWindowExW( - dwexstyle, - WINDOW_CLASS_NAME, - &window_name, - dwstyle, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - parent_hwnd, - None, - Some(hinstance.into()), - Some(&context as *const _ as *const _), - ) - }; - - // Failure to create a `WindowsWindowState` can cause window creation to fail, - // so check the inner result first. - let this = context.inner.take().transpose()?; - let hwnd = creation_result?; - let this = this.unwrap(); - - register_drag_drop(&this)?; - set_non_rude_hwnd(hwnd, true); - configure_dwm_dark_mode(hwnd, appearance); - this.state.border_offset.update(hwnd)?; - let placement = - retrieve_window_placement(hwnd, display, params.bounds, &this.state.border_offset)?; - if params.show { - let mut placement = placement; - if !params.focus { - placement.showCmd = SW_SHOWNOACTIVATE.0 as u32; - } - unsafe { SetWindowPlacement(hwnd, &placement)? }; - } else { - this.state.initial_placement.set(Some(WindowOpenStatus { - placement, - state: WindowOpenState::Windowed, - })); - } - - Ok(Self(this)) - } -} - -impl rwh::HasWindowHandle for WindowsWindow { - fn window_handle(&self) -> std::result::Result, rwh::HandleError> { - let raw = rwh::Win32WindowHandle::new(unsafe { - NonZeroIsize::new_unchecked(self.0.hwnd.0 as isize) - }) - .into(); - Ok(unsafe { rwh::WindowHandle::borrow_raw(raw) }) - } -} - -impl rwh::HasDisplayHandle for WindowsWindow { - fn display_handle(&self) -> std::result::Result, rwh::HandleError> { - Ok(rwh::DisplayHandle::windows()) - } -} - -impl Drop for WindowsWindow { - fn drop(&mut self) { - // clone this `Rc` to prevent early release of the pointer - let this = self.0.clone(); - self.0 - .executor - .spawn(async move { - let handle = this.hwnd; - unsafe { - RevokeDragDrop(handle).log_err(); - DestroyWindow(handle).log_err(); - } - }) - .detach(); - } -} - -impl PlatformWindow for WindowsWindow { - fn bounds(&self) -> Bounds { - self.state.bounds() - } - - fn is_maximized(&self) -> bool { - self.state.is_maximized() - } - - fn window_bounds(&self) -> WindowBounds { - self.state.window_bounds() - } - - /// get the logical size of the app's drawable area. - /// - /// Currently, GPUI uses the logical size of the app to handle mouse interactions (such as - /// whether the mouse collides with other elements of GPUI). - fn content_size(&self) -> Size { - self.state.content_size() - } - - fn resize(&mut self, size: Size) { - let hwnd = self.0.hwnd; - let bounds = gpui::bounds(self.bounds().origin, size).to_device_pixels(self.scale_factor()); - let rect = calculate_window_rect(bounds, &self.state.border_offset); - - self.0 - .executor - .spawn(async move { - unsafe { - SetWindowPos( - hwnd, - None, - bounds.origin.x.0, - bounds.origin.y.0, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOMOVE, - ) - .context("unable to set window content size") - .log_err(); - } - }) - .detach(); - } - - fn scale_factor(&self) -> f32 { - self.state.scale_factor.get() - } - - fn appearance(&self) -> WindowAppearance { - self.state.appearance.get() - } - - fn display(&self) -> Option> { - Some(Rc::new(self.state.display.get())) - } - - fn mouse_position(&self) -> Point { - let scale_factor = self.scale_factor(); - let point = unsafe { - let mut point: POINT = std::mem::zeroed(); - GetCursorPos(&mut point) - .context("unable to get cursor position") - .log_err(); - ScreenToClient(self.0.hwnd, &mut point).ok().log_err(); - point - }; - logical_point(point.x as f32, point.y as f32, scale_factor) - } - - fn modifiers(&self) -> Modifiers { - current_modifiers() - } - - fn capslock(&self) -> Capslock { - current_capslock() - } - - fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { - self.state.input_handler.set(Some(input_handler)); - } - - fn take_input_handler(&mut self) -> Option { - self.state.input_handler.take() - } - - fn prompt( - &self, - level: PromptLevel, - msg: &str, - detail: Option<&str>, - answers: &[PromptButton], - ) -> Option> { - let (done_tx, done_rx) = oneshot::channel(); - let msg = msg.to_string(); - let detail_string = detail.map(|detail| detail.to_string()); - let handle = self.0.hwnd; - let answers = answers.to_vec(); - self.0 - .executor - .spawn(async move { - unsafe { - let mut config = TASKDIALOGCONFIG::default(); - config.cbSize = std::mem::size_of::() as _; - config.hwndParent = handle; - let title; - let main_icon; - match level { - PromptLevel::Info => { - title = windows::core::w!("Info"); - main_icon = TD_INFORMATION_ICON; - } - PromptLevel::Warning => { - title = windows::core::w!("Warning"); - main_icon = TD_WARNING_ICON; - } - PromptLevel::Critical => { - title = windows::core::w!("Critical"); - main_icon = TD_ERROR_ICON; - } - }; - config.pszWindowTitle = title; - config.Anonymous1.pszMainIcon = main_icon; - let instruction = HSTRING::from(msg); - config.pszMainInstruction = PCWSTR::from_raw(instruction.as_ptr()); - let hints_encoded; - if let Some(ref hints) = detail_string { - hints_encoded = HSTRING::from(hints); - config.pszContent = PCWSTR::from_raw(hints_encoded.as_ptr()); - }; - let mut button_id_map = Vec::with_capacity(answers.len()); - let mut buttons = Vec::new(); - let mut btn_encoded = Vec::new(); - for (index, btn) in answers.iter().enumerate() { - let encoded = HSTRING::from(btn.label().as_ref()); - let button_id = match btn { - PromptButton::Ok(_) => IDOK.0, - PromptButton::Cancel(_) => IDCANCEL.0, - // the first few low integer values are reserved for known buttons - // so for simplicity we just go backwards from -1 - PromptButton::Other(_) => -(index as i32) - 1, - }; - button_id_map.push(button_id); - buttons.push(TASKDIALOG_BUTTON { - nButtonID: button_id, - pszButtonText: PCWSTR::from_raw(encoded.as_ptr()), - }); - btn_encoded.push(encoded); - } - config.cButtons = buttons.len() as _; - config.pButtons = buttons.as_ptr(); - - config.pfCallback = None; - let mut res = std::mem::zeroed(); - let _ = TaskDialogIndirect(&config, Some(&mut res), None, None) - .context("unable to create task dialog") - .log_err(); - - if let Some(clicked) = - button_id_map.iter().position(|&button_id| button_id == res) - { - let _ = done_tx.send(clicked); - } - } - }) - .detach(); - - Some(done_rx) - } - - fn activate(&self) { - let hwnd = self.0.hwnd; - let this = self.0.clone(); - self.0 - .executor - .spawn(async move { - this.set_window_placement().log_err(); - - unsafe { - // If the window is minimized, restore it. - if IsIconic(hwnd).as_bool() { - ShowWindowAsync(hwnd, SW_RESTORE).ok().log_err(); - } - - SetActiveWindow(hwnd).ok(); - SetFocus(Some(hwnd)).ok(); - } - - // premium ragebait by windows, this is needed because the window - // must have received an input event to be able to set itself to foreground - // so let's just simulate user input as that seems to be the most reliable way - // some more info: https://gist.github.com/Aetopia/1581b40f00cc0cadc93a0e8ccb65dc8c - // bonus: this bug also doesn't manifest if you have vs attached to the process - let inputs = [ - INPUT { - r#type: INPUT_KEYBOARD, - Anonymous: INPUT_0 { - ki: KEYBDINPUT { - wVk: VK_MENU, - dwFlags: KEYBD_EVENT_FLAGS(0), - ..Default::default() - }, - }, - }, - INPUT { - r#type: INPUT_KEYBOARD, - Anonymous: INPUT_0 { - ki: KEYBDINPUT { - wVk: VK_MENU, - dwFlags: KEYEVENTF_KEYUP, - ..Default::default() - }, - }, - }, - ]; - unsafe { SendInput(&inputs, std::mem::size_of::() as i32) }; - - // todo(windows) - // crate `windows 0.56` reports true as Err - unsafe { SetForegroundWindow(hwnd).as_bool() }; - }) - .detach(); - } - - fn request_attention(&self) { - if self.is_active() { - return; - } - - let hwnd = self.0.hwnd; - self.0 - .executor - .spawn(async move { - let info = FLASHWINFO { - cbSize: std::mem::size_of::() as u32, - hwnd, - dwFlags: FLASHW_ALL, - uCount: 1, - dwTimeout: 0, - }; - unsafe { FlashWindowEx(&info).ok().log_err() }; - }) - .detach(); - } - - fn is_active(&self) -> bool { - self.0.hwnd == unsafe { GetActiveWindow() } - } - - fn is_hovered(&self) -> bool { - self.state.hovered.get() - } - - fn background_appearance(&self) -> WindowBackgroundAppearance { - self.state.background_appearance.get() - } - - fn is_subpixel_rendering_supported(&self) -> bool { - true - } - - fn set_title(&mut self, title: &str) { - unsafe { SetWindowTextW(self.0.hwnd, &HSTRING::from(title)) } - .inspect_err(|e| log::error!("Set title failed: {e}")) - .ok(); - } - - fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) { - self.state.background_appearance.set(background_appearance); - let hwnd = self.0.hwnd; - - // using Dwm APIs for Mica and MicaAlt backdrops. - // others follow the set_window_composition_attribute approach - match background_appearance { - WindowBackgroundAppearance::Opaque => { - set_window_composition_attribute(hwnd, None, 0); - } - WindowBackgroundAppearance::Transparent => { - set_window_composition_attribute(hwnd, None, 2); - } - WindowBackgroundAppearance::Blurred => { - set_window_composition_attribute(hwnd, Some((0, 0, 0, 0)), 4); - } - WindowBackgroundAppearance::MicaBackdrop => { - // DWMSBT_MAINWINDOW => MicaBase - dwm_set_window_composition_attribute(hwnd, 2); - } - WindowBackgroundAppearance::MicaAltBackdrop => { - // DWMSBT_TABBEDWINDOW => MicaAlt - dwm_set_window_composition_attribute(hwnd, 4); - } - } - } - - fn minimize(&self) { - unsafe { ShowWindowAsync(self.0.hwnd, SW_MINIMIZE).ok().log_err() }; - } - - fn zoom(&self) { - unsafe { - if IsWindowVisible(self.0.hwnd).as_bool() { - ShowWindowAsync(self.0.hwnd, SW_MAXIMIZE).ok().log_err(); - } else if let Some(mut status) = self.state.initial_placement.take() { - status.state = WindowOpenState::Maximized; - self.state.initial_placement.set(Some(status)); - } - } - } - - fn toggle_fullscreen(&self) { - if unsafe { IsWindowVisible(self.0.hwnd).as_bool() } { - self.0.toggle_fullscreen(); - } else if let Some(mut status) = self.state.initial_placement.take() { - status.state = WindowOpenState::Fullscreen; - self.state.initial_placement.set(Some(status)); - } - } - - fn is_fullscreen(&self) -> bool { - self.state.is_fullscreen() - } - - fn on_request_frame(&self, callback: Box) { - self.state.callbacks.request_frame.set(Some(callback)); - } - - fn on_input(&self, callback: Box DispatchEventResult>) { - self.state.callbacks.input.set(Some(callback)); - } - - fn on_active_status_change(&self, callback: Box) { - self.0 - .state - .callbacks - .active_status_change - .set(Some(callback)); - } - - fn on_hover_status_change(&self, callback: Box) { - self.0 - .state - .callbacks - .hovered_status_change - .set(Some(callback)); - } - - fn on_resize(&self, callback: Box, f32)>) { - self.state.callbacks.resize.set(Some(callback)); - } - - fn on_moved(&self, callback: Box) { - self.state.callbacks.moved.set(Some(callback)); - } - - fn on_should_close(&self, callback: Box bool>) { - self.state.callbacks.should_close.set(Some(callback)); - } - - fn on_close(&self, callback: Box) { - self.state.callbacks.close.set(Some(callback)); - } - - fn on_hit_test_window_control(&self, callback: Box Option>) { - self.0 - .state - .callbacks - .hit_test_window_control - .set(Some(callback)); - } - - fn on_appearance_changed(&self, callback: Box) { - self.0 - .state - .callbacks - .appearance_changed - .set(Some(callback)); - } - - fn draw(&self, scene: &Scene) { - self.state - .renderer - .borrow_mut() - .draw(scene, self.state.background_appearance.get()) - .log_err(); - } - - #[cfg(any(test, feature = "test-support"))] - fn render_to_image(&self, scene: &Scene) -> anyhow::Result { - self.state - .renderer - .borrow_mut() - .render_to_image(scene, self.state.background_appearance.get()) - } - - fn sprite_atlas(&self) -> Arc { - self.state.renderer.borrow().sprite_atlas() - } - - fn get_raw_handle(&self) -> HWND { - self.0.hwnd - } - - fn gpu_specs(&self) -> Option { - self.state.renderer.borrow().gpu_specs().log_err() - } - - fn update_ime_position(&self, bounds: Bounds) { - let scale_factor = self.state.scale_factor.get(); - let caret_position = POINT { - x: (bounds.origin.x.as_f32() * scale_factor) as i32, - y: (bounds.origin.y.as_f32() * scale_factor) as i32 - + ((bounds.size.height.as_f32() * scale_factor) as i32 / 2), - }; - - self.0.update_ime_position(self.0.hwnd, caret_position); - } - - fn play_system_bell(&self) { - // MB_OK: The sound specified as the Windows Default Beep sound. - let _ = unsafe { MessageBeep(MB_OK) }; - } - - fn a11y_init(&self, callbacks: gpui::A11yCallbacks) { - let action_handler = A11yActionHandler(callbacks.action); - let is_focused = unsafe { GetForegroundWindow() } == self.0.hwnd; - - let adapter = accesskit_windows::Adapter::new( - accesskit_windows::HWND(self.0.hwnd.0), - is_focused, - action_handler, - ); - - let activation_handler = A11yActivationHandler { - callback: callbacks.activation, - }; - - *self.state.a11y.borrow_mut() = Some(A11yState { - adapter, - activation_handler, - }); - } - - fn a11y_tree_update(&self, tree_update: accesskit::TreeUpdate) { - let events = { - let mut a11y = self.state.a11y.borrow_mut(); - a11y.as_mut() - .and_then(|a11y| a11y.adapter.update_if_active(|| tree_update)) - }; - // The borrow must be dropped before raising events, because - // `events.raise()` calls `UiaRaiseAutomationPropertyChangedEvent` - // which may send a nested `WM_GETOBJECT` back into this window - // procedure, re-entering `handle_wm_getobject` which also borrows - // `self.state.a11y`. - if let Some(events) = events { - events.raise(); - } - } - - fn a11y_update_window_bounds(&self) { - // Windows UIA handles window bounds tracking automatically. - } -} - -pub(crate) struct A11yState { - pub(crate) adapter: accesskit_windows::Adapter, - pub(crate) activation_handler: A11yActivationHandler, -} - -pub(crate) struct A11yActivationHandler { - callback: Box Option + Send + 'static>, -} - -impl accesskit::ActivationHandler for A11yActivationHandler { - fn request_initial_tree(&mut self) -> Option { - (self.callback)() - } -} - -struct A11yActionHandler(Box); - -impl accesskit::ActionHandler for A11yActionHandler { - fn do_action(&mut self, request: accesskit::ActionRequest) { - (self.0)(request); - } -} - -#[implement(IDropTarget)] -struct WindowsDragDropHandler(pub Rc); - -impl WindowsDragDropHandler { - fn handle_drag_drop(&self, input: PlatformInput) { - if let Some(mut func) = self.0.state.callbacks.input.take() { - func(input); - self.0.state.callbacks.input.set(Some(func)); - } - } -} - -#[allow(non_snake_case)] -impl IDropTarget_Impl for WindowsDragDropHandler_Impl { - fn DragEnter( - &self, - pdataobj: windows::core::Ref, - _grfkeystate: MODIFIERKEYS_FLAGS, - pt: &POINTL, - pdweffect: *mut DROPEFFECT, - ) -> windows::core::Result<()> { - unsafe { - let idata_obj = pdataobj.ok()?; - let config = FORMATETC { - cfFormat: CF_HDROP.0, - ptd: std::ptr::null_mut() as _, - dwAspect: DVASPECT_CONTENT.0, - lindex: -1, - tymed: TYMED_HGLOBAL.0 as _, - }; - let cursor_position = POINT { x: pt.x, y: pt.y }; - if idata_obj.QueryGetData(&config as _) == S_OK { - *pdweffect = DROPEFFECT_COPY; - let Some(mut idata) = idata_obj.GetData(&config as _).log_err() else { - return Ok(()); - }; - if idata.u.hGlobal.is_invalid() { - return Ok(()); - } - let hdrop = HDROP(idata.u.hGlobal.0); - let mut paths = SmallVec::<[PathBuf; 2]>::new(); - with_file_names(hdrop, |file_name| { - if let Some(path) = PathBuf::from_str(&file_name).log_err() { - paths.push(path); - } - }); - ReleaseStgMedium(&mut idata); - let mut cursor_position = cursor_position; - ScreenToClient(self.0.hwnd, &mut cursor_position) - .ok() - .log_err(); - let scale_factor = self.0.state.scale_factor.get(); - let input = PlatformInput::FileDrop(FileDropEvent::Entered { - position: logical_point( - cursor_position.x as f32, - cursor_position.y as f32, - scale_factor, - ), - paths: ExternalPaths(paths), - }); - self.handle_drag_drop(input); - } else { - *pdweffect = DROPEFFECT_NONE; - } - self.0 - .drop_target_helper - .DragEnter(self.0.hwnd, idata_obj, &cursor_position, *pdweffect) - .log_err(); - } - Ok(()) - } - - fn DragOver( - &self, - _grfkeystate: MODIFIERKEYS_FLAGS, - pt: &POINTL, - pdweffect: *mut DROPEFFECT, - ) -> windows::core::Result<()> { - let mut cursor_position = POINT { x: pt.x, y: pt.y }; - unsafe { - *pdweffect = DROPEFFECT_COPY; - self.0 - .drop_target_helper - .DragOver(&cursor_position, *pdweffect) - .log_err(); - ScreenToClient(self.0.hwnd, &mut cursor_position) - .ok() - .log_err(); - } - let scale_factor = self.0.state.scale_factor.get(); - let input = PlatformInput::FileDrop(FileDropEvent::Pending { - position: logical_point( - cursor_position.x as f32, - cursor_position.y as f32, - scale_factor, - ), - }); - self.handle_drag_drop(input); - - Ok(()) - } - - fn DragLeave(&self) -> windows::core::Result<()> { - unsafe { - self.0.drop_target_helper.DragLeave().log_err(); - } - let input = PlatformInput::FileDrop(FileDropEvent::Exited); - self.handle_drag_drop(input); - - Ok(()) - } - - fn Drop( - &self, - pdataobj: windows::core::Ref, - _grfkeystate: MODIFIERKEYS_FLAGS, - pt: &POINTL, - pdweffect: *mut DROPEFFECT, - ) -> windows::core::Result<()> { - let idata_obj = pdataobj.ok()?; - let mut cursor_position = POINT { x: pt.x, y: pt.y }; - unsafe { - *pdweffect = DROPEFFECT_COPY; - self.0 - .drop_target_helper - .Drop(idata_obj, &cursor_position, *pdweffect) - .log_err(); - ScreenToClient(self.0.hwnd, &mut cursor_position) - .ok() - .log_err(); - } - let scale_factor = self.0.state.scale_factor.get(); - let input = PlatformInput::FileDrop(FileDropEvent::Submit { - position: logical_point( - cursor_position.x as f32, - cursor_position.y as f32, - scale_factor, - ), - }); - self.handle_drag_drop(input); - - Ok(()) - } -} - -#[derive(Debug, Clone)] -pub(crate) struct ClickState { - button: Cell, - last_click: Cell, - last_position: Cell>, - double_click_spatial_tolerance_width: Cell, - double_click_spatial_tolerance_height: Cell, - double_click_interval: Cell, - pub(crate) current_count: Cell, -} - -impl ClickState { - pub fn new() -> Self { - let double_click_spatial_tolerance_width = unsafe { GetSystemMetrics(SM_CXDOUBLECLK) }; - let double_click_spatial_tolerance_height = unsafe { GetSystemMetrics(SM_CYDOUBLECLK) }; - let double_click_interval = Duration::from_millis(unsafe { GetDoubleClickTime() } as u64); - - ClickState { - button: Cell::new(MouseButton::Left), - last_click: Cell::new(Instant::now()), - last_position: Cell::new(Point::default()), - double_click_spatial_tolerance_width: Cell::new(double_click_spatial_tolerance_width), - double_click_spatial_tolerance_height: Cell::new(double_click_spatial_tolerance_height), - double_click_interval: Cell::new(double_click_interval), - current_count: Cell::new(0), - } - } - - /// update self and return the needed click count - pub fn update(&self, button: MouseButton, new_position: Point) -> usize { - if self.button.get() == button && self.is_double_click(new_position) { - self.current_count.update(|it| it + 1); - } else { - self.current_count.set(1); - } - self.last_click.set(Instant::now()); - self.last_position.set(new_position); - self.button.set(button); - - self.current_count.get() - } - - pub fn system_update(&self, wparam: usize) { - match wparam { - // SPI_SETDOUBLECLKWIDTH - 29 => self - .double_click_spatial_tolerance_width - .set(unsafe { GetSystemMetrics(SM_CXDOUBLECLK) }), - // SPI_SETDOUBLECLKHEIGHT - 30 => self - .double_click_spatial_tolerance_height - .set(unsafe { GetSystemMetrics(SM_CYDOUBLECLK) }), - // SPI_SETDOUBLECLICKTIME - 32 => self - .double_click_interval - .set(Duration::from_millis(unsafe { GetDoubleClickTime() } as u64)), - _ => {} - } - } - - #[inline] - fn is_double_click(&self, new_position: Point) -> bool { - let diff = self.last_position.get() - new_position; - - self.last_click.get().elapsed() < self.double_click_interval.get() - && diff.x.0.abs() <= self.double_click_spatial_tolerance_width.get() - && diff.y.0.abs() <= self.double_click_spatial_tolerance_height.get() - } -} - -#[derive(Copy, Clone)] -struct StyleAndBounds { - style: WINDOW_STYLE, - x: i32, - y: i32, - cx: i32, - cy: i32, -} - -#[repr(C)] -struct WINDOWCOMPOSITIONATTRIBDATA { - attrib: u32, - pv_data: *mut std::ffi::c_void, - cb_data: usize, -} - -#[repr(C)] -struct AccentPolicy { - accent_state: u32, - accent_flags: u32, - gradient_color: u32, - animation_id: u32, -} - -type Color = (u8, u8, u8, u8); - -#[derive(Debug, Default, Clone)] -pub(crate) struct WindowBorderOffset { - pub(crate) width_offset: Cell, - pub(crate) height_offset: Cell, -} - -impl WindowBorderOffset { - pub(crate) fn update(&self, hwnd: HWND) -> anyhow::Result<()> { - let window_rect = unsafe { - let mut rect = std::mem::zeroed(); - GetWindowRect(hwnd, &mut rect)?; - rect - }; - let client_rect = unsafe { - let mut rect = std::mem::zeroed(); - GetClientRect(hwnd, &mut rect)?; - rect - }; - self.width_offset - .set((window_rect.right - window_rect.left) - (client_rect.right - client_rect.left)); - self.height_offset - .set((window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top)); - Ok(()) - } -} - -#[derive(Clone)] -struct WindowOpenStatus { - placement: WINDOWPLACEMENT, - state: WindowOpenState, -} - -#[derive(Clone, Copy)] -enum WindowOpenState { - Maximized, - Fullscreen, - Windowed, -} - -const WINDOW_CLASS_NAME: PCWSTR = w!("Zed::Window"); - -fn register_window_class(icon_handle: HICON) { - static ONCE: Once = Once::new(); - ONCE.call_once(|| { - let wc = WNDCLASSW { - lpfnWndProc: Some(window_procedure), - hIcon: icon_handle, - lpszClassName: PCWSTR(WINDOW_CLASS_NAME.as_ptr()), - style: CS_HREDRAW | CS_VREDRAW, - hInstance: get_module_handle().into(), - hbrBackground: unsafe { CreateSolidBrush(COLORREF(0x00000000)) }, - ..Default::default() - }; - unsafe { RegisterClassW(&wc) }; - }); -} - -unsafe extern "system" fn window_procedure( - hwnd: HWND, - msg: u32, - wparam: WPARAM, - lparam: LPARAM, -) -> LRESULT { - if msg == WM_NCCREATE { - let window_params = unsafe { &*(lparam.0 as *const CREATESTRUCTW) }; - let window_creation_context = window_params.lpCreateParams as *mut WindowCreateContext; - let window_creation_context = unsafe { &mut *window_creation_context }; - return match WindowsWindowInner::new(window_creation_context, hwnd, window_params) { - Ok(window_state) => { - let weak = Box::new(Rc::downgrade(&window_state)); - unsafe { set_window_long(hwnd, GWLP_USERDATA, Box::into_raw(weak) as isize) }; - window_creation_context.inner = Some(Ok(window_state)); - unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) } - } - Err(error) => { - window_creation_context.inner = Some(Err(error)); - LRESULT(0) - } - }; - } - - let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak; - if ptr.is_null() { - return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }; - } - let inner = unsafe { &*ptr }; - let result = if let Some(inner) = inner.upgrade() { - inner.handle_msg(hwnd, msg, wparam, lparam) - } else { - unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) } - }; - - if msg == WM_NCDESTROY { - unsafe { set_window_long(hwnd, GWLP_USERDATA, 0) }; - unsafe { drop(Box::from_raw(ptr)) }; - } - - result -} - -pub(crate) fn window_from_hwnd(hwnd: HWND) -> Option> { - if hwnd.is_invalid() { - return None; - } - - let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak; - if !ptr.is_null() { - let inner = unsafe { &*ptr }; - inner.upgrade() - } else { - None - } -} - -fn get_module_handle() -> HMODULE { - unsafe { - let mut h_module = std::mem::zeroed(); - GetModuleHandleExW( - GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - windows::core::w!("ZedModule"), - &mut h_module, - ) - .expect("Unable to get module handle"); // this should never fail - - h_module - } -} - -fn register_drag_drop(window: &Rc) -> Result<()> { - let window_handle = window.hwnd; - let handler = WindowsDragDropHandler(window.clone()); - // The lifetime of `IDropTarget` is handled by Windows, it won't release until - // we call `RevokeDragDrop`. - // So, it's safe to drop it here. - let drag_drop_handler: IDropTarget = handler.into(); - unsafe { - RegisterDragDrop(window_handle, &drag_drop_handler) - .context("unable to register drag-drop event")?; - } - Ok(()) -} - -fn calculate_window_rect(bounds: Bounds, border_offset: &WindowBorderOffset) -> RECT { - // NOTE: - // The reason we're not using `AdjustWindowRectEx()` here is - // that the size reported by this function is incorrect. - // You can test it, and there are similar discussions online. - // See: https://stackoverflow.com/questions/12423584/how-to-set-exact-client-size-for-overlapped-window-winapi - // - // So we manually calculate these values here. - let mut rect = RECT { - left: bounds.left().0, - top: bounds.top().0, - right: bounds.right().0, - bottom: bounds.bottom().0, - }; - let left_offset = border_offset.width_offset.get() / 2; - let top_offset = border_offset.height_offset.get() / 2; - let right_offset = border_offset.width_offset.get() - left_offset; - let bottom_offset = border_offset.height_offset.get() - top_offset; - rect.left -= left_offset; - rect.top -= top_offset; - rect.right += right_offset; - rect.bottom += bottom_offset; - rect -} - -fn calculate_client_rect( - rect: RECT, - border_offset: &WindowBorderOffset, - scale_factor: f32, -) -> Bounds { - let left_offset = border_offset.width_offset.get() / 2; - let top_offset = border_offset.height_offset.get() / 2; - let right_offset = border_offset.width_offset.get() - left_offset; - let bottom_offset = border_offset.height_offset.get() - top_offset; - let left = rect.left + left_offset; - let top = rect.top + top_offset; - let right = rect.right - right_offset; - let bottom = rect.bottom - bottom_offset; - let physical_size = size(DevicePixels(right - left), DevicePixels(bottom - top)); - Bounds { - origin: logical_point(left as f32, top as f32, scale_factor), - size: physical_size.to_pixels(scale_factor), - } -} - -fn retrieve_window_placement( - hwnd: HWND, - display: WindowsDisplay, - initial_bounds: Bounds, - border_offset: &WindowBorderOffset, -) -> Result { - let mut placement = WINDOWPLACEMENT { - length: std::mem::size_of::() as u32, - ..Default::default() - }; - unsafe { GetWindowPlacement(hwnd, &mut placement)? }; - // the bounds may be not inside the display - let bounds = if display.check_given_bounds(initial_bounds) { - initial_bounds - } else { - display.default_bounds() - }; - // `bounds` is expressed in logical pixels for `display`, so it must be converted - // to device pixels using that display's own scale factor. The window's current - // scale factor can't be used here: `CreateWindowExW` was called with - // `CW_USEDEFAULT`, so at this point the window may still be sitting on whichever - // monitor Windows picked by default, which can have a different DPI than `display` - // and would otherwise throw off the physical position (e.g. leaving the window - // partially off-screen when moved to a monitor with a different scale factor). - let bounds = bounds.to_device_pixels(display.scale_factor()); - placement.rcNormalPosition = calculate_window_rect(bounds, border_offset); - Ok(placement) -} - -fn dwm_set_window_composition_attribute(hwnd: HWND, backdrop_type: u32) { - let mut version = unsafe { std::mem::zeroed() }; - let status = unsafe { windows::Wdk::System::SystemServices::RtlGetVersion(&mut version) }; - - // DWMWA_SYSTEMBACKDROP_TYPE is available only on version 22621 or later - // using SetWindowCompositionAttributeType as a fallback - if !status.is_ok() || version.dwBuildNumber < 22621 { - return; - } - - unsafe { - let result = DwmSetWindowAttribute( - hwnd, - DWMWA_SYSTEMBACKDROP_TYPE, - &backdrop_type as *const _ as *const _, - std::mem::size_of_val(&backdrop_type) as u32, - ); - - if !result.is_ok() { - return; - } - } -} - -fn set_window_composition_attribute(hwnd: HWND, color: Option, state: u32) { - let mut version = unsafe { std::mem::zeroed() }; - let status = unsafe { windows::Wdk::System::SystemServices::RtlGetVersion(&mut version) }; - - if !status.is_ok() || version.dwBuildNumber < 17763 { - return; - } - - unsafe { - type SetWindowCompositionAttributeType = - unsafe extern "system" fn(HWND, *mut WINDOWCOMPOSITIONATTRIBDATA) -> BOOL; - let module_name = PCSTR::from_raw(c"user32.dll".as_ptr() as *const u8); - if let Some(user32) = GetModuleHandleA(module_name) - .context("Unable to get user32.dll handle") - .log_err() - { - let func_name = PCSTR::from_raw(c"SetWindowCompositionAttribute".as_ptr() as *const u8); - let Some(raw_set_window_composition_attribute) = GetProcAddress(user32, func_name) - else { - return; - }; - let set_window_composition_attribute: SetWindowCompositionAttributeType = - std::mem::transmute(raw_set_window_composition_attribute); - let mut color = color.unwrap_or_default(); - let is_acrylic = state == 4; - if is_acrylic && color.3 == 0 { - color.3 = 1; - } - let accent = AccentPolicy { - accent_state: state, - accent_flags: if is_acrylic { 0 } else { 2 }, - gradient_color: (color.0 as u32) - | ((color.1 as u32) << 8) - | ((color.2 as u32) << 16) - | ((color.3 as u32) << 24), - animation_id: 0, - }; - let mut data = WINDOWCOMPOSITIONATTRIBDATA { - attrib: 0x13, - pv_data: &accent as *const _ as *mut _, - cb_data: std::mem::size_of::(), - }; - let _ = set_window_composition_attribute(hwnd, &mut data as *mut _ as _); - } - } -} - -// When the platform title bar is hidden, Windows may think that our application is meant to appear 'fullscreen' -// and will stop the taskbar from appearing on top of our window. Prevent this. -// https://devblogs.microsoft.com/oldnewthing/20250522-00/?p=111211 -fn set_non_rude_hwnd(hwnd: HWND, non_rude: bool) { - if non_rude { - unsafe { SetPropW(hwnd, w!("NonRudeHWND"), Some(HANDLE(1 as _))) }.log_err(); - } else { - unsafe { RemovePropW(hwnd, w!("NonRudeHWND")) }.log_err(); - } -} - -#[cfg(test)] -mod tests { - use super::ClickState; - use gpui::{DevicePixels, MouseButton, point}; - use std::time::Duration; - - #[test] - fn test_double_click_interval() { - let state = ClickState::new(); - assert_eq!( - state.update(MouseButton::Left, point(DevicePixels(0), DevicePixels(0))), - 1 - ); - assert_eq!( - state.update(MouseButton::Right, point(DevicePixels(0), DevicePixels(0))), - 1 - ); - assert_eq!( - state.update(MouseButton::Left, point(DevicePixels(0), DevicePixels(0))), - 1 - ); - assert_eq!( - state.update(MouseButton::Left, point(DevicePixels(0), DevicePixels(0))), - 2 - ); - state - .last_click - .update(|it| it - Duration::from_millis(700)); - assert_eq!( - state.update(MouseButton::Left, point(DevicePixels(0), DevicePixels(0))), - 1 - ); - } - - #[test] - fn test_double_click_spatial_tolerance() { - let state = ClickState::new(); - assert_eq!( - state.update(MouseButton::Left, point(DevicePixels(-3), DevicePixels(0))), - 1 - ); - assert_eq!( - state.update(MouseButton::Left, point(DevicePixels(0), DevicePixels(3))), - 2 - ); - assert_eq!( - state.update(MouseButton::Right, point(DevicePixels(3), DevicePixels(2))), - 1 - ); - assert_eq!( - state.update(MouseButton::Right, point(DevicePixels(10), DevicePixels(0))), - 1 - ); - } -} diff --git a/crates/gpui_pre_windows/src/wrapper.rs b/crates/gpui_pre_windows/src/wrapper.rs deleted file mode 100644 index 60bbc433..00000000 --- a/crates/gpui_pre_windows/src/wrapper.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::ops::Deref; - -use windows::Win32::{Foundation::HWND, UI::WindowsAndMessaging::HCURSOR}; - -#[derive(Debug, Clone, Copy)] -pub(crate) struct SafeCursor { - raw: HCURSOR, -} - -unsafe impl Send for SafeCursor {} -unsafe impl Sync for SafeCursor {} - -impl From for SafeCursor { - fn from(value: HCURSOR) -> Self { - SafeCursor { raw: value } - } -} - -impl Deref for SafeCursor { - type Target = HCURSOR; - - fn deref(&self) -> &Self::Target { - &self.raw - } -} - -#[derive(Debug, Clone, Copy)] -pub(crate) struct SafeHwnd { - raw: HWND, -} - -impl SafeHwnd { - pub(crate) fn as_raw(&self) -> HWND { - self.raw - } -} - -unsafe impl Send for SafeHwnd {} -unsafe impl Sync for SafeHwnd {} - -impl From for SafeHwnd { - fn from(value: HWND) -> Self { - SafeHwnd { raw: value } - } -} - -impl Deref for SafeHwnd { - type Target = HWND; - - fn deref(&self) -> &Self::Target { - &self.raw - } -} diff --git a/crates/gpui_web/Cargo.toml b/crates/gpui_web/Cargo.toml deleted file mode 100644 index 29da556d..00000000 --- a/crates/gpui_web/Cargo.toml +++ /dev/null @@ -1,203 +0,0 @@ -# Vendored fork of the published `gpui-pre-web` 0.3.3 crate. -# -# The `src/` tree here is the published 0.3.3 sources verbatim, except for two -# changes in `src/events.rs` -- both marked `HeroGPUI fork:` at the site, both -# recorded in `[package.metadata.herogpui-vendor]` below, and both written up -# for upstream in `docs/upstream/gpui-web-scroll-and-ime.md`. This manifest -# additionally drops `multithreaded` from `default` (see `[features]`), which -# is a build-configuration choice rather than a source change. Nothing else -# differs; a reviewer can confirm that with -# -# diff -r crates/gpui_web/src \ -# ~/.cargo/registry/src/index.crates.io-*/gpui-pre-web-0.3.3/src -# -# `gpui_platform` pulls this crate in automatically on wasm32 (its -# `[target.'cfg(target_family = "wasm")'.dependencies.gpui_web]` table), which -# is the only edge into it -- no workspace crate names `gpui_web`, and adding -# such an edge would buy nothing, because feature sets are unioned and -# `gpui_platform` already enables the default ones. The `default` list in this -# manifest is therefore the only place the `multithreaded` feature can be -# switched. The fork itself is delivered by `[patch.crates-io]` in the -# workspace root. That patch key matters: keying the patch by a git URL, as an -# earlier Zed-git-rev layout did, matches nothing in a registry dependency -# graph and silently applies no override at all. -# -# The `version` above must stay exactly the version upstream `gpui-pre-platform` -# asks for (`gpui-pre-web = "=0.3.3"`). A mismatch makes `[patch.crates-io]` -# stop applying with no error at all: cargo resolves the registry crate, the -# fork's two hunks disappear from the build, and nothing reports it. Check with -# `cargo tree -i gpui-pre-web --target wasm32-unknown-unknown`, which must -# print this path. -# -# Unlike the 1.18.1 crate this replaced, 0.3.3 bundles no fonts -- its -# `WebPlatform` starts with an empty font database and the application is -# expected to call `App::text_system().add_fonts(..)`. `crates/herogpui-web` -# already does exactly that, so the vendored `assets/fonts` tree that the old -# fork carried is gone rather than re-added. -# -# This manifest carries its own `[workspace]` table (as the published crate -# does), which makes it a workspace root of its own rather than a member of -# HeroGPUI's. That is deliberate: it keeps upstream's lint configuration -- -# which the workspace's own `[workspace.lints]` would contradict, starting with -# `unsafe_code = "deny"` -- and keeps this un-owned code out of -# `cargo fmt --all` and `.shots/lint.ps1`. A member would have to be reformatted -# to our rules, and every reformatted line is a line a reviewer must diff by -# hand instead of by `diff -r`. -[package] -name = "gpui-pre-web" -version = "0.3.3" -publish = false -edition = "2024" -license = "Apache-2.0" -autoexamples = false -description = "HeroGPUI's fork of gpui_web 0.3.3: shift+wheel horizontal scroll and an IME mirror resync after paste" -repository = "https://github.com/zed-industries/zed" - -[package.metadata.cargo-shear] -ignored = ["scheduler"] - -# Upstream's provenance stamp, kept verbatim so the snapshot this fork is -# based on stays nameable. -[package.metadata.gpui-pre] -zed-crate = "gpui_web" -zed-version = "0.1.0" -zed-rev = "5b055fa789a8b8d38ac951a6e0cde272f66b4495" - -# The complete record of how this differs from the published crate. The first -# two entries are in `src/events.rs`; together they add 9 lines of code and -# remove 5 (the rest of each hunk is comment). The third is the `default` -# feature list above and touches no source. An incomplete record here is what -# makes a vendored fork unreviewable, so this list is the thing to update first -# when the fork changes. -[package.metadata.herogpui-vendor] -upstream = "https://crates.io/crates/gpui-pre-web" -version = "0.3.3" -changes = [ - "Map shift+wheel to horizontal scroll when the event carries no deltaX, so a mouse can scroll a horizontally scrollable region in the browser (`register_wheel`).", - "Refresh the IME mirror after both the synchronous text paste and the async image/mixed paste, so the hidden textarea does not keep pre-paste text and corrupt the next composition diff (`register_paste`).", - "Drop `multithreaded` from the `default` feature list, so optional `wasm_thread` and its `#![feature]` attribute leave the wasm32 graph and the pinned stable toolchain can build this target. Not a source change; see the comment above `[features]`.", -] - -# The third and last deviation from upstream, and the only one outside -# `src/events.rs`: upstream ships `default = ["multithreaded"]`. -# -# `multithreaded` pulls in optional `wasm_thread`, whose `lib.rs` opens with a -# `#![feature]` attribute, so it forces every wasm32 build of this workspace -- -# including the `wasm-release` artifact and CI's `wasm` job -- onto a nightly -# toolchain with `error[E0554]` from a dependency we do not own. Nothing was -# using it: `crates/herogpui-web` starts the app with -# `gpui_platform::single_threaded_web()`, and the multi-threaded platform needs -# web workers over shared wasm memory, which a browser only grants in a -# cross-origin-isolated context -- something plain GitHub Pages, which serves -# the gallery, sends no COOP/COEP headers for. -# -# This is the only place the feature can be switched. `gpui_platform` declares -# the wasm32 edge into this crate with default features on, and cargo unions -# feature sets, so `default-features = false` on an edge of ours would change -# nothing. -# -# Measured with the feature off: `wasm_thread` leaves the wasm32 graph and the -# lockfile, the pinned stable toolchain builds `-p herogpui-web` at -# `--profile wasm-release`, and the resulting artifact boots, renders, presses, -# focuses, takes keyboard input and resolves a `background_executor().timer()` -# in a browser -- indistinguishably from the nightly/`multithreaded` build, -# which is 23 KB larger. Turn `multithreaded` back on (and go back to nightly) -# only together with a cross-origin-isolated deployment that can actually use -# it. -[features] -default = [] -multithreaded = ["dep:wasm_thread", "scheduler/wasm-threads"] - -[lib] -name = "gpui_web" -path = "src/gpui_web.rs" - -[target.'cfg(target_family = "wasm")'.dependencies] -gpui = { package = "gpui-pre", version = "=0.3.3", default-features = false } -scheduler = { package = "gpui-pre-scheduler", version = "=0.3.3" } -parking_lot = { version = "0.12.1", features = ["nightly"] } -gpui_wgpu = { package = "gpui-pre-wgpu", version = "=0.3.3" } -http_client = { package = "gpui-pre-http-client", version = "=0.3.3" } -anyhow = { version = "1.0.86" } -futures = { version = "0.3.32" } -log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] } -uuid = { version = "1.1.2", features = ["v4", "v5", "v7", "serde"] } -wasm-bindgen = { version = "0.2.120" } -wasm-bindgen-futures = { version = "0.4" } -web-time = { version = "1.1.0" } -console_error_panic_hook = { version = "0.1.7" } -js-sys = { version = "0.3" } -raw-window-handle = { version = "0.6" } -wasm_thread = { version = "0.3", features = ["es_modules"], optional = true } -web-sys = { version = "0.3", features = [ - "console", - "Blob", - "Clipboard", - "ClipboardEvent", - "ClipboardItem", - "CompositionEvent", - "CssStyleDeclaration", - "DataTransfer", - "DataTransferItem", - "DataTransferItemList", - "Document", - "DomRect", - "DragEvent", - "Element", - "EventTarget", - "File", - "HtmlCanvasElement", - "HtmlElement", - "HtmlTextAreaElement", - "IdleDeadline", - "IdleRequestOptions", - "InputEvent", - "KeyboardEvent", - "Location", - "MediaQueryList", - "MediaQueryListEvent", - "MouseEvent", - "Navigator", - "PointerEvent", - "ReadableStream", - "ReadableStreamDefaultReader", - "ReadableStreamReadResult", - "ResizeObserver", - "ResizeObserverBoxOptions", - "ResizeObserverEntry", - "ResizeObserverSize", - "ResizeObserverOptions", - "Screen", - "Storage", - "VisualViewport", - "Headers", - "Request", - "RequestCredentials", - "RequestInit", - "RequestRedirect", - "Response", - "WheelEvent", - "Window", -] } - -[lints.rust.unexpected_cfgs] -level = "allow" - -[lints.clippy] -dbg_macro = "deny" -todo = "deny" -declare_interior_mutable_const = "deny" -redundant_clone = "deny" -disallowed_methods = "deny" -type_complexity = "allow" -let_underscore_future = "allow" -single_range_in_vec_init = "allow" -too_many_arguments = "allow" -large_enum_variant = "allow" -nonminimal_bool = "allow" - -[lints.clippy.style] -level = "allow" -priority = -1 - -[workspace] diff --git a/crates/gpui_web/LICENSE-APACHE b/crates/gpui_web/LICENSE-APACHE deleted file mode 100644 index 461a0fe5..00000000 --- a/crates/gpui_web/LICENSE-APACHE +++ /dev/null @@ -1,222 +0,0 @@ -Copyright 2022 - 2025 Zed Industries, Inc. - - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - - http://www.apache.org/licenses/LICENSE-2.0 - - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - - -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - - 1. Definitions. - - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - - END OF TERMS AND CONDITIONS diff --git a/crates/gpui_web/src/dispatcher.rs b/crates/gpui_web/src/dispatcher.rs deleted file mode 100644 index 37ba891f..00000000 --- a/crates/gpui_web/src/dispatcher.rs +++ /dev/null @@ -1,435 +0,0 @@ -use gpui::{ - PlatformDispatcher, Priority, PriorityQueueReceiver, PriorityQueueSender, RunnableVariant, -}; -use std::cell::{Cell, RefCell}; -use std::sync::Arc; -use std::sync::atomic::AtomicI32; -use std::time::Duration; -use wasm_bindgen::prelude::*; -use web_time::Instant; - -#[cfg(feature = "multithreaded")] -const MIN_BACKGROUND_THREADS: usize = 2; - -fn shared_memory_supported() -> bool { - let global = js_sys::global(); - let has_shared_array_buffer = - js_sys::Reflect::has(&global, &JsValue::from_str("SharedArrayBuffer")).unwrap_or(false); - let has_atomics = js_sys::Reflect::has(&global, &JsValue::from_str("Atomics")).unwrap_or(false); - let memory = js_sys::WebAssembly::Memory::from(wasm_bindgen::memory()); - let buffer = memory.buffer(); - let is_shared_buffer = buffer.is_instance_of::(); - has_shared_array_buffer && has_atomics && is_shared_buffer -} - -fn wait_async_supported() -> bool { - let global = js_sys::global(); - let Ok(atomics) = js_sys::Reflect::get(&global, &JsValue::from_str("Atomics")) else { - return false; - }; - let Ok(wait_async) = js_sys::Reflect::get(&atomics, &JsValue::from_str("waitAsync")) else { - return false; - }; - - wait_async.is_function() -} - -enum MainThreadItem { - Runnable(RunnableVariant), - Delayed { - runnable: RunnableVariant, - millis: i32, - }, - Idle { - runnable: RunnableVariant, - timeout: Option, - }, - Function(Box), - // TODO-Wasm: Shouldn't these run on their own dedicated thread? - RealtimeFunction(Box), -} - -struct MainThreadMailbox { - sender: PriorityQueueSender, - receiver: parking_lot::Mutex>, - signal: AtomicI32, -} - -impl MainThreadMailbox { - fn new() -> Self { - let (sender, receiver) = PriorityQueueReceiver::new(); - Self { - sender, - receiver: parking_lot::Mutex::new(receiver), - signal: AtomicI32::new(0), - } - } - - fn post(&self, priority: Priority, item: MainThreadItem) { - if self.sender.spin_send(priority, item).is_err() { - log::error!("MainThreadMailbox::send failed: receiver disconnected"); - } - - // TODO-Wasm: Verify this lock-free protocol - let view = self.signal_view(); - js_sys::Atomics::store(&view, 0, 1).ok(); - js_sys::Atomics::notify(&view, 0).ok(); - } - - fn drain(&self, window: &web_sys::Window) { - let mut receiver = self.receiver.lock(); - loop { - // We need these `spin` variants because we can't acquire a lock on the main thread. - // TODO-WASM: Should we do something different? - match receiver.spin_try_pop() { - Ok(Some(item)) => execute_on_main_thread(window, item), - Ok(None) => break, - Err(_) => break, - } - } - } - - fn signal_view(&self) -> js_sys::Int32Array { - let byte_offset = self.signal.as_ptr() as u32; - let memory = js_sys::WebAssembly::Memory::from(wasm_bindgen::memory()); - js_sys::Int32Array::new_with_byte_offset_and_length(&memory.buffer(), byte_offset, 1) - } - - fn run_waker_loop(self: &Arc, window: web_sys::Window) { - if !shared_memory_supported() { - log::warn!("SharedArrayBuffer not available; main thread mailbox waker loop disabled"); - return; - } - - let mailbox = Arc::clone(self); - wasm_bindgen_futures::spawn_local(async move { - let view = mailbox.signal_view(); - loop { - js_sys::Atomics::store(&view, 0, 0).expect("Atomics.store failed"); - - // Items posted between the previous drain and the store above - // set the signal we just cleared, so their notify is lost. - // Drain again after re-arming to avoid missing them. - mailbox.drain(&window); - - let result = match js_sys::Atomics::wait_async(&view, 0, 0) { - Ok(result) => result, - Err(error) => { - log::error!("Atomics.waitAsync failed: {error:?}"); - break; - } - }; - - let is_async = js_sys::Reflect::get(&result, &JsValue::from_str("async")) - .ok() - .and_then(|v| v.as_bool()) - .unwrap_or(false); - - // `async: false` means the signal changed between the store and - // the wait ("not-equal"): work has already arrived, so skip - // waiting and drain immediately. - if is_async { - let promise: js_sys::Promise = - js_sys::Reflect::get(&result, &JsValue::from_str("value")) - .expect("waitAsync result missing 'value'") - .unchecked_into(); - - let _ = wasm_bindgen_futures::JsFuture::from(promise).await; - } - - mailbox.drain(&window); - } - }); - } -} - -pub struct WebDispatcher { - main_thread_id: std::thread::ThreadId, - background_sender: PriorityQueueSender, - main_thread_mailbox: Arc, - supports_threads: bool, - #[cfg(feature = "multithreaded")] - _background_threads: Vec>, -} - -impl WebDispatcher { - pub fn new(browser_window: web_sys::Window, allow_threads: bool) -> Self { - #[cfg(feature = "multithreaded")] - let (background_sender, background_receiver) = PriorityQueueReceiver::new(); - #[cfg(not(feature = "multithreaded"))] - let (background_sender, _) = PriorityQueueReceiver::new(); - - let main_thread_mailbox = Arc::new(MainThreadMailbox::new()); - - let supports_threads = cfg!(feature = "multithreaded") - && allow_threads - && shared_memory_supported() - && wait_async_supported(); - - if supports_threads { - main_thread_mailbox.run_waker_loop(browser_window.clone()); - } else if cfg!(feature = "multithreaded") && allow_threads { - log::warn!( - "Required WebAssembly threading APIs are unavailable; falling back to single-threaded dispatcher" - ); - } - - #[cfg(feature = "multithreaded")] - let background_threads = if supports_threads { - let thread_count = browser_window - .navigator() - .hardware_concurrency() - .max(MIN_BACKGROUND_THREADS as f64) as usize; - - // TODO-Wasm: Is it bad to have web workers blocking for a long time like this? - (0..thread_count) - .map(|i| { - let mut receiver = background_receiver.clone(); - wasm_thread::Builder::new() - .name(format!("background-worker-{i}")) - .spawn(move || { - loop { - let runnable: RunnableVariant = match receiver.pop() { - Ok(runnable) => runnable, - Err(_) => { - log::info!( - "background-worker-{i}: channel disconnected, exiting" - ); - break; - } - }; - - runnable.run(); - } - }) - .expect("failed to spawn background worker thread") - }) - .collect::>() - } else { - Vec::new() - }; - - Self { - main_thread_id: std::thread::current().id(), - background_sender, - main_thread_mailbox, - supports_threads, - #[cfg(feature = "multithreaded")] - _background_threads: background_threads, - } - } - - fn on_main_thread(&self) -> bool { - std::thread::current().id() == self.main_thread_id - } - - pub(crate) fn dispatch_function_on_main_thread( - &self, - function: impl FnOnce() + Send + 'static, - ) { - if self.on_main_thread() { - let callback = Closure::once_into_js(function); - browser_window().queue_microtask(callback.unchecked_ref()); - } else { - self.main_thread_mailbox - .post(Priority::High, MainThreadItem::Function(Box::new(function))); - } - } -} - -impl PlatformDispatcher for WebDispatcher { - fn is_main_thread(&self) -> bool { - self.on_main_thread() - } - - fn dispatch(&self, runnable: RunnableVariant, priority: Priority) { - if !self.supports_threads { - self.dispatch_on_main_thread(runnable, priority); - return; - } - - let result = if self.on_main_thread() { - self.background_sender.spin_send(priority, runnable) - } else { - self.background_sender.send(priority, runnable) - }; - - if let Err(error) = result { - log::error!("dispatch: failed to send to background queue: {error:?}"); - } - } - - fn dispatch_on_main_thread(&self, runnable: RunnableVariant, priority: Priority) { - if self.on_main_thread() { - schedule_runnable(&browser_window(), runnable, priority); - } else { - self.main_thread_mailbox - .post(priority, MainThreadItem::Runnable(runnable)); - } - } - - fn dispatch_after(&self, duration: Duration, runnable: RunnableVariant) { - let millis = duration.as_millis().min(i32::MAX as u128) as i32; - if self.on_main_thread() { - let callback = Closure::once_into_js(move || { - runnable.run(); - }); - browser_window() - .set_timeout_with_callback_and_timeout_and_arguments_0( - callback.unchecked_ref(), - millis, - ) - .ok(); - } else { - self.main_thread_mailbox - .post(Priority::High, MainThreadItem::Delayed { runnable, millis }); - } - } - - fn spawn_realtime(&self, function: Box) { - if self.on_main_thread() { - let callback = Closure::once_into_js(move || { - function(); - }); - browser_window().queue_microtask(callback.unchecked_ref()); - } else { - self.main_thread_mailbox - .post(Priority::High, MainThreadItem::RealtimeFunction(function)); - } - } - - fn dispatch_on_main_thread_when_idle( - &self, - runnable: RunnableVariant, - timeout: Option, - ) { - if self.on_main_thread() { - schedule_idle_runnable(&browser_window(), runnable, timeout); - } else { - self.main_thread_mailbox - .post(Priority::Low, MainThreadItem::Idle { runnable, timeout }); - } - } - - fn idle_time_remaining(&self) -> Option { - if !self.on_main_thread() { - return None; - } - IDLE_DEADLINE.with(|deadline| { - deadline - .borrow() - .as_ref() - .map(|deadline| Duration::from_secs_f64(deadline.time_remaining() / 1000.0)) - }) - } - - fn now(&self) -> Instant { - Instant::now() - } -} - -fn browser_window() -> web_sys::Window { - web_sys::window().expect("must be running in a browser window context") -} - -fn execute_on_main_thread(window: &web_sys::Window, item: MainThreadItem) { - match item { - MainThreadItem::Runnable(runnable) => { - runnable.run(); - } - MainThreadItem::Idle { runnable, timeout } => { - schedule_idle_runnable(window, runnable, timeout); - } - MainThreadItem::Delayed { runnable, millis } => { - let callback = Closure::once_into_js(move || { - runnable.run(); - }); - window - .set_timeout_with_callback_and_timeout_and_arguments_0( - callback.unchecked_ref(), - millis, - ) - .ok(); - } - MainThreadItem::Function(function) | MainThreadItem::RealtimeFunction(function) => { - function(); - } - } -} - -thread_local! { - /// The deadline of the idle callback currently running; read by - /// [`PlatformDispatcher::idle_time_remaining`] from inside the runnable. - static IDLE_DEADLINE: RefCell> = const { RefCell::new(None) }; - /// Whether `requestIdleCallback` exists (it is absent on Safari), probed - /// on first use. - static IDLE_CALLBACK_SUPPORTED: Cell> = const { Cell::new(None) }; -} - -/// Registers one `requestIdleCallback` per runnable, mirroring how -/// `dispatch_after` maps each timer to its own platform alarm. The browser -/// already provides the queue semantics a central pump would rebuild: idle -/// callbacks run in registration order, an idle period drains as many as its -/// deadline allows, and a callback whose `timeout` expires is posted as an -/// ordinary task instead. -fn schedule_idle_runnable( - window: &web_sys::Window, - runnable: RunnableVariant, - timeout: Option, -) { - if !idle_callback_supported(window) { - // Safari: run idle work as ordinary macrotasks. With no metered - // deadline, `idle_time_remaining` stays `None` and idle tasks bound - // their own slices. - schedule_runnable(window, runnable, Priority::Low); - return; - } - let callback = Closure::once_into_js(move |deadline: web_sys::IdleDeadline| { - IDLE_DEADLINE.with(|current| *current.borrow_mut() = Some(deadline)); - runnable.run(); - IDLE_DEADLINE.with(|current| *current.borrow_mut() = None); - }); - let result = match timeout { - Some(timeout) => { - let options = web_sys::IdleRequestOptions::new(); - options.set_timeout(timeout.as_millis().min(u32::MAX as u128) as u32); - window.request_idle_callback_with_options(callback.unchecked_ref(), &options) - } - None => window.request_idle_callback(callback.unchecked_ref()), - }; - if let Err(error) = result { - log::error!("requestIdleCallback failed: {error:?}"); - } -} - -fn idle_callback_supported(window: &web_sys::Window) -> bool { - IDLE_CALLBACK_SUPPORTED.with(|supported| { - if let Some(supported) = supported.get() { - return supported; - } - let probed = - js_sys::Reflect::has(window.as_ref(), &JsValue::from_str("requestIdleCallback")) - .unwrap_or(false); - supported.set(Some(probed)); - probed - }) -} - -fn schedule_runnable(window: &web_sys::Window, runnable: RunnableVariant, priority: Priority) { - let callback = Closure::once_into_js(move || { - runnable.run(); - }); - let callback: &js_sys::Function = callback.unchecked_ref(); - - match priority { - Priority::RealtimeAudio => { - window.queue_microtask(callback); - } - _ => { - // TODO-Wasm: this ought to enqueue so we can dequeue with proper priority - window - .set_timeout_with_callback_and_timeout_and_arguments_0(callback, 0) - .ok(); - } - } -} diff --git a/crates/gpui_web/src/display.rs b/crates/gpui_web/src/display.rs deleted file mode 100644 index aaf70c67..00000000 --- a/crates/gpui_web/src/display.rs +++ /dev/null @@ -1,101 +0,0 @@ -use anyhow::Result; -use gpui::{Bounds, DisplayId, Pixels, PlatformDisplay, Point, Size, px}; - -#[derive(Debug)] -pub struct WebDisplay { - id: DisplayId, - uuid: uuid::Uuid, - browser_window: web_sys::Window, -} - -// Safety: `web_sys::Window` is only accessed from the main thread. Displays -// are handed out as `Rc` and read by GPUI on the -// foreground thread; background worker threads (when the `multithreaded` -// feature is enabled) never touch them. -unsafe impl Send for WebDisplay {} -unsafe impl Sync for WebDisplay {} - -impl WebDisplay { - pub fn new(browser_window: web_sys::Window) -> Self { - WebDisplay { - id: DisplayId::new(1), - uuid: uuid::Uuid::new_v4(), - browser_window, - } - } - - fn screen_size(&self) -> Size { - let Some(screen) = self.browser_window.screen().ok() else { - return Size { - width: px(1920.), - height: px(1080.), - }; - }; - - let width = screen.width().unwrap_or(1920) as f32; - let height = screen.height().unwrap_or(1080) as f32; - - Size { - width: px(width), - height: px(height), - } - } - - fn viewport_size(&self) -> Size { - let width = self - .browser_window - .inner_width() - .ok() - .and_then(|v| v.as_f64()) - .unwrap_or(1920.0) as f32; - let height = self - .browser_window - .inner_height() - .ok() - .and_then(|v| v.as_f64()) - .unwrap_or(1080.0) as f32; - - Size { - width: px(width), - height: px(height), - } - } -} - -impl PlatformDisplay for WebDisplay { - fn id(&self) -> DisplayId { - self.id - } - - fn uuid(&self) -> Result { - Ok(self.uuid) - } - - fn bounds(&self) -> Bounds { - let size = self.screen_size(); - Bounds { - origin: Point::default(), - size, - } - } - - fn visible_bounds(&self) -> Bounds { - let size = self.viewport_size(); - Bounds { - origin: Point::default(), - size, - } - } - - fn default_bounds(&self) -> Bounds { - let visible = self.visible_bounds(); - let width = visible.size.width * 0.75; - let height = visible.size.height * 0.75; - let origin_x = (visible.size.width - width) / 2.0; - let origin_y = (visible.size.height - height) / 2.0; - Bounds { - origin: Point::new(origin_x, origin_y), - size: Size { width, height }, - } - } -} diff --git a/crates/gpui_web/src/events.rs b/crates/gpui_web/src/events.rs deleted file mode 100644 index 26ed3be0..00000000 --- a/crates/gpui_web/src/events.rs +++ /dev/null @@ -1,1494 +0,0 @@ -use std::{collections::HashMap, rc::Rc}; - -use gpui::{ - Capslock, ClipboardEntry, ClipboardItem, ClipboardString, DispatchEventResult, GestureTuning, - Image, ImageFormat, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent, - MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, - Pixels, PlatformInput, Point, ScrollDelta, ScrollWheelEvent, TouchEvent, TouchId, TouchPhase, - point, px, -}; -use wasm_bindgen::prelude::*; - -use crate::ime_mirror::ImeMirror; -use crate::window::WebWindowInner; - -pub struct WebEventListeners { - _handles: Vec, -} - -/// A DOM event listener that is removed from its target when dropped. -/// -/// Dropping the `Closure` alone would leave the listener attached to the DOM -/// pointing at a freed function; the next event would then throw "closure -/// invoked after being dropped". Keeping the target alongside the closure -/// lets `Drop` unregister the listener first. -pub(crate) struct EventListenerHandle { - target: web_sys::EventTarget, - event_name: &'static str, - closure: Closure, -} - -impl EventListenerHandle { - pub(crate) fn add( - target: &web_sys::EventTarget, - event_name: &'static str, - handler: impl FnMut(JsValue) + 'static, - ) -> Self { - let closure = Closure::::new(handler); - target - .add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref()) - .ok(); - Self { - target: target.clone(), - event_name, - closure, - } - } - - /// Registers with `{passive: false}` so that `preventDefault()` works. - /// Needed for events like `wheel` which are passive by default in modern - /// browsers. Removal does not need to match the `passive` option, so - /// `Drop` works the same as for [`EventListenerHandle::add`]. - fn add_non_passive( - target: &web_sys::EventTarget, - event_name: &'static str, - handler: impl FnMut(JsValue) + 'static, - ) -> Self { - let closure = Closure::::new(handler); - let target_js: &JsValue = target.as_ref(); - let callback_js: &JsValue = closure.as_ref(); - let options = js_sys::Object::new(); - js_sys::Reflect::set(&options, &"passive".into(), &false.into()).ok(); - if let Ok(add_fn_val) = js_sys::Reflect::get(target_js, &"addEventListener".into()) { - if let Ok(add_fn) = add_fn_val.dyn_into::() { - add_fn - .call3(target_js, &event_name.into(), callback_js, &options) - .ok(); - } - } - Self { - target: target.clone(), - event_name, - closure, - } - } -} - -impl Drop for EventListenerHandle { - fn drop(&mut self) { - self.target - .remove_event_listener_with_callback( - self.event_name, - self.closure.as_ref().unchecked_ref(), - ) - .ok(); - } -} - -pub(crate) struct ClickState { - last_position: Point, - last_time: f64, - current_count: usize, -} - -#[derive(Default)] -pub(crate) struct TouchIds { - next: u64, - active: HashMap, -} - -impl TouchIds { - fn start(&mut self, pointer_id: i32) -> Option { - let next = self.next.checked_add(1)?; - let touch_id = TouchId(self.next); - self.next = next; - self.active.insert(pointer_id, touch_id); - Some(touch_id) - } - - fn active(&self, pointer_id: i32) -> Option { - self.active.get(&pointer_id).copied() - } - - fn end(&mut self, pointer_id: i32) -> Option { - self.active.remove(&pointer_id) - } -} - -impl Default for ClickState { - fn default() -> Self { - Self { - last_position: Point::default(), - last_time: 0.0, - current_count: 0, - } - } -} - -impl ClickState { - fn register_click(&mut self, position: Point, time: f64) -> usize { - let distance = ((f32::from(position.x) - f32::from(self.last_position.x)).powi(2) - + (f32::from(position.y) - f32::from(self.last_position.y)).powi(2)) - .sqrt(); - - if (time - self.last_time) < 400.0 && distance < 5.0 { - self.current_count += 1; - } else { - self.current_count = 1; - } - - self.last_position = position; - self.last_time = time; - self.current_count - } -} - -impl WebWindowInner { - pub fn register_event_listeners(self: &Rc) -> WebEventListeners { - let mut handles = vec![ - self.register_pointer_down(), - self.register_pointer_up(), - self.register_pointer_cancel(), - self.register_touch_end(), - self.register_pointer_move(), - self.register_pointer_leave(), - self.register_wheel(), - self.register_context_menu(), - self.register_dragover(), - self.register_drop(), - self.register_key_down(), - self.register_key_up(), - self.register_before_input(), - self.register_input(), - self.register_paste(), - self.register_composition_start(), - self.register_composition_update(), - self.register_composition_end(), - self.register_focus(), - self.register_blur(), - self.register_pointer_enter(), - ]; - handles.extend(self.register_selection_change()); - handles.extend(self.register_visibility_change()); - handles.extend(self.register_appearance_change()); - handles.extend(self.register_fullscreen_change()); - - WebEventListeners { _handles: handles } - } - - fn listen( - self: &Rc, - event_name: &'static str, - handler: impl FnMut(JsValue) + 'static, - ) -> EventListenerHandle { - EventListenerHandle::add(self.canvas.as_ref(), event_name, handler) - } - - fn listen_input( - self: &Rc, - event_name: &'static str, - handler: impl FnMut(JsValue) + 'static, - ) -> EventListenerHandle { - EventListenerHandle::add(self.ime_mirror.event_target(), event_name, handler) - } - - fn listen_non_passive( - self: &Rc, - event_name: &'static str, - handler: impl FnMut(JsValue) + 'static, - ) -> EventListenerHandle { - EventListenerHandle::add_non_passive(self.canvas.as_ref(), event_name, handler) - } - - fn dispatch_input(&self, input: PlatformInput) -> Option { - self.with_callback(|callbacks| &mut callbacks.input, |callback| callback(input)) - } - - /// Records the latest modifier state and reports whether it changed, so - /// that `ModifiersChanged` is only dispatched on actual transitions - /// rather than for every key event. - fn update_modifiers(&self, modifiers: Modifiers, capslock: Capslock) -> bool { - let mut current_state = self.state.borrow_mut(); - let changed = current_state.modifiers != modifiers || current_state.capslock != capslock; - current_state.modifiers = modifiers; - current_state.capslock = capslock; - changed - } - - fn register_pointer_down(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen("pointerdown", move |event: JsValue| { - let event: web_sys::PointerEvent = event.unchecked_into(); - event.prevent_default(); - - let pointer_type = event.pointer_type(); - let position = pointer_position_in_element(&event); - this.gesture_start_visual_viewport_height - .set(this.visual_viewport_height()); - - // Capture the pointer so drags that leave the canvas keep - // delivering pointermove/pointerup here; otherwise a release - // outside the canvas is never seen and `pressed_button` stays - // stuck. The capture is released implicitly on pointerup. - this.canvas.set_pointer_capture(event.pointer_id()).ok(); - - if pointer_type == "touch" { - let Some(touch_id) = this.touch_ids.borrow_mut().start(event.pointer_id()) else { - log::error!("exhausted touch identifiers"); - return; - }; - this.state.borrow_mut().mouse_position = position; - if this.touch_tap_candidate.get().is_none() { - this.touch_tap_candidate - .set(Some((event.pointer_id(), position))); - } - this.dispatch_input(PlatformInput::Touch(TouchEvent { - id: touch_id, - phase: TouchPhase::Started, - position, - predicted_position: None, - force: None, - })); - // Keyboard and IME focus intentionally do not change here: - // whether this touch is a tap or a pan is only known at - // release, and only a tap may affect them (see - // `touch_tap_candidate`). The release handler still runs - // within a user gesture, as keyboard summoning requires. - return; - } - - let button = dom_mouse_button_to_gpui(event.button()); - let modifiers = modifiers_from_mouse_event(&event, this.is_mac); - let time = js_sys::Date::now(); - - this.pressed_button.set(Some(button)); - let click_count = this.click_state.borrow_mut().register_click(position, time); - - { - let mut current_state = this.state.borrow_mut(); - current_state.mouse_position = position; - current_state.modifiers = modifiers; - } - - this.dispatch_input(PlatformInput::MouseDown(MouseDownEvent { - button, - position, - modifiers, - click_count, - first_mouse: false, - })); - - this.ime_mirror.focus(); - }) - } - - fn pointer_targets_text_input(&self, position: Point) -> bool { - self.with_input_handler(|handler| { - handler.query_accepts_text_input() - && handler - .element_bounds() - .is_some_and(|bounds| bounds.contains(&position)) - }) - .unwrap_or(false) - } - - fn focused_input_accepts_text(&self) -> bool { - self.with_input_handler(|handler| handler.query_accepts_text_input()) - .unwrap_or(false) - } - - fn register_pointer_up(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen("pointerup", move |event: JsValue| { - let event: web_sys::PointerEvent = event.unchecked_into(); - event.prevent_default(); - - let position = pointer_position_in_element(&event); - - if event.pointer_type() == "touch" { - let Some(touch_id) = this.touch_ids.borrow_mut().end(event.pointer_id()) else { - return; - }; - this.state.borrow_mut().mouse_position = position; - let completes_tap = match this.touch_tap_candidate.get() { - Some((pointer_id, _)) if pointer_id == event.pointer_id() => { - this.touch_tap_candidate.set(None); - true - } - _ => false, - }; - let focused_input_accepted_text_before_tap = this.focused_input_accepts_text(); - // A recognized tap is dispatched synchronously inside this - // call, so the text-input check below sees the state the tap - // produced. - let dispatch_result = this.dispatch_input(PlatformInput::Touch(TouchEvent { - id: touch_id, - phase: TouchPhase::Ended, - position, - predicted_position: None, - force: None, - })); - - // A keyboard opening or closing mid-gesture reflows the - // layout, so the release position no longer refers to the - // content the user aimed at (a tap that summoned the keyboard - // often ends up below the shrunken layout, which would - // immediately dismiss it again). Skip the sync then, and for - // anything that wasn't a tap: pans and flings must not move - // keyboard or IME focus at all. - let viewport_stable = this.gesture_start_visual_viewport_height.get() - == this.visual_viewport_height(); - if completes_tap && viewport_stable { - let preserve_focused_input = should_preserve_focused_input( - focused_input_accepted_text_before_tap, - this.focused_input_accepts_text(), - dispatch_result, - ); - if !preserve_focused_input { - this.sync_virtual_keyboard(this.pointer_targets_text_input(position)); - } - } - this.schedule_ime_mirror_sync(); - return; - } - - let button = dom_mouse_button_to_gpui(event.button()); - let modifiers = modifiers_from_mouse_event(&event, this.is_mac); - - this.pressed_button.set(None); - let click_count = this.click_state.borrow().current_count; - - { - let mut current_state = this.state.borrow_mut(); - current_state.mouse_position = position; - current_state.modifiers = modifiers; - } - - this.dispatch_input(PlatformInput::MouseUp(MouseUpEvent { - button, - position, - modifiers, - click_count, - })); - - this.schedule_ime_mirror_sync(); - }) - } - - /// The visual viewport's current height in layout pixels, or zero when - /// the API is unavailable. - fn visual_viewport_height(&self) -> f64 { - self.browser_window - .visual_viewport() - .map_or(0.0, |viewport| viewport.height() * viewport.scale()) - } - - /// Whether the software keyboard is likely hidden — a heuristic, since - /// no cross-browser keyboard-visibility signal exists. It infers from - /// the visual viewport: a shown keyboard shrinks its height well below - /// the greatest height seen at the current width (the width only changes - /// on rotation, which restarts the calibration). `window.innerHeight` - /// can't serve as the reference because Android shrinks it along with - /// the keyboard. Unknown states err toward "visible" so ordinary - /// editable taps don't gratuitously restart the IME session. - /// - /// Restricted to coarse-pointer environments: elsewhere (desktop - /// browsers, including touchscreen laptops) viewport height tracks - /// user window resizes rather than a software keyboard, so the - /// calibration would misfire. Split-screen resizes on mobile can still - /// fool it; tracking `visualViewport` resize events around focus - /// transitions would be sturdier. - fn keyboard_likely_dismissed(&self) -> bool { - let coarse_pointer = self - .browser_window - .match_media("(pointer: coarse)") - .ok() - .flatten() - .is_some_and(|media_query_list| media_query_list.matches()); - if !coarse_pointer { - return false; - } - let Some(viewport) = self.browser_window.visual_viewport() else { - return false; - }; - let width = viewport.width() * viewport.scale(); - let height = viewport.height() * viewport.scale(); - let (probe_width, probe_height) = self.visual_viewport_probe.get(); - let max_height = if width == probe_width { - probe_height.max(height) - } else { - height - }; - self.visual_viewport_probe.set((width, max_height)); - height >= max_height * 0.85 - } - - /// The browser or OS took over the pointer (native scrolling, a system - /// gesture, the pointer being removed): no pointerup will follow, so the - /// gesture must unwind rather than complete. - fn register_pointer_cancel(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen("pointercancel", move |event: JsValue| { - let event: web_sys::PointerEvent = event.unchecked_into(); - if event.pointer_type() == "touch" { - let Some(touch_id) = this.touch_ids.borrow_mut().end(event.pointer_id()) else { - return; - }; - if let Some((pointer_id, _)) = this.touch_tap_candidate.get() - && pointer_id == event.pointer_id() - { - this.touch_tap_candidate.set(None); - } - this.dispatch_input(PlatformInput::Touch(TouchEvent { - id: touch_id, - phase: TouchPhase::Cancelled, - position: pointer_position_in_element(&event), - predicted_position: None, - force: None, - })); - } else { - this.pressed_button.set(None); - } - }) - } - - /// Cancels touch default handling separately because iOS does not consistently - /// transfer pointer-event cancellation to the corresponding touch event. - fn register_touch_end(self: &Rc) -> EventListenerHandle { - self.listen_non_passive("touchend", move |event: JsValue| { - let event: web_sys::Event = event.unchecked_into(); - event.prevent_default(); - }) - } - - /// See [`ImeMirror::schedule_sync`]. - fn schedule_ime_mirror_sync(self: &Rc) { - ImeMirror::schedule_sync(self); - } - - /// Aligns the software keyboard with the text input targeted by a touch tap. - /// - /// Mobile browsers show the keyboard only when an editable element is - /// focused from within a user gesture, so this runs while the tap's - /// `pointerup` is still on the stack (by which point GPUI has usually - /// painted a frame since the `MouseDown`, so the input handler reflects - /// the tap's focus change). `readOnly` suppresses the keyboard while - /// keeping the hidden input available to the IME. Leaving it blurred after - /// a non-editable tap lets the next editable tap establish a new input - /// session instead of relying on a same-task blur/focus cycle, which iOS - /// may coalesce. - /// - /// We don't use `navigator.virtualKeyboard` here because it's - /// Chromium-only. - pub(crate) fn sync_virtual_keyboard(self: &Rc, editable: bool) { - let was_editable = !self.ime_mirror.read_only(); - self.ime_mirror.set_read_only(!editable); - // Trigger a focus event only when the keyboard actually needs - // summoning. Cycling focus on every tap would restart the IME - // connection right as the keyboard reads the tapped caret's context, - // racing its word segmentation. But `focus()` on an already-focused - // element is a no-op, so a dismissed keyboard would otherwise never - // return for taps that stay within editable content: detect that - // through the visual viewport and force a fresh focus event. - let editable_needs_focus_event = editable - && (!was_editable || !self.ime_mirror.is_focused() || self.keyboard_likely_dismissed()); - if editable_needs_focus_event || (!editable && was_editable) { - self.suppress_focus_status_events.set(true); - if editable { - // A same-task blur/focus cycle may be coalesced by iOS, but - // this branch only runs when the keyboard is already gone, - // so a coalesced cycle loses nothing. - if self.ime_mirror.is_focused() { - self.ime_mirror.blur(); - } - self.ime_mirror.focus(); - } else { - self.ime_mirror.blur(); - } - self.suppress_focus_status_events.set(false); - - if editable { - let callback = wasm_bindgen::closure::Closure::once_into_js({ - let this = Rc::clone(self); - move || { - this.state.borrow_mut().is_active = true; - this.with_callback( - |callbacks| &mut callbacks.active_status_change, - |callback| callback(true), - ); - } - }); - if let Err(error) = self - .browser_window - .set_timeout_with_callback(callback.unchecked_ref()) - { - log::warn!("failed to defer web window activation: {error:?}"); - } - } - } - } - - /// Dispatches a full key press for editing intents that arrive without a - /// usable key event (Android IMEs send `key: "Unidentified"` placeholders - /// and express backspace/enter through `beforeinput` instead), so they - /// run through the same keybinding path as hardware keys. - fn dispatch_synthetic_keystroke(&self, key: &str, modifiers: Modifiers) { - let keystroke = Keystroke { - modifiers, - key: key.to_string(), - key_char: None, - }; - self.dispatch_input(PlatformInput::KeyDown(KeyDownEvent { - keystroke: keystroke.clone(), - is_held: false, - prefer_character_input: false, - })); - self.dispatch_input(PlatformInput::KeyUp(KeyUpEvent { keystroke })); - } - - fn register_pointer_move(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen("pointermove", move |event: JsValue| { - let event: web_sys::PointerEvent = event.unchecked_into(); - event.prevent_default(); - - let position = pointer_position_in_element(&event); - - if event.pointer_type() == "touch" { - let Some(touch_id) = this.touch_ids.borrow().active(event.pointer_id()) else { - return; - }; - this.state.borrow_mut().mouse_position = position; - // Mirror the slop rule of gpui's tap recognizer: once the - // touch travels beyond it, its release must not affect the - // keyboard. Only gpui knows what the gesture truly resolved - // to; this platform-side shadow exists because the keyboard - // decision must be made synchronously inside the browser's - // pointerup handler. - if let Some((pointer_id, start_position)) = this.touch_tap_candidate.get() - && pointer_id == event.pointer_id() - && (position - start_position).magnitude() - > f64::from(GestureTuning::default().touch_slop) - { - this.touch_tap_candidate.set(None); - } - this.dispatch_input(PlatformInput::Touch(TouchEvent { - id: touch_id, - phase: TouchPhase::Moved, - position, - predicted_position: predicted_pointer_position(&event, position), - force: None, - })); - return; - } - - let modifiers = modifiers_from_mouse_event(&event, this.is_mac); - let current_pressed = this.pressed_button.get(); - - { - let mut current_state = this.state.borrow_mut(); - current_state.mouse_position = position; - current_state.modifiers = modifiers; - } - - this.dispatch_input(PlatformInput::MouseMove(MouseMoveEvent { - position, - pressed_button: current_pressed, - modifiers, - })); - }) - } - - fn register_pointer_leave(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen("pointerleave", move |event: JsValue| { - let event: web_sys::PointerEvent = event.unchecked_into(); - - let position = pointer_position_in_element(&event); - let modifiers = modifiers_from_mouse_event(&event, this.is_mac); - let current_pressed = this.pressed_button.get(); - - { - let mut current_state = this.state.borrow_mut(); - current_state.mouse_position = position; - current_state.modifiers = modifiers; - current_state.is_hovered = false; - } - - this.dispatch_input(PlatformInput::MouseExited(MouseExitEvent { - position, - pressed_button: current_pressed, - modifiers, - })); - - this.with_callback( - |callbacks| &mut callbacks.hover_status_change, - |callback| callback(false), - ); - }) - } - - fn register_wheel(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen_non_passive("wheel", move |event: JsValue| { - let event: web_sys::WheelEvent = event.unchecked_into(); - event.prevent_default(); - - let mouse_event: &web_sys::MouseEvent = event.as_ref(); - let position = mouse_position_in_element(mouse_event); - let modifiers = modifiers_from_wheel_event(mouse_event, this.is_mac); - - // HeroGPUI fork: shift+wheel scrolls horizontally. - // - // A mouse wheel reports only `deltaY`, and every desktop platform - // treats shift+wheel as the horizontal axis; upstream passes the - // raw axes through, so a horizontally scrollable region (Tabs - // overflow, Table, the gallery's code blocks) cannot be scrolled - // at all with a wheel in the browser. A trackpad already sends a - // real `deltaX`, so the swap is taken only when `deltaX` is zero - // and the true horizontal gesture is left untouched. - let (delta_x, delta_y) = if modifiers.shift && event.delta_x() == 0.0 { - (event.delta_y(), 0.0) - } else { - (event.delta_x(), event.delta_y()) - }; - let delta_mode = event.delta_mode(); - let delta = if delta_mode == 1 { - ScrollDelta::Lines(point(-delta_x as f32, -delta_y as f32)) - } else { - ScrollDelta::Pixels(point(px(-delta_x as f32), px(-delta_y as f32))) - }; - - { - let mut current_state = this.state.borrow_mut(); - current_state.modifiers = modifiers; - } - - this.dispatch_input(PlatformInput::ScrollWheel(ScrollWheelEvent { - position, - delta, - modifiers, - touch_phase: TouchPhase::Moved, - })); - }) - } - - fn register_context_menu(self: &Rc) -> EventListenerHandle { - self.listen("contextmenu", move |event: JsValue| { - let event: web_sys::Event = event.unchecked_into(); - event.prevent_default(); - }) - } - - /// Browsers only expose dropped files as `File` objects, never as - /// filesystem paths, so no `FileDrop` input can be synthesized: GPUI's - /// `ExternalPaths` consumers would try to read paths that don't exist. - /// The events are still intercepted so the browser doesn't navigate to - /// the dropped file. Delivering actual file drops would require plumbing - /// `File` contents through a web-specific channel. - fn register_dragover(self: &Rc) -> EventListenerHandle { - self.listen("dragover", move |event: JsValue| { - let event: web_sys::DragEvent = event.unchecked_into(); - event.prevent_default(); - }) - } - - fn register_drop(self: &Rc) -> EventListenerHandle { - self.listen("drop", move |event: JsValue| { - let event: web_sys::DragEvent = event.unchecked_into(); - event.prevent_default(); - }) - } - - fn register_key_down(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen_input("keydown", move |event: JsValue| { - let event: web_sys::KeyboardEvent = event.unchecked_into(); - - let modifiers = modifiers_from_keyboard_event(&event, this.is_mac); - let capslock = capslock_from_keyboard_event(&event); - - if this.update_modifiers(modifiers, capslock) { - this.dispatch_input(PlatformInput::ModifiersChanged(ModifiersChangedEvent { - modifiers, - capslock, - })); - } - - let key = dom_key_to_gpui_key(&event); - - if is_modifier_only_key(&key) { - return; - } - - let is_held = event.repeat(); - let key_char = compute_key_char(&event, &key, &modifiers); - - let keystroke = Keystroke { - modifiers, - key, - key_char: key_char.clone(), - }; - - let result = this.dispatch_input(PlatformInput::KeyDown(KeyDownEvent { - keystroke, - is_held, - prefer_character_input: false, - })); - - if let Some(result) = result { - if !result.propagate { - event.prevent_default(); - this.schedule_ime_mirror_sync(); - return; - } - } - - if this.is_composing.get() || event.is_composing() { - event.prevent_default(); - return; - } - - if keystroke_inserts_text(&modifiers, this.is_mac) - && let Some(text) = key_char - { - this.with_input_handler(|handler| { - handler.replace_text_in_range(None, &text); - }); - // The character went into the input handler; suppress browser - // side-effects for the same keystroke (space scrolling the - // page, quick-find, etc.). Everything not handled above falls - // through so browser shortcuts keep their defaults. - event.prevent_default(); - } - this.schedule_ime_mirror_sync(); - }) - } - - fn register_key_up(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen_input("keyup", move |event: JsValue| { - let event: web_sys::KeyboardEvent = event.unchecked_into(); - - let modifiers = modifiers_from_keyboard_event(&event, this.is_mac); - let capslock = capslock_from_keyboard_event(&event); - - if this.update_modifiers(modifiers, capslock) { - this.dispatch_input(PlatformInput::ModifiersChanged(ModifiersChangedEvent { - modifiers, - capslock, - })); - } - - let key = dom_key_to_gpui_key(&event); - - if is_modifier_only_key(&key) { - return; - } - - let key_char = compute_key_char(&event, &key, &modifiers); - - let keystroke = Keystroke { - modifiers, - key, - key_char, - }; - - let result = this.dispatch_input(PlatformInput::KeyUp(KeyUpEvent { keystroke })); - if let Some(result) = result { - if !result.propagate { - event.prevent_default(); - } - } - }) - } - - /// Imports IME edits from the hidden input into the app. - /// - /// Text-editing `beforeinput` events are deliberately left uncancelled, - /// so the browser applies them to the mirror element exactly as the IME - /// expects (cancelling them and echoing the edit back programmatically - /// restarts the IME connection on every keystroke, which desynchronizes - /// the keyboard's internal state — e.g. Gboard then swallows backspaces - /// against a stale private buffer). The resulting `input` event is - /// diffed against the last known mirror text; IME edits are contiguous, - /// so a common prefix/suffix diff recovers them exactly. - /// - /// The diff supplies only the *shape* of the edit — how many UTF-16 - /// units were removed before/after the element's pre-edit selection and - /// what text replaced them. It never supplies document coordinates: - /// mirror offsets captured at sync time go stale whenever the document - /// changes underneath (this is a live collaborative document). The - /// position comes from `selected_text_range()` queried in this same - /// synchronous callback — the editor resolves its selection through - /// anchors, so the freshly-fetched offsets are exact, and nothing can - /// run between the query and the edit below. - fn register_input(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen_input("input", move |event: JsValue| { - let event: web_sys::InputEvent = event.unchecked_into(); - - // Composition text is delivered through the composition events; - // the mirror is reconciled once on compositionend. - if this.is_composing.get() || event.is_composing() { - return; - } - - let new_value = this.ime_mirror.value(); - let old_value = this.ime_mirror.stored_text(); - if new_value == old_value { - return; - } - - let old_units: Vec = old_value.encode_utf16().collect(); - let new_units: Vec = new_value.encode_utf16().collect(); - - // A prefix/suffix diff is ambiguous when the inserted text - // shares characters with what follows it (inserting "pactor " - // before "pact" also reads as inserting "or pact" four units - // later). The edit's true position is not ambiguous: the browser - // leaves the caret at the end of an IME edit, so the suffix is - // anchored as "everything after the post-edit caret", and the - // prefix is capped to fit. Greedy matching is only a fallback - // for edits where the anchored suffix doesn't verify. - let post_edit_caret = this - .ime_mirror - .selection_start() - .map(|caret| caret as usize); - let anchored_suffix_length = post_edit_caret - .map(|caret| new_units.len().saturating_sub(caret)) - .filter(|&suffix_length| { - suffix_length <= old_units.len() - && old_units[old_units.len() - suffix_length..] - == new_units[new_units.len() - suffix_length..] - }); - let suffix_length = anchored_suffix_length.unwrap_or_else(|| { - old_units - .iter() - .rev() - .zip(new_units.iter().rev()) - .take_while(|(old_unit, new_unit)| old_unit == new_unit) - .count() - }); - let prefix_length = old_units - .iter() - .zip(&new_units) - .take_while(|(old_unit, new_unit)| old_unit == new_unit) - .count() - .min(old_units.len() - suffix_length) - .min(new_units.len() - suffix_length); - - let inserted_text = String::from_utf16_lossy( - &new_units[prefix_length..new_units.len() - suffix_length], - ); - let replaced_old_end = old_units.len() - suffix_length; - - // The edit's shape relative to the element's pre-edit selection. - // The element is private to the IME and these syncs, so the - // stored selection is exact. - let (element_selection_start, element_selection_end) = - this.ime_mirror.stored_selection(); - let removed_before_selection = - (element_selection_start as usize).saturating_sub(prefix_length); - let removed_after_selection = - replaced_old_end.saturating_sub(element_selection_end as usize); - - let applied = this.with_input_handler(|handler| { - let Some(selection) = handler.selected_text_range(false) else { - return false; - }; - let range = selection - .range - .start - .saturating_sub(removed_before_selection) - ..selection.range.end + removed_after_selection; - handler.replace_text_in_range(Some(range), &inserted_text); - true - }); - if applied != Some(true) { - return; - } - - this.ime_mirror.adopt_element_state(); - }) - } - - /// Imports IME-driven selection moves on the mirror element into the app. - /// - /// Some IME gestures preview their effect by moving the field's - /// selection before committing an edit — Android's slide-on-backspace - /// grows a selection over the text it will delete. A native field - /// renders that selection itself; this import gives the app the same - /// chance. Like edit imports, the move is expressed relative to the - /// element's stored selection and applied to the app selection queried - /// in the same synchronous callback, never through document coordinates, - /// which go stale in a collaborative document. - /// - /// Self-inflicted events are filtered by state, not by suppression - /// flags: `selectionchange` dispatches asynchronously, after the sync or - /// import that caused it has already adopted the element's selection, so - /// a stored-state match means there is nothing to import. - /// - /// Registered on the document: Chrome dispatches text-control selection - /// changes there, not on the element. - fn register_selection_change(self: &Rc) -> Option { - let document = self.browser_window.document()?; - let this = Rc::clone(self); - Some(EventListenerHandle::add( - document.as_ref(), - "selectionchange", - move |_event: JsValue| { - if this.is_composing.get() || !this.ime_mirror.is_focused() { - return; - } - // An in-flight edit owns the selection; its import adopts it. - if this.ime_mirror.value() != this.ime_mirror.stored_text() { - return; - } - let Some(element_start) = this.ime_mirror.selection_start() else { - return; - }; - let element_end = this - .ime_mirror - .element_selection_end() - .unwrap_or(element_start); - let (stored_start, stored_end) = this.ime_mirror.stored_selection(); - if (element_start, element_end) == (stored_start, stored_end) { - return; - } - let applied = this.with_input_handler(|handler| { - let Some(selection) = handler.selected_text_range(false) else { - return false; - }; - // The app range corresponding to the stored element - // selection is exactly `selection`; a single consistent - // alignment between the two maps the moved endpoints. - // Disagreeing alignments mean the app selection changed - // underneath and the pending resync owns the element. - let alignment = selection.range.start.checked_sub(stored_start as usize); - if alignment.is_none() - || alignment != selection.range.end.checked_sub(stored_end as usize) - { - return false; - } - let alignment = alignment.unwrap(); - handler.set_selected_text_range( - alignment + element_start as usize..alignment + element_end as usize, - ); - true - }); - if applied == Some(true) { - this.ime_mirror.adopt_element_state(); - } else { - // No import is coming for this move; without a forced - // sync the mirror would keep deferring to it and show - // the IME a selection the app never adopted. - this.ime_mirror.reject_selection_import(); - this.schedule_ime_mirror_sync(); - } - }, - )) - } - - /// Software keyboards (IMEs) express editing through `beforeinput` - /// rather than key events: Android IMEs emit only a placeholder key - /// event (`key: "Unidentified"`, `keyCode` 229). This handler only - /// intercepts the intents that must not mutate the mirror element; - /// ordinary edits deliberately proceed to the element and are imported - /// by `register_input`. Desktop keystrokes never reach this handler, - /// because `register_key_down` calls `preventDefault()` for every - /// keystroke it inserts, which cancels the corresponding `beforeinput`. - fn register_before_input(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen_input("beforeinput", move |event: JsValue| { - let event: web_sys::InputEvent = event.unchecked_into(); - - // During composition the composition{update,end} handlers own - // the text. - if this.is_composing.get() || event.is_composing() { - return; - } - - match event.input_type().as_str() { - // Enter means "submit", not "insert a newline into the - // mirror": run it through the keybinding path instead of - // letting it mutate the element. - "insertLineBreak" | "insertParagraph" => { - event.prevent_default(); - this.dispatch_synthetic_keystroke("enter", Modifiers::default()); - this.schedule_ime_mirror_sync(); - } - // Everything else (insertText, deleteContent*, autocorrect's - // insertReplacementText, ...) is left to the browser's - // default action on the mirror element; `register_input` - // imports the resulting element diff into the editor. - _ => {} - } - }) - } - - /// Paste is delivered through the DOM `paste` event rather than - /// `Platform::read_from_clipboard`: the browser's asynchronous clipboard - /// read API cannot fit that synchronous signature, while `ClipboardEvent` - /// exposes `clipboardData` synchronously inside the event. It fires for - /// any browser-initiated paste (keyboard, menu bar, context menu). - /// - /// Text-only pastes reach the input handler synchronously. Pasted image - /// files only expose their bytes through asynchronous blob reads, so - /// pastes containing images are delivered once those reads resolve — to - /// whichever input handler is focused at that point, matching how an - /// application-level paste action would behave. - fn register_paste(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen_input("paste", move |event: JsValue| { - let event: web_sys::ClipboardEvent = event.unchecked_into(); - let Some(clipboard_data) = event.clipboard_data() else { - return; - }; - let text = clipboard_data - .get_data("text/plain") - .ok() - .filter(|text| !text.is_empty()); - - // File handles must be collected synchronously: the browser - // clears `clipboardData`'s item list once this handler returns, - // while the `File`s themselves stay readable afterwards. - let mut image_files = Vec::new(); - let items = clipboard_data.items(); - for index in 0..items.length() { - let Some(item) = items.get(index) else { - continue; - }; - if item.kind() != "file" { - continue; - } - let Some(format) = ImageFormat::from_mime_type(&item.type_()) else { - continue; - }; - if let Ok(Some(file)) = item.get_as_file() { - image_files.push((format, file)); - } - } - - if text.is_none() && image_files.is_empty() { - return; - } - event.prevent_default(); - - if image_files.is_empty() { - if let Some(text) = text { - this.with_input_handler(|handler| { - handler.paste(ClipboardItem::new_string(text)); - }); - // HeroGPUI fork: paste cancels the DOM edit, so refresh - // the mirror from the application's replacement before - // the next IME edit. Without this the hidden textarea - // still holds the pre-paste text, and the next - // composition or `beforeinput` diff is computed against - // stale content -- pasted text gets duplicated or eaten. - this.schedule_ime_mirror_sync(); - } - return; - } - - let this = Rc::clone(&this); - wasm_bindgen_futures::spawn_local(async move { - let mut entries = Vec::new(); - if let Some(text) = text { - entries.push(ClipboardEntry::String(ClipboardString::new(text))); - } - for (format, file) in image_files { - match crate::platform::read_blob_bytes(&file).await { - Ok(bytes) => { - entries.push(ClipboardEntry::Image(Image::from_bytes(format, bytes))); - } - Err(error) => { - log::error!( - "failed to read pasted image: {}", - crate::platform::js_error_message(&error) - ); - } - } - } - if entries.is_empty() { - return; - } - this.with_input_handler(|handler| { - handler.paste(ClipboardItem { entries }); - }); - // Same fork as the text-only path above, for the async - // image/mixed paste that resolves a frame or more later. - this.schedule_ime_mirror_sync(); - }); - }) - } - - fn register_composition_start(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen_input("compositionstart", move |_event: JsValue| { - this.is_composing.set(true); - }) - } - - fn register_composition_update(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen_input("compositionupdate", move |event: JsValue| { - let event: web_sys::CompositionEvent = event.unchecked_into(); - let data = event.data().unwrap_or_default(); - this.is_composing.set(true); - this.with_input_handler(|handler| { - handler.replace_and_mark_text_in_range(None, &data, None); - }); - }) - } - - fn register_composition_end(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen_input("compositionend", move |event: JsValue| { - let event: web_sys::CompositionEvent = event.unchecked_into(); - let data = event.data().unwrap_or_default(); - this.is_composing.set(false); - this.with_input_handler(|handler| { - // Only commit the final text when a marked range still - // exists. When a caret move ended the composition, the - // editor has already unmarked (keeping the composed text as - // committed content); inserting `data` at the selection - // would duplicate the word at the new caret position. - if handler.marked_text_range().is_some() { - handler.replace_text_in_range(None, &data); - } - handler.unmark_text(); - }); - // Adopt the element's post-composition state as the mirror - // baseline without writing anything: the browser applied the - // commit to the element itself, and a write here would restart - // the IME mid-commit. The deferred sync reconciles any - // app-side divergence afterwards. - this.ime_mirror.adopt_element_state(); - this.schedule_ime_mirror_sync(); - }) - } - - fn register_focus(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen_input("focus", move |_event: JsValue| { - if this.suppress_focus_status_events.get() { - return; - } - { - let mut state = this.state.borrow_mut(); - state.is_active = true; - } - this.with_callback( - |callbacks| &mut callbacks.active_status_change, - |callback| callback(true), - ); - }) - } - - fn register_blur(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen_input("blur", move |_event: JsValue| { - if this.suppress_focus_status_events.get() { - return; - } - { - let mut state = this.state.borrow_mut(); - state.is_active = false; - } - this.with_callback( - |callbacks| &mut callbacks.active_status_change, - |callback| callback(false), - ); - }) - } - - fn register_pointer_enter(self: &Rc) -> EventListenerHandle { - let this = Rc::clone(self); - self.listen("pointerenter", move |_event: JsValue| { - { - let mut state = this.state.borrow_mut(); - state.is_hovered = true; - } - this.with_callback( - |callbacks| &mut callbacks.hover_status_change, - |callback| callback(true), - ); - }) - } -} - -fn dom_key_to_gpui_key(event: &web_sys::KeyboardEvent) -> String { - let key = event.key(); - match key.as_str() { - "Enter" => "enter".to_string(), - "Backspace" => "backspace".to_string(), - "Tab" => "tab".to_string(), - "Escape" => "escape".to_string(), - "Delete" => "delete".to_string(), - " " => "space".to_string(), - "ArrowLeft" => "left".to_string(), - "ArrowRight" => "right".to_string(), - "ArrowUp" => "up".to_string(), - "ArrowDown" => "down".to_string(), - "Home" => "home".to_string(), - "End" => "end".to_string(), - "PageUp" => "pageup".to_string(), - "PageDown" => "pagedown".to_string(), - "Insert" => "insert".to_string(), - "Control" => "control".to_string(), - "Alt" => "alt".to_string(), - "Shift" => "shift".to_string(), - "Meta" => "platform".to_string(), - "CapsLock" => "capslock".to_string(), - other => { - if let Some(rest) = other.strip_prefix('F') { - if let Ok(number) = rest.parse::() { - if (1..=35).contains(&number) { - return format!("f{number}"); - } - } - } - other.to_lowercase() - } - } -} - -fn dom_mouse_button_to_gpui(button: i16) -> MouseButton { - match button { - 0 => MouseButton::Left, - 1 => MouseButton::Middle, - 2 => MouseButton::Right, - 3 => MouseButton::Navigate(NavigationDirection::Back), - 4 => MouseButton::Navigate(NavigationDirection::Forward), - _ => MouseButton::Left, - } -} - -fn modifiers_from_keyboard_event(event: &web_sys::KeyboardEvent, _is_mac: bool) -> Modifiers { - Modifiers { - control: event.ctrl_key(), - alt: event.alt_key(), - shift: event.shift_key(), - platform: event.meta_key(), - function: false, - } -} - -fn modifiers_from_mouse_event(event: &web_sys::PointerEvent, _is_mac: bool) -> Modifiers { - let mouse_event: &web_sys::MouseEvent = event.as_ref(); - Modifiers { - control: mouse_event.ctrl_key(), - alt: mouse_event.alt_key(), - shift: mouse_event.shift_key(), - platform: mouse_event.meta_key(), - function: false, - } -} - -fn modifiers_from_wheel_event(event: &web_sys::MouseEvent, _is_mac: bool) -> Modifiers { - Modifiers { - control: event.ctrl_key(), - alt: event.alt_key(), - shift: event.shift_key(), - platform: event.meta_key(), - function: false, - } -} - -fn capslock_from_keyboard_event(event: &web_sys::KeyboardEvent) -> Capslock { - Capslock { - on: event.get_modifier_state("CapsLock"), - } -} - -fn should_preserve_focused_input( - accepted_text_before_tap: bool, - accepts_text_after_tap: bool, - dispatch_result: Option, -) -> bool { - accepted_text_before_tap - && accepts_text_after_tap - && dispatch_result.is_some_and(|result| result.default_prevented) -} - -pub(crate) fn is_mac_platform(browser_window: &web_sys::Window) -> bool { - let navigator = browser_window.navigator(); - - #[allow(deprecated)] - // navigator.platform() is deprecated but navigator.userAgentData is not widely available yet - if let Ok(platform) = navigator.platform() { - if platform.contains("Mac") { - return true; - } - } - - if let Ok(user_agent) = navigator.user_agent() { - return user_agent.contains("Mac"); - } - - false -} - -fn is_modifier_only_key(key: &str) -> bool { - matches!( - key, - "control" | "alt" | "shift" | "platform" | "capslock" | "compose" | "process" - ) -} - -/// Whether a keystroke with these modifiers produces text to insert. -/// -/// On macOS, Option participates in text entry (e.g. option-n composes "~" -/// or accented characters), so only Command and Control disqualify. Elsewhere, -/// plain Alt is a shortcut modifier, but AltGr is reported by browsers as -/// control+alt and `event.key()` then carries the composed character. -fn keystroke_inserts_text(modifiers: &Modifiers, is_mac: bool) -> bool { - if is_mac { - !modifiers.platform && !modifiers.control - } else { - modifiers.is_subset_of(&Modifiers::shift()) || (modifiers.control && modifiers.alt) - } -} - -fn compute_key_char( - event: &web_sys::KeyboardEvent, - gpui_key: &str, - modifiers: &Modifiers, -) -> Option { - // AltGr arrives as control+alt with the composed character in - // `event.key()`; bare Command/Control combinations are not text. - if (modifiers.platform || modifiers.control) && !(modifiers.control && modifiers.alt) { - return None; - } - - if is_modifier_only_key(gpui_key) { - return None; - } - - if gpui_key == "space" { - return Some(" ".to_string()); - } - - let raw_key = event.key(); - - if raw_key.len() == 1 { - return Some(raw_key); - } - - None -} - -fn pointer_position_in_element(event: &web_sys::PointerEvent) -> Point { - let mouse_event: &web_sys::MouseEvent = event.as_ref(); - mouse_position_in_element(mouse_event) -} - -/// How far ahead of the raw pointer position predictions may reach. -/// -/// Browsers predict much further (Chrome offers samples out to 25ms), but -/// prediction error grows with the horizon and surfaces as jitter: the -/// emitted pan deltas gain a term proportional to lead x change in velocity, -/// which at long leads visibly reverses direction mid-drag. Measurements -/// show leads up to ~10ms track at or below the raw stream's frame-to-frame -/// variation; AOSP similarly caps touch resampling extrapolation at 8ms -/// (`RESAMPLE_MAX_PREDICTION` in `InputTransport.cpp`). -const MAX_PREDICTION_LEAD_MS: f64 = 10.; - -/// The predicted pointer position closest to [`MAX_PREDICTION_LEAD_MS`] -/// ahead of `event`, from `getPredictedEvents()`, or `None` when the browser -/// offers no prediction (Safari lacks the method, Firefox returns an empty -/// array). A prediction further out than the cap is linearly scaled back to -/// it. -/// -/// Accessed through `Reflect` because calling a missing method through the -/// web-sys binding would throw, and predicted events' `offsetX`/`offsetY` -/// are unreliable across browsers (their target may be detached), so the -/// position is derived from the client-coordinate delta against the parent -/// event, anchored to the parent's element-relative `position`. -fn predicted_pointer_position( - event: &web_sys::PointerEvent, - position: Point, -) -> Option> { - let method = js_sys::Reflect::get(event, &JsValue::from_str("getPredictedEvents")).ok()?; - let method = method.dyn_ref::()?; - let predicted_events: js_sys::Array = method.call0(event).ok()?.dyn_into().ok()?; - let mut best: Option<(f64, web_sys::PointerEvent)> = None; - for predicted in predicted_events.iter() { - let Ok(predicted) = predicted.dyn_into::() else { - continue; - }; - let lead = predicted.time_stamp() - event.time_stamp(); - if lead <= 0. { - continue; - } - let distance_to_cap = (lead - MAX_PREDICTION_LEAD_MS).abs(); - if best - .as_ref() - .is_none_or(|(best_distance, _)| distance_to_cap < *best_distance) - { - best = Some((distance_to_cap, predicted)); - } - } - let (_, predicted) = best?; - let lead = predicted.time_stamp() - event.time_stamp(); - let scale = (MAX_PREDICTION_LEAD_MS / lead).min(1.) as f32; - let event: &web_sys::MouseEvent = event.as_ref(); - let predicted: &web_sys::MouseEvent = predicted.as_ref(); - Some(point( - position.x + px((predicted.client_x() - event.client_x()) as f32 * scale), - position.y + px((predicted.client_y() - event.client_y()) as f32 * scale), - )) -} - -fn mouse_position_in_element(event: &web_sys::MouseEvent) -> Point { - // offset_x/offset_y give position relative to the target element's padding edge - point(px(event.offset_x() as f32), px(event.offset_y() as f32)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn browser_pointer_id_reuse_gets_a_new_touch_id() { - let mut touch_ids = TouchIds::default(); - let first = touch_ids.start(7).expect("first touch id"); - let concurrent = touch_ids.start(8).expect("concurrent touch id"); - - assert_ne!(first, concurrent); - assert_eq!(touch_ids.active(7), Some(first)); - assert_eq!(touch_ids.end(7), Some(first)); - assert_eq!(touch_ids.active(7), None); - - let reused = touch_ids.start(7).expect("reused pointer touch id"); - assert_ne!(reused, first); - assert_ne!(reused, concurrent); - } - - #[test] - fn handled_tap_preserves_unchanged_text_input() { - assert!(should_preserve_focused_input( - true, - true, - Some(DispatchEventResult { - propagate: false, - default_prevented: true, - }), - )); - } - - #[test] - fn tap_does_not_preserve_unhandled_or_unfocused_input() { - let result = |default_prevented| { - Some(DispatchEventResult { - propagate: false, - default_prevented, - }) - }; - - assert!(!should_preserve_focused_input(true, true, result(false),)); - assert!(!should_preserve_focused_input(false, true, result(true),)); - assert!(!should_preserve_focused_input(true, false, result(true),)); - } -} diff --git a/crates/gpui_web/src/gpui_web.rs b/crates/gpui_web/src/gpui_web.rs deleted file mode 100644 index 60848043..00000000 --- a/crates/gpui_web/src/gpui_web.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![cfg(target_family = "wasm")] - -//! GPUI's browser platform uses one document-owned canvas and supports one top-level window. -//! Browser WebGPU is preferred by default, with an automatic WebGL2 fallback. Applications can -//! force either backend with [`WebBackendPreference`]. Opening a second top-level window, or -//! reopening one after it closes, returns [`WebWindowError`]. - -mod dispatcher; -mod display; -mod events; -mod http_client; -mod ime_mirror; -mod keyboard; -mod logging; -mod platform; -mod window; - -pub use dispatcher::WebDispatcher; -pub use display::WebDisplay; -pub use gpui_wgpu::WebBackendPreference; -pub use http_client::{FetchCredentials, FetchHttpClient}; -pub use keyboard::WebKeyboardLayout; -pub use logging::init_logging; -pub use platform::{WebPlatform, WebWindowError}; -pub use window::WebWindow; diff --git a/crates/gpui_web/src/http_client.rs b/crates/gpui_web/src/http_client.rs deleted file mode 100644 index 4a00324d..00000000 --- a/crates/gpui_web/src/http_client.rs +++ /dev/null @@ -1,309 +0,0 @@ -use crate::WebDispatcher; -use anyhow::{Context as _, anyhow}; -use futures::{ - AsyncRead, AsyncReadExt as _, FutureExt as _, SinkExt as _, TryStreamExt as _, - channel::{mpsc, oneshot}, -}; -use http_client::{AsyncBody, HttpClient, RedirectPolicy}; -use std::{ - io, - pin::Pin, - sync::Arc, - task::{Context, Poll}, -}; -use wasm_bindgen::JsCast as _; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(catch, js_name = "fetch")] - fn global_fetch(input: &web_sys::Request) -> Result; -} - -pub struct FetchHttpClient { - dispatcher: Arc, - user_agent: Option, - credentials: FetchCredentials, -} - -/// Controls whether browser Fetch requests include credentials. -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub enum FetchCredentials { - /// Never send credentials in the request or include credentials in the response. - Omit, - /// Only send and include credentials for same-origin requests. This is the default. - #[default] - SameOrigin, - /// Always include credentials, even for cross-origin requests. - Include, -} - -impl FetchHttpClient { - pub(crate) fn new(dispatcher: Arc) -> Self { - Self { - dispatcher, - user_agent: None, - credentials: FetchCredentials::default(), - } - } - - pub(crate) fn with_user_agent( - dispatcher: Arc, - user_agent: &str, - ) -> anyhow::Result { - Ok(Self { - dispatcher, - user_agent: Some(http_client::http::header::HeaderValue::from_str( - user_agent, - )?), - credentials: FetchCredentials::default(), - }) - } - - pub fn with_credentials(mut self, credentials: FetchCredentials) -> Self { - self.credentials = credentials; - self - } -} - -impl HttpClient for FetchHttpClient { - fn user_agent(&self) -> Option<&http_client::http::header::HeaderValue> { - self.user_agent.as_ref() - } - - fn proxy(&self) -> Option<&http_client::Url> { - None - } - - fn send( - &self, - req: http_client::http::Request, - ) -> futures::future::BoxFuture<'static, anyhow::Result>> - { - let (parts, body) = req.into_parts(); - let credentials = self.credentials; - let dispatcher = self.dispatcher.clone(); - - Box::pin(async move { - let body_bytes = read_body_to_bytes(body).await?; - let (sender, receiver) = oneshot::channel(); - - dispatcher.dispatch_function_on_main_thread(move || { - wasm_bindgen_futures::spawn_local(async move { - let result = fetch(parts, body_bytes, credentials).await; - if sender.send(result).is_err() { - log::debug!("fetch response receiver was dropped"); - } - }); - }); - - receiver.await.context("browser fetch task was canceled")? - }) - } -} - -async fn fetch( - parts: http_client::http::request::Parts, - body_bytes: Option>, - credentials: FetchCredentials, -) -> anyhow::Result> { - let init = web_sys::RequestInit::new(); - init.set_method(parts.method.as_str()); - init.set_credentials(match credentials { - FetchCredentials::Omit => web_sys::RequestCredentials::Omit, - FetchCredentials::SameOrigin => web_sys::RequestCredentials::SameOrigin, - FetchCredentials::Include => web_sys::RequestCredentials::Include, - }); - - if let Some(redirect_policy) = parts.extensions.get::() { - match redirect_policy { - RedirectPolicy::NoFollow => { - init.set_redirect(web_sys::RequestRedirect::Manual); - } - RedirectPolicy::FollowLimit(_) | RedirectPolicy::FollowAll => { - init.set_redirect(web_sys::RequestRedirect::Follow); - } - } - } - - if let Some(ref bytes) = body_bytes { - let uint8array = js_sys::Uint8Array::from(bytes.as_slice()); - init.set_body(uint8array.as_ref()); - } - - let url = parts.uri.to_string(); - let request = web_sys::Request::new_with_str_and_init(&url, &init) - .map_err(|error| anyhow!("failed to create fetch Request: {error:?}"))?; - - let request_headers = request.headers(); - for (name, value) in &parts.headers { - let value_str = value - .to_str() - .map_err(|_| anyhow!("non-ASCII header value for {name}"))?; - request_headers - .set(name.as_str(), value_str) - .map_err(|error| anyhow!("failed to set header {name}: {error:?}"))?; - } - - let promise = - global_fetch(&request).map_err(|error| anyhow!("fetch threw an error: {error:?}"))?; - let response_value = wasm_bindgen_futures::JsFuture::from(promise) - .await - .map_err(|error| anyhow!("fetch failed: {error:?}"))?; - - let web_response: web_sys::Response = response_value - .dyn_into() - .map_err(|error| anyhow!("fetch result is not a Response: {error:?}"))?; - - let status = web_response.status(); - let mut builder = http_client::http::Response::builder().status(status); - - // `Headers` is a JS iterable yielding `[name, value]` pairs. - // `js_sys::Array::from` calls `Array.from()` which accepts any iterable. - let header_pairs = js_sys::Array::from(&web_response.headers()); - for index in 0..header_pairs.length() { - match header_pairs.get(index).dyn_into::() { - Ok(pair) => match (pair.get(0).as_string(), pair.get(1).as_string()) { - (Some(name), Some(value)) => { - builder = builder.header(name, value); - } - (name, value) => { - log::warn!( - "skipping response header at index {index}: \ - name={name:?}, value={value:?}" - ); - } - }, - Err(entry) => { - log::warn!("skipping non-array header entry at index {index}: {entry:?}"); - } - } - } - - let body = match web_response.body() { - Some(stream) => { - let reader = stream - .get_reader() - .dyn_into::() - .map_err(|error| { - anyhow!("response body reader has an unexpected type: {error:?}") - })?; - AsyncBody::from_reader(ReadableStreamBody::new(reader)) - } - None => AsyncBody::empty(), - }; - - builder.body(body).map_err(|error| anyhow!(error)) -} - -// Request bodies are buffered into memory because streaming uploads require -// half-duplex Fetch support that browsers largely don't ship yet. -async fn read_body_to_bytes(mut body: AsyncBody) -> anyhow::Result>> { - let mut buffer = Vec::new(); - body.read_to_end(&mut buffer).await?; - if buffer.is_empty() { - Ok(None) - } else { - Ok(Some(buffer)) - } -} - -const RESPONSE_BODY_CHANNEL_CAPACITY: usize = 8; - -struct ReadableStreamBody { - chunks: futures::stream::IntoAsyncRead>>>, - // Dropping this sender resolves the pump's cancellation future, which - // cancels the browser-side `ReadableStream`. - _cancellation: oneshot::Sender<()>, -} - -impl ReadableStreamBody { - fn new(reader: web_sys::ReadableStreamDefaultReader) -> Self { - let (chunks_sender, chunks_receiver) = mpsc::channel(RESPONSE_BODY_CHANNEL_CAPACITY); - let (cancellation, cancellation_receiver) = oneshot::channel(); - wasm_bindgen_futures::spawn_local(pump_response_body( - reader, - chunks_sender, - cancellation_receiver, - )); - Self { - chunks: chunks_receiver.into_async_read(), - _cancellation: cancellation, - } - } -} - -impl AsyncRead for ReadableStreamBody { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buffer: &mut [u8], - ) -> Poll> { - Pin::new(&mut self.chunks).poll_read(cx, buffer) - } -} - -async fn pump_response_body( - reader: web_sys::ReadableStreamDefaultReader, - mut chunks: mpsc::Sender>>, - cancellation: oneshot::Receiver<()>, -) { - let cancellation = cancellation.fuse(); - futures::pin_mut!(cancellation); - - loop { - let read = wasm_bindgen_futures::JsFuture::from(reader.read()).fuse(); - futures::pin_mut!(read); - let result = futures::select_biased! { - _ = cancellation => { - cancel_reader(&reader).await; - return; - } - result = read => result, - }; - - let chunk = result - .map_err(|error| io::Error::other(format!("response stream failed: {error:?}"))) - .and_then(response_chunk); - match chunk { - Ok(Some(chunk)) => { - if chunks.send(Ok(chunk)).await.is_err() { - cancel_reader(&reader).await; - return; - } - } - Ok(None) => return, - Err(error) => { - if chunks.send(Err(error)).await.is_err() { - log::debug!("response body receiver was dropped after a stream error"); - } - return; - } - } - } -} - -fn response_chunk(result: JsValue) -> io::Result>> { - // `ReadableStreamReadResult` is a dictionary type, so there is no runtime - // class to check against; `unchecked_into` is the only available cast. - let result: web_sys::ReadableStreamReadResult = result.unchecked_into(); - if result.get_done().unwrap_or(false) { - return Ok(None); - } - - result - .get_value() - .dyn_into::() - .map(|bytes| Some(bytes.to_vec())) - .map_err(|value| { - io::Error::other(format!( - "response stream yielded a non-byte chunk: {value:?}" - )) - }) -} - -async fn cancel_reader(reader: &web_sys::ReadableStreamDefaultReader) { - if let Err(error) = wasm_bindgen_futures::JsFuture::from(reader.cancel()).await { - log::debug!("failed to cancel response body reader: {error:?}"); - } -} diff --git a/crates/gpui_web/src/ime_mirror.rs b/crates/gpui_web/src/ime_mirror.rs deleted file mode 100644 index 75ef8071..00000000 --- a/crates/gpui_web/src/ime_mirror.rs +++ /dev/null @@ -1,605 +0,0 @@ -//! The hidden `

, - /// One-based scene clip-chain index. Zero uses the inline bounds and radii. - pub clip_index: u32, - /// Explicit GPU record padding. - pub clip_padding: u32, -} - -impl ContentMask { - /// Scale the content mask's pixel units by the given scaling factor. - pub fn scale(&self, factor: f32) -> ContentMask { - ContentMask { - bounds: self.bounds.scale(factor), - corner_radii: self.corner_radii.scale(factor), - clip_index: self.clip_index, - clip_padding: 0, - } - } - - /// Intersect without changing either mask's rounded geometry. - pub fn intersect(&self, other: &Self) -> crate::ClipRegion { - crate::ClipRegion::from(*self).intersect(&crate::ClipRegion::from(*other)) - } -} - -impl Window { - fn mark_view_dirty(&mut self, view_id: EntityId) { - // Mark ancestor views as dirty. If already in the `dirty_views` set, then all its ancestors - // should already be dirty. - for view_id in self - .rendered_frame - .dispatch_tree - .view_path_reversed(view_id) - { - if !self.dirty_views.insert(view_id) { - break; - } - } - } - - /// Registers a callback to be invoked when the window appearance changes. - pub fn observe_window_appearance( - &self, - mut callback: impl FnMut(&mut Window, &mut App) + 'static, - ) -> Subscription { - let (subscription, activate) = self.appearance_observers.insert( - (), - Box::new(move |window, cx| { - callback(window, cx); - true - }), - ); - activate(); - subscription - } - - /// Registers a callback to be invoked when the window button layout changes. - pub fn observe_button_layout_changed( - &self, - mut callback: impl FnMut(&mut Window, &mut App) + 'static, - ) -> Subscription { - let (subscription, activate) = self.button_layout_observers.insert( - (), - Box::new(move |window, cx| { - callback(window, cx); - true - }), - ); - activate(); - subscription - } - - /// Replaces the root entity of the window with a new one. - pub fn replace_root( - &mut self, - cx: &mut App, - build_view: impl FnOnce(&mut Window, &mut Context) -> E, - ) -> Entity - where - E: 'static + Render, - { - let view = cx.new(|cx| build_view(self, cx)); - self.root = Some(view.clone().into()); - self.refresh(); - view - } - - /// Returns the root entity of the window, if it has one. - pub fn root(&self) -> Option>> - where - E: 'static + Render, - { - self.root - .as_ref() - .map(|view| view.clone().downcast::().ok()) - } - - /// Obtain a handle to the window that belongs to this context. - pub fn window_handle(&self) -> AnyWindowHandle { - self.handle - } - - /// Mark the window as dirty, scheduling it to be redrawn on the next frame. - pub fn refresh(&mut self) { - if self.invalidator.not_drawing() { - self.refreshing = true; - self.invalidator.set_dirty(true); - } - } - - /// Close this window. - pub fn remove_window(&mut self) { - self.removed = true; - } - - /// Obtain the currently focused [`FocusHandle`]. If no elements are focused, returns `None`. - pub fn focused(&self, cx: &App) -> Option { - self.focus - .and_then(|id| FocusHandle::for_id(id, &cx.focus_handles)) - } - - /// While focus-lost listeners are being dispatched, returns the closest ancestor of the - /// previously focused element that can still receive focus, making it a suitable target - /// for focus restoration. Returns `None` at all other times, or when no such ancestor exists. - pub fn focus_lost_restore_target(&self, cx: &App) -> Option { - let (_leaf, ancestors) = self.focus_lost_path.split_last()?; - ancestors.iter().rev().find_map(|id| { - self.rendered_frame.dispatch_tree.focusable_node_id(*id)?; - FocusHandle::for_id(*id, &cx.focus_handles) - }) - } - - /// Move focus to the element associated with the given [`FocusHandle`]. - pub fn focus(&mut self, handle: &FocusHandle, cx: &mut App) { - if !self.focus_enabled || self.focus == Some(handle.id) { - return; - } - - self.focus = Some(handle.id); - self.focus_generation = self.focus_generation.wrapping_add(1); - self.clear_pending_keystrokes(cx); - - self.refresh(); - } - - /// Remove focus from all elements within this context's window. - pub fn blur(&mut self, cx: &mut App) { - self.clear_pending_keystrokes(cx); - - if !self.focus_enabled { - return; - } - - if self.focus.is_some() { - self.focus_generation = self.focus_generation.wrapping_add(1); - } - self.focus = None; - self.refresh(); - } - - /// Blur the window and don't allow anything in it to be focused again. - pub fn disable_focus(&mut self, cx: &mut App) { - self.blur(cx); - self.focus_enabled = false; - } - - /// Move focus to next tab stop. - pub fn focus_next(&mut self, cx: &mut App) { - if !self.focus_enabled { - return; - } - - if let Some(handle) = self.rendered_frame.tab_stops.next(self.focus.as_ref()) { - self.focus(&handle, cx) - } - } - - /// Move focus to previous tab stop. - pub fn focus_prev(&mut self, cx: &mut App) { - if !self.focus_enabled { - return; - } - - if let Some(handle) = self.rendered_frame.tab_stops.prev(self.focus.as_ref()) { - self.focus(&handle, cx) - } - } - - /// Accessor for the text system. - pub fn text_system(&self) -> &Arc { - &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 if the platform window is maximized. - /// - /// On some platforms (namely Windows) this is different than the bounds being the size of the display - pub fn is_maximized(&self) -> bool { - self.platform_window.is_maximized() - } - - /// request a certain window decoration (Wayland) - pub fn request_decorations(&self, decorations: WindowDecorations) { - self.platform_window.request_decorations(decorations); - } - - /// Set the exclusive zone for a layer-shell surface: how much screen space it - /// reserves so other surfaces avoid occluding it (e.g. a panel reserving space). - /// Positive values reserve that distance from the anchored edge, 0 lets the - /// surface be moved out of others' exclusive zones, and -1 ignores reserved - /// space and may extend under other surfaces. (Wayland layer-shell windows only) - pub fn set_exclusive_zone(&self, zone: Pixels) { - self.platform_window.set_exclusive_zone(zone); - } - - /// Set which anchored edge a layer-shell surface's exclusive zone applies to. - /// This is only needed to disambiguate a corner-anchored surface; otherwise the - /// edge is deduced from the anchor. The edge must be a single edge the surface - /// is anchored to, or it is ignored. (Wayland layer-shell windows only) - #[cfg(all(target_os = "linux", feature = "wayland"))] - pub fn set_exclusive_edge(&self, edge: crate::layer_shell::Anchor) { - self.platform_window.set_exclusive_edge(edge); - } - - /// Start an interactive window resize operation if this window is resizable. - pub fn start_window_resize(&self, edge: ResizeEdge) { - if self.is_resizable { - self.platform_window.start_window_resize(edge); - } - } - - /// Linux (wayland) only: Set the window's input region, the area that receives pointer - /// and touch input. Events outside it pass through to whatever is below the window. - /// - /// - `Some(rects)` restricts input to the union of `rects`, in window coordinates. - /// - `Some(&[])` is an empty region, so the window receives no pointer or touch input. - /// - `None` resets the region to the default, so the whole window receives input again. - pub fn set_input_region(&self, region: Option<&[Bounds]>) { - self.platform_window.set_input_region(region); - } - - /// Return the `WindowBounds` to indicate that how a window should be opened - /// after it has been closed - pub fn window_bounds(&self) -> WindowBounds { - self.platform_window.window_bounds() - } - - /// Return the `WindowBounds` excluding insets (Wayland and X11) - pub fn inner_window_bounds(&self) -> WindowBounds { - self.platform_window.inner_window_bounds() - } - - /// Dispatch the given action on the currently focused element. - pub fn dispatch_action(&mut self, action: Box, cx: &mut App) { - let focus_id = self.focused(cx).map(|handle| handle.id); - - let window = self.handle; - cx.defer(move |cx| { - window - .update(cx, |_, window, cx| { - let node_id = window.focus_node_id_in_rendered_frame(focus_id); - window.dispatch_action_on_node(node_id, action.as_ref(), cx); - }) - .log_err(); - }) - } - - pub(crate) fn dispatch_keystroke_observers( - &mut self, - event: &dyn Any, - action: Option>, - context_stack: Vec, - cx: &mut App, - ) { - let Some(key_down_event) = event.downcast_ref::() else { - return; - }; - - cx.keystroke_observers.clone().retain(&(), move |callback| { - (callback)( - &KeystrokeEvent { - keystroke: key_down_event.keystroke.clone(), - action: action.as_ref().map(|action| action.boxed_clone()), - context_stack: context_stack.clone(), - }, - self, - cx, - ) - }); - } - - pub(crate) fn dispatch_keystroke_interceptors( - &mut self, - event: &dyn Any, - context_stack: Vec, - cx: &mut App, - ) { - let Some(key_down_event) = event.downcast_ref::() else { - return; - }; - - cx.keystroke_interceptors - .clone() - .retain(&(), move |callback| { - (callback)( - &KeystrokeEvent { - keystroke: key_down_event.keystroke.clone(), - action: None, - context_stack: context_stack.clone(), - }, - self, - cx, - ) - }); - } - - /// 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(&self, cx: &mut App, f: impl FnOnce(&mut Window, &mut App) + 'static) { - let handle = self.handle; - cx.defer(move |cx| { - handle.update(cx, |_, window, cx| f(window, cx)).ok(); - }); - } - - /// Subscribe to events emitted by a entity. - /// The entity to which you're subscribing must implement the [`EventEmitter`] trait. - /// The callback will be invoked a handle to the emitting entity, the event, and a window context for the current window. - pub fn observe( - &mut self, - observed: &Entity, - cx: &mut App, - mut on_notify: impl FnMut(Entity, &mut Window, &mut App) + 'static, - ) -> Subscription { - let entity_id = observed.entity_id(); - let observed = observed.downgrade(); - let window_handle = self.handle; - cx.new_observer( - entity_id, - Box::new(move |cx| { - window_handle - .update(cx, |_, window, cx| { - if let Some(handle) = observed.upgrade() { - on_notify(handle, window, cx); - true - } else { - false - } - }) - .unwrap_or(false) - }), - ) - } - - /// Subscribe to events emitted by a entity. - /// The entity to which you're subscribing must implement the [`EventEmitter`] trait. - /// The callback will be invoked a handle to the emitting entity, the event, and a window context for the current window. - pub fn subscribe( - &mut self, - entity: &Entity, - cx: &mut App, - mut on_event: impl FnMut(Entity, &Evt, &mut Window, &mut App) + 'static, - ) -> Subscription - where - Emitter: EventEmitter, - Evt: 'static, - { - let entity_id = entity.entity_id(); - let handle = entity.downgrade(); - let window_handle = self.handle; - cx.new_subscription( - entity_id, - ( - TypeId::of::(), - Box::new(move |event, cx| { - window_handle - .update(cx, |_, window, cx| { - if let Some(entity) = handle.upgrade() { - let event = event.downcast_ref().expect("invalid event type"); - on_event(entity, event, window, cx); - true - } else { - false - } - }) - .unwrap_or(false) - }), - ), - ) - } - - /// Register a callback to be invoked when the given `Entity` is released. - pub fn observe_release( - &self, - entity: &Entity, - cx: &mut App, - mut on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static, - ) -> Subscription - where - T: 'static, - { - let entity_id = entity.entity_id(); - let window_handle = self.handle; - let (subscription, activate) = cx.release_listeners.insert( - entity_id, - Box::new(move |entity, cx| { - let entity = entity.downcast_mut().expect("invalid entity type"); - let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx)); - }), - ); - activate(); - subscription - } - - /// Creates an [`AsyncWindowContext`], which has a static lifetime and can be held across - /// await points in async code. - pub fn to_async(&self, cx: &App) -> AsyncWindowContext { - AsyncWindowContext::new_context(cx.to_async(), self.handle) - } - - /// Schedule the given closure to be run directly after the current frame is rendered. - pub fn on_next_frame(&self, callback: impl FnOnce(&mut Window, &mut App) + 'static) { - RefCell::borrow_mut(&self.next_frame_callbacks).push(Box::new(callback)); - self.platform_window.schedule_frame(); - // Next-frame callbacks create frame demand without dirtying the - // window, so the platform's frame source must be woken explicitly. - self.invalidator.wake_platform(); - } - - /// Schedule a frame to be drawn on the next animation frame. - /// - /// This is useful for elements that need to animate continuously, such as a video player or an animated GIF. - /// It will cause the window to redraw on the next frame, even if no other changes have occurred. - /// - /// If called from within a view, it will notify that view on the next frame. Otherwise, it will refresh the entire window. - /// - /// Callers driving purely decorative animations (spinners, pulses, and the - /// like) should prefer [`AnimationExt::with_animation`](crate::AnimationExt::with_animation), - /// which automatically respects [`App::reduce_motion`]. When using this - /// method directly for decorative motion, check [`App::reduce_motion`] - /// and skip the frame request when it is set. - pub fn request_animation_frame(&self) { - let entity = self.current_view(); - self.on_next_frame(move |_, cx| cx.notify(entity)); - } - - /// Runs all callbacks scheduled via [`Self::on_next_frame`], returning how many ran. - /// - /// Tests have no platform frame loop, so this simulates the delivery of the - /// next frame. - #[cfg(any(test, feature = "test-support"))] - pub fn simulate_next_frame(&mut self, cx: &mut App) -> usize { - let callbacks = self.next_frame_callbacks.take(); - let count = callbacks.len(); - for callback in callbacks { - callback(self, cx); - } - count - } - - /// Spawn the future returned by the given closure on the application thread pool. - /// The closure is provided a handle to the current window and an `AsyncWindowContext` for - /// use within your future. - #[track_caller] - pub fn spawn(&self, cx: &App, f: AsyncFn) -> Task - where - R: 'static, - AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static, - { - let handle = self.handle; - cx.spawn(async move |app| { - let mut async_window_cx = AsyncWindowContext::new_context(app.clone(), handle); - f(&mut async_window_cx).await - }) - } - - /// Spawn the future returned by the given closure on the application thread - /// pool, with the given priority. The closure is provided a handle to the - /// current window and an `AsyncWindowContext` for use within your future. - #[track_caller] - pub fn spawn_with_priority( - &self, - priority: Priority, - cx: &App, - f: AsyncFn, - ) -> Task - where - R: 'static, - AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static, - { - let handle = self.handle; - cx.spawn_with_priority(priority, async move |app| { - let mut async_window_cx = AsyncWindowContext::new_context(app.clone(), handle); - f(&mut async_window_cx).await - }) - } - - /// Notify the window that its bounds have changed. - /// - /// This updates internal state like `viewport_size` and `scale_factor` from - /// the platform window, then notifies observers. Normally called automatically - /// by the platform's resize callback, but exposed publicly for test infrastructure. - pub fn bounds_changed(&mut self, cx: &mut App) { - self.scale_factor = self.platform_window.scale_factor(); - self.viewport_size = self.platform_window.content_size(); - self.display_id = self.platform_window.display().map(|display| display.id()); - self.mouse_position = self.platform_window.mouse_position(); - - self.refresh(); - - self.bounds_observers - .clone() - .retain(&(), |callback| callback(self, cx)); - } - - /// Returns the bounds of the current window in the global coordinate space, which could span across multiple displays. - pub fn bounds(&self) -> Bounds { - self.platform_window.bounds() - } - - /// Renders the current frame's scene to a texture and returns the pixel data as an RGBA image. - /// This does not present the frame to screen - useful for visual testing where we want - /// to capture what would be rendered without displaying it or requiring the window to be visible. - #[cfg(any(test, feature = "test-support"))] - pub fn render_to_image(&self) -> anyhow::Result { - self.platform_window - .render_to_image(&self.rendered_frame.scene) - } - - /// Returns the quads in the most recently rendered frame's scene, so tests can assert on - /// painted output without rasterizing the frame. Quad bounds are in scaled pixels and are - /// not clipped; each quad carries the content mask it will be clipped to when drawn. Quads - /// whose bounds don't intersect their content mask are culled at paint time and won't appear. - #[cfg(any(test, feature = "test-support"))] - pub fn painted_quads(&self) -> Vec { - self.rendered_frame.scene.quads.clone() - } - - /// Returns the immutable rounded clip nodes referenced by painted primitives. - #[cfg(any(test, feature = "test-support"))] - pub fn painted_clips(&self) -> Vec> { - self.rendered_frame.scene.rounded_clips.clone() - } - - /// Returns painted shadows for renderer regression tests. - #[cfg(any(test, feature = "test-support"))] - pub fn painted_shadows(&self) -> Vec { - self.rendered_frame.scene.shadows.clone() - } - - /// Set the content size of the window. - pub fn resize(&mut self, size: Size) { - self.platform_window.resize(size); - } - - /// Returns whether or not the window is currently fullscreen - pub fn is_fullscreen(&self) -> bool { - self.platform_window.is_fullscreen() - } - - /// Returns whether the window is currently in simple (borderless) fullscreen, - /// where it covers the entire screen including the menu bar and notch area. - /// Always `false` on platforms other than macOS. - pub fn is_simple_fullscreen(&self) -> bool { - self.platform_window.is_simple_fullscreen() - } - - pub(crate) fn appearance_changed(&mut self, cx: &mut App) { - self.appearance = self.platform_window.appearance(); - - self.appearance_observers - .clone() - .retain(&(), |callback| callback(self, cx)); - } - - pub(crate) fn button_layout_changed(&mut self, cx: &mut App) { - self.button_layout_observers - .clone() - .retain(&(), |callback| callback(self, cx)); - } - - /// Returns the appearance of the current window. - pub fn appearance(&self) -> WindowAppearance { - self.appearance - } - - /// Returns the size of the drawable area within the window. - pub fn viewport_size(&self) -> Size { - self.viewport_size - } - - /// Returns whether this window is focused by the operating system (receiving key events). - pub fn is_window_active(&self) -> bool { - self.active.get() - } - - /// Returns whether this window is considered to be the window - /// that currently owns the mouse cursor. - /// On mac, this is equivalent to `is_window_active`. - pub fn is_window_hovered(&self) -> bool { - if cfg!(any( - target_os = "windows", - target_os = "linux", - target_os = "freebsd" - )) { - self.hovered.get() - } else { - self.is_window_active() - } - } - - /// Toggle zoom on the window. - pub fn zoom_window(&self) { - self.platform_window.zoom(); - } - - /// Opens the native title bar context menu, useful when implementing client side decorations (Wayland and X11) - pub fn show_window_menu(&self, position: Point) { - self.platform_window.show_window_menu(position) - } - - /// Handle window movement for Linux and macOS. - /// Tells the compositor to take control of window movement (Wayland and X11) - /// - /// Events may not be received during a move operation. - pub fn start_window_move(&self) { - self.platform_window.start_window_move() - } - - /// When using client side decorations, set this to the width of the invisible decorations (Wayland and X11) - pub fn set_client_inset(&mut self, inset: Pixels) { - self.client_inset = Some(inset); - self.platform_window.set_client_inset(inset); - } - - /// Returns the client_inset value by [`Self::set_client_inset`]. - pub fn client_inset(&self) -> Option { - self.client_inset - } - - /// Returns whether the title bar window controls need to be rendered by the application (Wayland and X11) - pub fn window_decorations(&self) -> Decorations { - self.platform_window.window_decorations() - } - - /// Returns whether this window is resizable. - pub fn is_resizable(&self) -> bool { - self.is_resizable - } - - /// Returns whether this window is minimizable. - pub fn is_minimizable(&self) -> bool { - self.is_minimizable - } - - /// Returns the controls supported by the platform. - pub fn window_controls(&self) -> WindowControls { - self.platform_window.window_controls() - } - - /// Updates the window's title at the platform level. - pub fn set_window_title(&mut self, title: &str) { - self.platform_window.set_title(title); - self.a11y.set_window_title(title.to_string()); - } - - /// Sets the position of the macOS traffic light buttons. - #[cfg(target_os = "macos")] - pub fn set_traffic_light_position(&self, position: Point) { - self.platform_window.set_traffic_light_position(position); - } - - /// Sets the application identifier. - pub fn set_app_id(&mut self, app_id: &str) { - self.platform_window.set_app_id(app_id); - } - - /// Sets the window background appearance. - pub fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) { - self.platform_window - .set_background_appearance(background_appearance); - } - - /// Mark the window as dirty at the platform level. - pub fn set_window_edited(&mut self, edited: bool) { - self.platform_window.set_edited(edited); - } - - /// Set the path of the file this window represents. - /// On macOS, this sets the window's accessibility document property (AXDocument). - pub fn set_document_path(&self, path: Option<&std::path::Path>) { - self.platform_window.set_document_path(path); - } - - /// Determine the display on which the window is visible. - pub fn display(&self, cx: &App) -> Option> { - cx.platform - .displays() - .into_iter() - .find(|display| Some(display.id()) == self.display_id) - } - - /// Show the platform character palette. - pub fn show_character_palette(&self) { - self.platform_window.show_character_palette(); - } - - /// The scale factor of the display associated with the window. For example, it could - /// return 2.0 for a "retina" display, indicating that each logical pixel should actually - /// be rendered as two pixels on screen. - pub fn scale_factor(&self) -> f32 { - self.scale_factor - } - - /// Overrides the display scale factor for tests. - #[cfg(any(test, feature = "test-support"))] - pub fn set_scale_factor(&mut self, scale_factor: f32) { - self.scale_factor = scale_factor; - self.refresh(); - } - - /// The size of an em for the base font of the application. Adjusting this value allows the - /// UI to scale, just like zooming a web page. - pub fn rem_size(&self) -> Pixels { - self.rem_size_override_stack - .last() - .copied() - .unwrap_or(self.rem_size) - } - - /// Sets the size of an em for the base font of the application. Adjusting this value allows the - /// UI to scale, just like zooming a web page. - pub fn set_rem_size(&mut self, rem_size: impl Into) { - self.rem_size = rem_size.into(); - } - - /// Acquire a globally unique identifier for the given ElementId. - /// Only valid for the duration of the provided closure. - pub fn with_global_id( - &mut self, - element_id: ElementId, - f: impl FnOnce(&GlobalElementId, &mut Self) -> R, - ) -> R { - self.with_id(element_id, |this| { - let global_id = GlobalElementId(Arc::from(&*this.element_id_stack)); - - f(&global_id, this) - }) - } - - /// Calls the provided closure with the element ID pushed on the stack. - #[inline] - pub fn with_id( - &mut self, - element_id: impl Into, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.element_id_stack.push(element_id.into()); - let result = f(self); - self.element_id_stack.pop(); - result - } - - /// Executes the provided function with the specified rem size. - /// - /// This method must only be called as part of element drawing. - // This function is called in a highly recursive manner in editor - // prepainting, make sure its inlined to reduce the stack burden - #[inline] - pub fn with_rem_size(&mut self, rem_size: Option>, f: F) -> R - where - F: FnOnce(&mut Self) -> R, - { - self.invalidator.debug_assert_paint_or_prepaint(); - - if let Some(rem_size) = rem_size { - self.rem_size_override_stack.push(rem_size.into()); - let result = f(self); - self.rem_size_override_stack.pop(); - result - } else { - f(self) - } - } - - /// The line height associated with the current text style. - pub fn line_height(&self) -> Pixels { - self.text_style().line_height_in_pixels(self.rem_size()) - } - - /// Rounds a logical value to the nearest device pixel. - #[inline] - pub fn pixel_snap(&self, value: Pixels) -> Pixels { - px(round_to_device_pixel(value.0, self.scale_factor()) / self.scale_factor()) - } - - /// f64 variant of [`Self::pixel_snap`]. - #[inline] - pub fn pixel_snap_f64(&self, value: f64) -> f64 { - let scale_factor = f64::from(self.scale_factor()); - round_half_toward_zero_f64(value * scale_factor) / scale_factor - } - - /// Snaps a bounds' origin and size to the nearest device pixel. - #[inline] - pub fn pixel_snap_bounds(&self, bounds: Bounds) -> Bounds { - bounds.map(|c| self.pixel_snap(c)) - } - - /// Snaps a point's coordinates to the nearest device pixel. - #[inline] - pub fn pixel_snap_point(&self, position: Point) -> Point { - position.map(|c| self.pixel_snap(c)) - } - - #[inline] - fn snap_bounds(&self, bounds: Bounds) -> Bounds { - let scale_factor = self.scale_factor(); - let left = round_to_device_pixel(bounds.left().0, scale_factor); - let top = round_to_device_pixel(bounds.top().0, scale_factor); - let right = round_to_device_pixel(bounds.right().0, scale_factor).max(left); - let bottom = round_to_device_pixel(bounds.bottom().0, scale_factor).max(top); - Bounds::from_corners( - point(ScaledPixels(left), ScaledPixels(top)), - point(ScaledPixels(right), ScaledPixels(bottom)), - ) - } - - /// Rounds half-to-zero but clamps any non-zero input up to 1 dp so thin strokes do not disappear. - #[inline] - fn snap_stroke(&self, value: Pixels) -> ScaledPixels { - ScaledPixels(round_stroke_to_device_pixel(value.0, self.scale_factor())) - } - - #[inline] - fn snap_border_widths(&self, edges: Edges) -> Edges { - edges.map(|e| self.snap_stroke(*e)) - } - - /// Floors the near edge and ceils the far edge, producing a strict superset of the raw region. - #[inline] - fn cover_bounds(&self, bounds: Bounds) -> Bounds { - let scale_factor = self.scale_factor(); - let left = floor_to_device_pixel(bounds.left().0, scale_factor); - let top = floor_to_device_pixel(bounds.top().0, scale_factor); - let right = ceil_to_device_pixel(bounds.right().0, scale_factor).max(left); - let bottom = ceil_to_device_pixel(bounds.bottom().0, scale_factor).max(top); - Bounds::from_corners( - point(ScaledPixels(left), ScaledPixels(top)), - point(ScaledPixels(right), ScaledPixels(bottom)), - ) - } - - #[inline] - fn snapped_content_mask(&mut self) -> ContentMask { - let region = self.content_mask(); - let mut clip_index = 0; - for shape in ®ion.rounded_clips { - let mut clip = shape.scale(self.scale_factor()); - // Use the original element's snapped shape, as paint_quad does. - // A descendant's culling bounds must never relocate these curves. - clip.bounds = self.snap_bounds(shape.bounds); - clip.parent = clip_index; - clip_index = self.next_frame.scene.insert_clip(clip); - } - ContentMask { - bounds: self.cover_bounds(region.bounds), - corner_radii: Corners::default(), - clip_index, - clip_padding: 0, - } - } - - /// Call to prevent the default action of an event. Currently only used to prevent - /// parent elements from becoming focused on mouse down. - pub fn prevent_default(&mut self) { - self.default_prevented = true; - } - - /// Obtain whether default has been prevented for the event currently being dispatched. - pub fn default_prevented(&self) -> bool { - self.default_prevented - } - - /// Determine whether the given action is available along the dispatch path to the currently focused element. - pub fn is_action_available(&self, action: &dyn Action, cx: &App) -> bool { - let node_id = - self.focus_node_id_in_rendered_frame(self.focused(cx).map(|handle| handle.id)); - self.rendered_frame - .dispatch_tree - .is_action_available(action, node_id) - } - - /// Determine whether the given action is available along the dispatch path to the given focus_handle. - pub fn is_action_available_in(&self, action: &dyn Action, focus_handle: &FocusHandle) -> bool { - let node_id = self.focus_node_id_in_rendered_frame(Some(focus_handle.id)); - self.rendered_frame - .dispatch_tree - .is_action_available(action, node_id) - } - - /// The position of the mouse relative to the window. - pub fn mouse_position(&self) -> Point { - self.mouse_position - } - - /// Captures the pointer for the given hitbox. While captured, all mouse move and mouse up - /// events will be routed to listeners that check this hitbox's `is_hovered` status, - /// regardless of actual hit testing. This enables drag operations that continue - /// even when the pointer moves outside the element's bounds. - /// - /// The capture is automatically released on mouse up. - pub fn capture_pointer(&mut self, hitbox_id: HitboxId) { - self.captured_hitbox = Some(hitbox_id); - } - - /// Releases any active pointer capture. - pub fn release_pointer(&mut self) { - self.captured_hitbox = None; - } - - /// Returns the hitbox that has captured the pointer, if any. - pub fn captured_hitbox(&self) -> Option { - self.captured_hitbox - } - - /// Captures the current long press for the given entity. - /// - /// The capture is released when the gesture ends or is cancelled, or when - /// a replacement touch begins. A listener must also call - /// [`Self::prevent_default`] on the started event to claim the gesture. - pub fn capture_long_press(&mut self, entity: &Entity) { - self.long_press_capture = Some(entity.entity_id()); - } - - /// Returns whether the given entity has captured the current long press. - pub fn has_long_press_capture(&self, entity: &Entity) -> bool { - self.long_press_capture == Some(entity.entity_id()) - } - - /// The current state of the keyboard's modifiers - pub fn modifiers(&self) -> Modifiers { - self.modifiers - } - - /// Returns true if the last input event was keyboard-based (key press, tab navigation, etc.) - /// This is used for focus-visible styling to show focus indicators only for keyboard navigation. - pub fn last_input_was_keyboard(&self) -> bool { - self.last_input_modality == InputModality::Keyboard - } - - pub(crate) fn last_input_was_touch(&self) -> bool { - self.last_input_modality == InputModality::Touch - } - - /// The current state of the keyboard's capslock - pub fn capslock(&self) -> Capslock { - self.capslock - } - - /// Produces a new frame and assigns it to `rendered_frame`. To actually show - /// the contents of the new [`Scene`], use [`Self::present`]. - #[profiling::function] - pub fn draw(&mut self, cx: &mut App) -> ArenaClearNeeded { - // Drain every draw in profiler builds so a previous frame's - // first-invalidation timestamp can't be attributed to this one. - #[cfg(feature = "profiler")] - let frame_dirty = self.invalidator.take_frame_dirty(); - #[cfg(feature = "profiler")] - self.window_profiler.begin_draw(); - - // Set up the per-App arena for element allocation during this draw. - // This ensures that multiple test Apps have isolated arenas. - let arena_scope = ElementArenaScope::enter(&cx.element_arena); - - self.invalidate_entities(); - cx.entities.clear_accessed(); - debug_assert!(self.rendered_entity_stack.is_empty()); - self.invalidator.set_dirty(false); - self.requested_autoscroll = None; - - // Restore the previously-used input handler. - // Place it back into a None slot (left by a previous .take()) so that - // cached paint_range indices in reuse_paint find the handler at the - // expected position. - if let Some(input_handler) = self.platform_window.take_input_handler() { - if let Some(slot) = self - .rendered_frame - .input_handlers - .iter_mut() - .rev() - .find(|h| h.is_none()) - { - *slot = Some(input_handler); - } else { - self.rendered_frame.input_handlers.push(Some(input_handler)); - } - } - if !cx.mode.skip_drawing() { - self.draw_roots(cx); - #[cfg(feature = "profiler")] - { - let viewport_size = self.viewport_size; - let scale_factor = self.scale_factor(); - self.debug_frame_overlay.paint( - &mut self.next_frame.scene, - viewport_size, - scale_factor, - ); - } - } - self.dirty_views.clear(); - self.next_frame.window_active = self.active.get(); - - // Register requested input handler with the platform window. - // Use .take() instead of .pop() to preserve Vec length, so that cached - // paint_range indices remain valid for reuse_paint on the next frame. - // Search backwards to find the last Some entry, since reuse_paint may - // have copied None slots from the previous frame. (Fixes #50456) - let focused_text_input_active = if let Some(mut input_handler) = self - .next_frame - .input_handlers - .iter_mut() - .rev() - .find_map(|h| h.take()) - { - let accepts_text_input = input_handler.accepts_text_input(self, cx); - self.platform_window.set_input_handler(input_handler); - accepts_text_input - } else { - false - }; - self.apply_text_input_configuration(cx); - if focused_text_input_active != self.focused_text_input_active { - self.focused_text_input_active = focused_text_input_active; - self.platform_window - .text_input_state_changed(if focused_text_input_active { - TextInputStateChange::FocusGained - } else { - TextInputStateChange::FocusLost - }); - } - - self.layout_engine.as_mut().unwrap().clear(); - self.text_system().finish_frame(); - self.next_frame.finish(&mut self.rendered_frame); - - self.invalidator.set_phase(DrawPhase::Focus); - let previous_focus_path = self.rendered_frame.focus_path(); - let previous_window_active = self.rendered_frame.window_active; - mem::swap(&mut self.rendered_frame, &mut self.next_frame); - self.next_frame.clear(); - let current_focus_path = self.rendered_frame.focus_path(); - let current_window_active = self.rendered_frame.window_active; - let mut focus_before_listeners = self.focus; - - if previous_focus_path != current_focus_path - || previous_window_active != current_window_active - { - if !previous_focus_path.is_empty() && current_focus_path.is_empty() { - self.focus_lost_path = previous_focus_path.clone(); - self.focus_lost_listeners - .clone() - .retain(&(), |listener| listener(self, cx)); - self.focus_lost_path = SmallVec::new(); - // The focus-lost fallback (e.g. a workspace refocusing itself) may target - // an element that isn't part of the element tree, in which case scheduling - // a redraw below would dispatch focus-lost again, looping forever. Only - // track focus movement caused by the focus listeners. - focus_before_listeners = self.focus; - } - - let event = WindowFocusEvent { - previous_focus_path: if previous_window_active { - previous_focus_path - } else { - Default::default() - }, - current_focus_path: if current_window_active { - current_focus_path - } else { - Default::default() - }, - }; - self.focus_listeners - .clone() - .retain(&(), |listener| listener(&event, self, cx)); - } - - debug_assert!(self.rendered_entity_stack.is_empty()); - self.record_entities_accessed(cx); - self.reset_cursor_style(cx); - self.refreshing = false; - self.invalidator.set_phase(DrawPhase::None); - // Focus listeners may move focus (e.g. a dock forwarding focus to its active - // panel). `Window::focus` suppresses `refresh` while a draw is in progress, so - // schedule another frame here to render the new focus state and dispatch the - // resulting focus events. - if self.focus != focus_before_listeners { - self.refresh(); - } - self.needs_present.set(true); - - #[cfg(feature = "profiler")] - { - let draw_duration = self - .window_profiler - .end_draw(frame_dirty.dirty_at, frame_dirty.invalidations); - self.debug_frame_overlay.record_frame(draw_duration); - } - - // Exit the scope to obtain the arena-clear token this draw owes; the - // scope's teardown itself happens in `ElementArenaScope::drop`. - arena_scope.exit(&cx.element_arena) - } - - fn record_entities_accessed(&mut self, cx: &mut App) { - let mut entities_ref = cx.entities.accessed_entities.get_mut(); - let mut entities = mem::take(entities_ref.deref_mut()); - let handle = self.handle; - cx.record_entities_accessed( - handle, - // Try moving window invalidator into the Window - self.invalidator.clone(), - &entities, - ); - let mut entities_ref = cx.entities.accessed_entities.get_mut(); - mem::swap(&mut entities, entities_ref.deref_mut()); - } - - fn invalidate_entities(&mut self) { - let mut views = self.invalidator.take_views(); - for entity in views.drain() { - self.mark_view_dirty(entity); - } - self.invalidator.replace_views(views); - } - - #[profiling::function] - fn present(&mut self) { - #[cfg(feature = "profiler")] - let _foreground_turn = profiler::journal::foreground_turn(); - #[cfg(feature = "profiler")] - let present_start = Instant::now(); - self.platform_window.draw(&self.rendered_frame.scene); - #[cfg(feature = "profiler")] - self.window_profiler.record_present( - present_start, - Instant::now(), - self.active.get(), - !self.next_frame_callbacks.borrow().is_empty(), - ); - self.needs_present.set(false); - profiling::finish_frame!(); - } - - /// Presents the most recently drawn frame if it hasn't been presented yet. - /// - /// Benchmarks drive drawing synchronously rather than through a platform - /// frame-request loop, so they call this after each measured update to - /// submit the frame like production presentation would. - #[cfg(any(feature = "bench-support", all(test, feature = "profiler")))] - pub fn present_if_needed(&mut self) { - if self.needs_present.get() { - self.present(); - } - } - - /// Returns a snapshot of the current input-latency histograms. - #[cfg(feature = "profiler")] - pub fn input_latency_snapshot(&self) -> profiler::InputLatencySnapshot { - self.window_profiler.input_latency_snapshot() - } - - /// Returns a snapshot of the current frame-duration histograms. - #[cfg(feature = "profiler")] - pub fn frame_duration_snapshot(&self) -> profiler::FrameDurationSnapshot { - self.window_profiler.frame_duration_snapshot() - } - - /// Returns the current mode of the debug frame overlay. - #[cfg(feature = "profiler")] - pub fn debug_frame_overlay_mode(&self) -> DebugFrameOverlayMode { - self.debug_frame_overlay.mode() - } - - /// Sets the mode of the debug frame overlay and schedules a redraw. - #[cfg(feature = "profiler")] - pub fn set_debug_frame_overlay_mode(&mut self, mode: DebugFrameOverlayMode) { - self.debug_frame_overlay.set_mode(mode); - self.refresh(); - } - - /// Advances the debug frame overlay through its hidden, frame-time-only, - /// and detailed modes. - #[cfg(feature = "profiler")] - pub fn cycle_debug_frame_overlay_mode(&mut self) { - self.set_debug_frame_overlay_mode(self.debug_frame_overlay.mode().next()); - } - - /// Clears the debug frame overlay's frame-time statistics, except for the - /// total frame count, and schedules a redraw. - #[cfg(feature = "profiler")] - pub fn reset_debug_frame_overlay_stats(&mut self) { - self.debug_frame_overlay.reset_stats(); - self.refresh(); - } - - fn draw_roots(&mut self, cx: &mut App) { - self.invalidator.set_phase(DrawPhase::Prepaint); - self.tooltip_bounds.take(); - - self.a11y.sync_active_flag(); - if self.a11y.is_active() { - self.a11y.begin_frame(); - } - - let _inspector_width: Pixels = rems(30.0).to_pixels(self.rem_size()); - let root_size = { - #[cfg(any(feature = "inspector", debug_assertions))] - { - if self.inspector.is_some() { - let mut size = self.viewport_size; - size.width = (size.width - _inspector_width).max(px(0.0)); - size - } else { - self.viewport_size - } - } - #[cfg(not(any(feature = "inspector", debug_assertions)))] - { - self.viewport_size - } - }; - - // Layout all root elements. Like the root element on the web, which - // stretches to fill the viewport unless explicitly sized, window roots - // fill the window when their size is `auto`. - let scale_factor = self.scale_factor(); - let mut root_element = self.root.as_ref().unwrap().clone().into_any_element(); - let root_layout_id = root_element.request_layout(self, cx); - self.layout_engine - .as_mut() - .unwrap() - .stretch_auto_size_to_fill(root_layout_id, root_size, scale_factor); - root_element.prepaint_as_root(Point::default(), root_size.into(), self, cx); - - #[cfg(any(feature = "inspector", debug_assertions))] - let inspector_element = self.prepaint_inspector(_inspector_width, cx); - - self.prepaint_deferred_draws(cx); - - let mut prompt_element = None; - let mut active_drag_element = None; - let mut tooltip_element = None; - if let Some(prompt) = self.prompt.take() { - let mut element = prompt.view.any_view().into_any_element(); - let prompt_layout_id = element.request_layout(self, cx); - self.layout_engine - .as_mut() - .unwrap() - .stretch_auto_size_to_fill(prompt_layout_id, root_size, scale_factor); - element.prepaint_as_root(Point::default(), root_size.into(), self, cx); - prompt_element = Some(element); - self.prompt = Some(prompt); - } else if let Some(active_drag) = cx.active_drag.take() { - let mut element = active_drag.view.clone().into_any_element(); - let offset = self.mouse_position() - active_drag.cursor_offset; - element.prepaint_as_root(offset, AvailableSpace::min_size(), self, cx); - active_drag_element = Some(element); - cx.active_drag = Some(active_drag); - } else { - tooltip_element = self.prepaint_tooltip(cx); - } - - self.mouse_hit_test = self.next_frame.hit_test(self.mouse_position); - - // Now actually paint the elements. - self.invalidator.set_phase(DrawPhase::Paint); - root_element.paint(self, cx); - - #[cfg(any(feature = "inspector", debug_assertions))] - self.paint_inspector(inspector_element, cx); - - self.paint_deferred_draws(cx); - - if let Some(mut prompt_element) = prompt_element { - prompt_element.paint(self, cx); - } else if let Some(mut drag_element) = active_drag_element { - drag_element.paint(self, cx); - } else if let Some(mut tooltip_element) = tooltip_element { - tooltip_element.paint(self, cx); - } - - #[cfg(any(feature = "inspector", debug_assertions))] - self.paint_inspector_hitbox(cx); - - // a11y may have been activated/deactivated halfway through the frame - let a11y_active_start_of_frame = self.a11y.is_active(); - self.a11y.sync_active_flag(); - let a11y_active_end_of_frame = self.a11y.is_active(); - - let should_send_a11y_update = a11y_active_start_of_frame && a11y_active_end_of_frame; - - if a11y_active_start_of_frame { - // Harvest frame metadata for the debug dump while the live window - // and frame are still in scope. - let frame_info = crate::window::a11y::debug::FrameDebugInfo { - viewport_size: self.viewport_size, - scale_factor: self.scale_factor, - tab_stop_count: self.next_frame.tab_stops.tab_stop_count(), - }; - // clear the builder state regardless - let tree_update = self.a11y.end_frame(frame_info); - - if should_send_a11y_update { - log::debug!( - "Sending a11y tree update: {} nodes", - tree_update.nodes.len() - ); - self.platform_window.a11y_tree_update(tree_update); - } - } - } - - fn prepaint_tooltip(&mut self, cx: &mut App) -> Option { - // Use indexing instead of iteration to avoid borrowing self for the duration of the loop. - for tooltip_request_index in (0..self.next_frame.tooltip_requests.len()).rev() { - let Some(Some(tooltip_request)) = self - .next_frame - .tooltip_requests - .get(tooltip_request_index) - .cloned() - else { - log::error!("Unexpectedly absent TooltipRequest"); - continue; - }; - let mut element = tooltip_request.tooltip.view.clone().into_any_element(); - let mouse_position = tooltip_request.tooltip.mouse_position; - let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self, cx); - - let mut tooltip_bounds = - Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size); - let window_bounds = Bounds { - origin: Point::default(), - size: self.viewport_size(), - }; - - if tooltip_bounds.right() > window_bounds.right() { - let new_x = mouse_position.x - tooltip_bounds.size.width - px(1.); - if new_x >= Pixels::ZERO { - tooltip_bounds.origin.x = new_x; - } else { - tooltip_bounds.origin.x = cmp::max( - Pixels::ZERO, - tooltip_bounds.origin.x - tooltip_bounds.right() - window_bounds.right(), - ); - } - } - - if tooltip_bounds.bottom() > window_bounds.bottom() { - let new_y = mouse_position.y - tooltip_bounds.size.height - px(1.); - if new_y >= Pixels::ZERO { - tooltip_bounds.origin.y = new_y; - } else { - tooltip_bounds.origin.y = cmp::max( - Pixels::ZERO, - tooltip_bounds.origin.y - tooltip_bounds.bottom() - window_bounds.bottom(), - ); - } - } - - // It's possible for an element to have an active tooltip while not being painted (e.g. - // via the `visible_on_hover` method). Since mouse listeners are not active in this - // case, instead update the tooltip's visibility here. - let is_visible = - (tooltip_request.tooltip.check_visible_and_update)(tooltip_bounds, self, cx); - if !is_visible { - continue; - } - - self.with_absolute_element_offset(tooltip_bounds.origin, |window| { - element.prepaint(window, cx) - }); - - self.tooltip_bounds = Some(TooltipBounds { - id: tooltip_request.id, - bounds: tooltip_bounds, - }); - return Some(element); - } - None - } - - fn prepaint_deferred_draws(&mut self, cx: &mut App) { - assert_eq!(self.element_id_stack.len(), 0); - - // Process deferred draws in multiple rounds to support nesting. - // Each round processes all current deferred draws, which may push new ones. - // - // The draws are processed in place rather than being moved out of - // `next_frame.deferred_draws`: `prepaint_index` snapshots that vector's - // length, so any prepaint range recorded during a round (view caches, - // nested deferred draws) must index the same vector `reuse_prepaint` - // slices on the next frame. Moving the draws out and re-appending them - // shifts the indices of nested draws, causing reused subtrees to graft - // the wrong deferred draws and panic in the dispatch tree. - let mut round_start = 0; - let mut depth = 0; - loop { - let round_end = self.next_frame.deferred_draws.len(); - if round_start == round_end { - break; - } - // Limit maximum nesting depth to prevent infinite loops. - assert!(depth < 10, "Exceeded maximum (10) deferred depth"); - depth += 1; - - // Sort this round by priority. - let mut traversal_order = (round_start..round_end).collect::>(); - traversal_order.sort_by_key(|ix| self.next_frame.deferred_draws[*ix].priority); - - for deferred_draw_ix in traversal_order { - let (element, parent_node, current_view, rem_size, absolute_offset, prepaint_range) = { - let deferred_draw = &mut self.next_frame.deferred_draws[deferred_draw_ix]; - self.element_id_stack - .clone_from(&deferred_draw.element_id_stack); - self.text_style_stack - .clone_from(&deferred_draw.text_style_stack); - ( - deferred_draw.element.take(), - deferred_draw.parent_node, - deferred_draw.current_view, - deferred_draw.rem_size, - deferred_draw.absolute_offset, - deferred_draw.prepaint_range.clone(), - ) - }; - self.next_frame.dispatch_tree.set_active_node(parent_node); - - let prepaint_start = self.prepaint_index(); - if let Some(mut element) = element { - self.with_rendered_view(current_view, |window| { - window.with_rem_size(Some(rem_size), |window| { - window.with_absolute_element_offset(absolute_offset, |window| { - element.prepaint(window, cx); - }); - }); - }); - self.next_frame.deferred_draws[deferred_draw_ix].element = Some(element); - } else { - self.reuse_prepaint(prepaint_range); - } - let prepaint_end = self.prepaint_index(); - self.next_frame.deferred_draws[deferred_draw_ix].prepaint_range = - prepaint_start..prepaint_end; - } - - self.element_id_stack.clear(); - self.text_style_stack.clear(); - round_start = round_end; - } - } - - fn paint_deferred_draws(&mut self, cx: &mut App) { - assert_eq!(self.element_id_stack.len(), 0); - - // Paint all deferred draws in priority order. - // Since prepaint has already processed nested deferreds, we just paint them all. - if self.next_frame.deferred_draws.len() == 0 { - return; - } - - let traversal_order = self.deferred_draw_traversal_order(); - let mut deferred_draws = mem::take(&mut self.next_frame.deferred_draws); - for deferred_draw_ix in traversal_order { - let mut deferred_draw = &mut deferred_draws[deferred_draw_ix]; - self.element_id_stack - .clone_from(&deferred_draw.element_id_stack); - self.next_frame - .dispatch_tree - .set_active_node(deferred_draw.parent_node); - - let paint_start = self.paint_index(); - let content_mask = deferred_draw.content_mask.clone(); - if let Some(element) = deferred_draw.element.as_mut() { - self.with_rendered_view(deferred_draw.current_view, |window| { - window.with_content_mask(content_mask, |window| { - window.with_rem_size(Some(deferred_draw.rem_size), |window| { - element.paint(window, cx); - }); - }) - }) - } else { - self.reuse_paint(deferred_draw.paint_range.clone()); - } - let paint_end = self.paint_index(); - deferred_draw.paint_range = paint_start..paint_end; - } - self.next_frame.deferred_draws = deferred_draws; - self.element_id_stack.clear(); - } - - fn deferred_draw_traversal_order(&mut self) -> SmallVec<[usize; 8]> { - let deferred_count = self.next_frame.deferred_draws.len(); - let mut sorted_indices = (0..deferred_count).collect::>(); - sorted_indices.sort_by_key(|ix| self.next_frame.deferred_draws[*ix].priority); - sorted_indices - } - - pub(crate) fn prepaint_index(&self) -> PrepaintStateIndex { - PrepaintStateIndex { - hitboxes_index: self.next_frame.hitboxes.len(), - tooltips_index: self.next_frame.tooltip_requests.len(), - deferred_draws_index: self.next_frame.deferred_draws.len(), - dispatch_tree_index: self.next_frame.dispatch_tree.len(), - accessed_element_states_index: self.next_frame.accessed_element_states.len(), - line_layout_index: self.text_system.layout_index(), - } - } - - pub(crate) fn reuse_prepaint(&mut self, range: Range) { - self.next_frame.hitboxes.extend( - self.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index] - .iter() - .cloned(), - ); - self.next_frame.tooltip_requests.extend( - self.rendered_frame.tooltip_requests - [range.start.tooltips_index..range.end.tooltips_index] - .iter_mut() - .map(|request| request.take()), - ); - self.next_frame.accessed_element_states.extend( - self.rendered_frame.accessed_element_states[range.start.accessed_element_states_index - ..range.end.accessed_element_states_index] - .iter() - .map(|(id, type_id)| (id.clone(), *type_id)), - ); - self.text_system - .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index); - - let reused_subtree = self.next_frame.dispatch_tree.reuse_subtree( - range.start.dispatch_tree_index..range.end.dispatch_tree_index, - &mut self.rendered_frame.dispatch_tree, - self.focus, - ); - - if reused_subtree.contains_focus() { - self.next_frame.focus = self.focus; - } - - self.next_frame.deferred_draws.extend( - self.rendered_frame.deferred_draws - [range.start.deferred_draws_index..range.end.deferred_draws_index] - .iter() - .map(|deferred_draw| DeferredDraw { - current_view: deferred_draw.current_view, - parent_node: reused_subtree.refresh_node_id(deferred_draw.parent_node), - element_id_stack: deferred_draw.element_id_stack.clone(), - text_style_stack: deferred_draw.text_style_stack.clone(), - content_mask: deferred_draw.content_mask.clone(), - rem_size: deferred_draw.rem_size, - priority: deferred_draw.priority, - element: None, - absolute_offset: deferred_draw.absolute_offset, - prepaint_range: deferred_draw.prepaint_range.clone(), - paint_range: deferred_draw.paint_range.clone(), - }), - ); - } - - pub(crate) fn paint_index(&self) -> PaintIndex { - PaintIndex { - scene_index: self.next_frame.scene.len(), - mouse_listeners_index: self.next_frame.mouse_listeners.len(), - input_handlers_index: self.next_frame.input_handlers.len(), - cursor_styles_index: self.next_frame.cursor_styles.len(), - accessed_element_states_index: self.next_frame.accessed_element_states.len(), - tab_handle_index: self.next_frame.tab_stops.paint_index(), - line_layout_index: self.text_system.layout_index(), - } - } - - pub(crate) fn reuse_paint(&mut self, range: Range) { - self.next_frame.cursor_styles.extend( - self.rendered_frame.cursor_styles - [range.start.cursor_styles_index..range.end.cursor_styles_index] - .iter() - .cloned(), - ); - self.next_frame.input_handlers.extend( - self.rendered_frame.input_handlers - [range.start.input_handlers_index..range.end.input_handlers_index] - .iter_mut() - .map(|handler| handler.take()), - ); - self.next_frame.mouse_listeners.extend( - self.rendered_frame.mouse_listeners - [range.start.mouse_listeners_index..range.end.mouse_listeners_index] - .iter_mut() - .map(|listener| listener.take()), - ); - self.next_frame.accessed_element_states.extend( - self.rendered_frame.accessed_element_states[range.start.accessed_element_states_index - ..range.end.accessed_element_states_index] - .iter() - .map(|(id, type_id)| (id.clone(), *type_id)), - ); - self.next_frame.tab_stops.replay( - &self.rendered_frame.tab_stops.insertion_history - [range.start.tab_handle_index..range.end.tab_handle_index], - ); - - self.text_system - .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index); - self.next_frame.scene.replay( - range.start.scene_index..range.end.scene_index, - &self.rendered_frame.scene, - ); - } - - /// Push a text style onto the stack, and call a function with that style active. - /// Use [`Window::text_style`] to get the current, combined text style. This method - /// should only be called as part of element drawing. - pub fn with_text_style(&mut self, style: Option, f: F) -> R - where - F: FnOnce(&mut Self) -> R, - { - self.invalidator.debug_assert_paint_or_prepaint(); - if let Some(style) = style { - self.text_style_stack.push(style); - let result = f(self); - self.text_style_stack.pop(); - result - } else { - f(self) - } - } - - /// Updates the cursor style at the platform level. This method should only be called - /// during the paint phase of element drawing. - pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) { - self.invalidator.debug_assert_paint(); - self.next_frame.cursor_styles.push(CursorStyleRequest { - hitbox_id: Some(hitbox.id), - style, - }); - } - - /// Updates the cursor style for the entire window at the platform level. A cursor - /// style using this method will have precedence over any cursor style set using - /// `set_cursor_style`. This method should only be called during the paint - /// phase of element drawing. - pub fn set_window_cursor_style(&mut self, style: CursorStyle) { - self.invalidator.debug_assert_paint(); - self.next_frame.cursor_styles.push(CursorStyleRequest { - hitbox_id: None, - style, - }) - } - - /// Sets a tooltip to be rendered for the upcoming frame. This method should only be called - /// during the paint phase of element drawing. - pub fn set_tooltip(&mut self, tooltip: AnyTooltip) -> TooltipId { - self.invalidator.debug_assert_prepaint(); - let id = TooltipId(post_inc(&mut self.next_tooltip_id.0)); - self.next_frame - .tooltip_requests - .push(Some(TooltipRequest { id, tooltip })); - id - } - - /// Invoke the given function with the given content mask after intersecting it - /// with the current mask. This method should only be called during element drawing. - // This function is called in a highly recursive manner in editor - // prepainting, make sure its inlined to reduce the stack burden - #[inline] - pub fn with_content_mask( - &mut self, - mask: Option>, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.invalidator.debug_assert_paint_or_prepaint(); - if let Some(mask) = mask { - let mask = mask.into().intersect(&self.content_mask()); - self.content_mask_stack.push(mask); - let result = f(self); - self.content_mask_stack.pop(); - result - } else { - f(self) - } - } - - /// Updates the global element offset relative to the current offset. This is used to implement - /// scrolling. This method should only be called during the prepaint phase of element drawing. - pub fn with_element_offset( - &mut self, - offset: Point, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.invalidator.debug_assert_prepaint(); - - if offset.is_zero() { - return f(self); - }; - - let abs_offset = self.element_offset() + offset; - self.with_absolute_element_offset(abs_offset, f) - } - - /// Updates the global element offset based on the given offset. This is used to implement - /// drag handles and other manual painting of elements. This method should only be called during - /// the prepaint phase of element drawing. - pub fn with_absolute_element_offset( - &mut self, - offset: Point, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.invalidator.debug_assert_prepaint(); - self.element_offset_stack.push(offset); - let result = f(self); - self.element_offset_stack.pop(); - result - } - - pub(crate) fn with_element_opacity( - &mut self, - opacity: Option, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.invalidator.debug_assert_paint_or_prepaint(); - - let Some(opacity) = opacity else { - return f(self); - }; - - let previous_opacity = self.element_opacity; - self.element_opacity = previous_opacity * opacity; - let result = f(self); - self.element_opacity = previous_opacity; - result - } - - /// Perform prepaint on child elements in a "retryable" manner, so that any side effects - /// of prepaints can be discarded before prepainting again. This is used to support autoscroll - /// where we need to prepaint children to detect the autoscroll bounds, then adjust the - /// element offset and prepaint again. See [`crate::List`] for an example. This method should only be - /// called during the prepaint phase of element drawing. - pub fn transact(&mut self, f: impl FnOnce(&mut Self) -> Result) -> Result { - self.invalidator.debug_assert_prepaint(); - let index = self.prepaint_index(); - let result = f(self); - if result.is_err() { - self.next_frame.hitboxes.truncate(index.hitboxes_index); - self.next_frame - .tooltip_requests - .truncate(index.tooltips_index); - self.next_frame - .deferred_draws - .truncate(index.deferred_draws_index); - self.next_frame - .dispatch_tree - .truncate(index.dispatch_tree_index); - self.next_frame - .accessed_element_states - .truncate(index.accessed_element_states_index); - self.text_system.truncate_layouts(index.line_layout_index); - } - result - } - - /// When you call this method during [`Element::prepaint`], containing elements will attempt to - /// scroll to cause the specified bounds to become visible. When they decide to autoscroll, they will call - /// [`Element::prepaint`] again with a new set of bounds. See [`crate::List`] for an example of an element - /// that supports this method being called on the elements it contains. This method should only be - /// called during the prepaint phase of element drawing. - pub fn request_autoscroll(&mut self, bounds: Bounds) { - self.invalidator.debug_assert_prepaint(); - self.requested_autoscroll = Some(bounds); - } - - /// This method can be called from a containing element such as [`crate::List`] to support the autoscroll behavior - /// described in [`Self::request_autoscroll`]. - pub fn take_autoscroll(&mut self) -> Option> { - self.invalidator.debug_assert_prepaint(); - self.requested_autoscroll.take() - } - - /// Asynchronously load an asset, if the asset hasn't finished loading this will return None. - /// Your view will be re-drawn once the asset has finished loading. - /// - /// Note that the multiple calls to this method will only result in one `Asset::load` call at a - /// time. - pub fn use_asset(&mut self, source: &A::Source, cx: &mut App) -> Option { - let (task, is_first) = cx.fetch_asset::(source); - task.clone().now_or_never().or_else(|| { - if is_first { - let entity_id = self.current_view(); - self.spawn(cx, { - let task = task.clone(); - async move |cx| { - task.await; - - cx.on_next_frame(move |_, cx| { - cx.notify(entity_id); - }); - } - }) - .detach(); - } - - None - }) - } - - /// Asynchronously load an asset, if the asset hasn't finished loading or doesn't exist this will return None. - /// Your view will not be re-drawn once the asset has finished loading. - /// - /// Note that the multiple calls to this method will only result in one `Asset::load` call at a - /// time. - pub fn get_asset(&mut self, source: &A::Source, cx: &mut App) -> Option { - let (task, _) = cx.fetch_asset::(source); - task.now_or_never() - } - /// Obtain the current element offset. This method should only be called during the - /// prepaint phase of element drawing. - pub fn element_offset(&self) -> Point { - self.invalidator.debug_assert_prepaint(); - self.element_offset_stack - .last() - .copied() - .unwrap_or_default() - } - - /// Obtain the current element opacity. This method should only be called during the - /// prepaint phase of element drawing. - #[inline] - pub(crate) fn element_opacity(&self) -> f32 { - self.invalidator.debug_assert_paint_or_prepaint(); - self.element_opacity - } - - /// Obtain the current content mask. This method should only be called during element drawing. - pub fn content_mask(&self) -> crate::ClipRegion { - self.invalidator.debug_assert_paint_or_prepaint(); - self.content_mask_stack.last().cloned().unwrap_or_else(|| { - ContentMask { - bounds: Bounds { - origin: Point::default(), - size: self.viewport_size, - }, - ..Default::default() - } - .into() - }) - } - - /// Provide elements in the called function with a new namespace in which their identifiers must be unique. - /// This can be used within a custom element to distinguish multiple sets of child elements. - pub fn with_element_namespace( - &mut self, - element_id: impl Into, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.element_id_stack.push(element_id.into()); - let result = f(self); - self.element_id_stack.pop(); - result - } - - /// Use a piece of state that exists as long this element is being rendered in consecutive frames. - pub fn use_keyed_state( - &mut self, - key: impl Into, - cx: &mut App, - init: impl FnOnce(&mut Self, &mut Context) -> S, - ) -> Entity { - let current_view = self.current_view(); - self.with_global_id(key.into(), |global_id, window| { - window.with_element_state(global_id, |state: Option>, window| { - if let Some(state) = state { - (state.clone(), state) - } else { - let new_state = cx.new(|cx| init(window, cx)); - cx.observe(&new_state, move |_, cx| { - cx.notify(current_view); - }) - .detach(); - (new_state.clone(), new_state) - } - }) - }) - } - - /// Use a piece of state that exists as long this element is being rendered in consecutive frames, without needing to specify a key - /// - /// NOTE: This method uses the location of the caller to generate an ID for this state. - /// If this is not sufficient to identify your state (e.g. you're rendering a list item), - /// you can provide a custom ElementID using the `use_keyed_state` method. - #[track_caller] - pub fn use_state( - &mut self, - cx: &mut App, - init: impl FnOnce(&mut Self, &mut Context) -> S, - ) -> Entity { - self.use_keyed_state( - ElementId::CodeLocation(*core::panic::Location::caller()), - cx, - init, - ) - } - - /// Updates or initializes state for an element with the given id that lives across multiple - /// frames. If an element with this ID existed in the rendered frame, its state will be passed - /// to the given closure. The state returned by the closure will be stored so it can be referenced - /// when drawing the next frame. This method should only be called as part of element drawing. - pub fn with_element_state( - &mut self, - global_id: &GlobalElementId, - f: impl FnOnce(Option, &mut Self) -> (R, S), - ) -> R - where - S: 'static, - { - self.invalidator.debug_assert_paint_or_prepaint(); - - let key = (global_id.clone(), TypeId::of::()); - self.next_frame.accessed_element_states.push(key.clone()); - - if let Some(any) = self - .next_frame - .element_states - .remove(&key) - .or_else(|| self.rendered_frame.element_states.remove(&key)) - { - let ElementStateBox { - inner, - #[cfg(debug_assertions)] - type_name, - } = any; - // Using the extra inner option to avoid needing to reallocate a new box. - let mut state_box = inner - .downcast::>() - .map_err(|_| { - #[cfg(debug_assertions)] - { - anyhow::anyhow!( - "invalid element state type for id, requested {:?}, actual: {:?}", - std::any::type_name::(), - type_name - ) - } - - #[cfg(not(debug_assertions))] - { - anyhow::anyhow!( - "invalid element state type for id, requested {:?}", - std::any::type_name::(), - ) - } - }) - .unwrap(); - - let state = state_box.take().expect( - "reentrant call to with_element_state for the same state type and element id", - ); - let (result, state) = f(Some(state), self); - state_box.replace(state); - self.next_frame.element_states.insert( - key, - ElementStateBox { - inner: state_box, - #[cfg(debug_assertions)] - type_name, - }, - ); - result - } else { - let (result, state) = f(None, self); - self.next_frame.element_states.insert( - key, - ElementStateBox { - inner: Box::new(Some(state)), - #[cfg(debug_assertions)] - type_name: std::any::type_name::(), - }, - ); - result - } - } - - /// A variant of `with_element_state` that allows the element's id to be optional. This is a convenience - /// method for elements where the element id may or may not be assigned. Prefer using `with_element_state` - /// when the element is guaranteed to have an id. - /// - /// The first option means 'no ID provided' - /// The second option means 'not yet initialized' - pub fn with_optional_element_state( - &mut self, - global_id: Option<&GlobalElementId>, - f: impl FnOnce(Option>, &mut Self) -> (R, Option), - ) -> R - where - S: 'static, - { - self.invalidator.debug_assert_paint_or_prepaint(); - - if let Some(global_id) = global_id { - self.with_element_state(global_id, |state, cx| { - let (result, state) = f(Some(state), cx); - let state = - state.expect("you must return some state when you pass some element id"); - (result, state) - }) - } else { - let (result, state) = f(None, self); - debug_assert!( - state.is_none(), - "you must not return an element state when passing None for the global id" - ); - result - } - } - - /// Executes the given closure within the context of a tab group. - #[inline] - pub fn with_tab_group(&mut self, index: Option, f: impl FnOnce(&mut Self) -> R) -> R { - if let Some(index) = index { - self.next_frame.tab_stops.begin_group(index); - let result = f(self); - self.next_frame.tab_stops.end_group(); - result - } else { - f(self) - } - } - - /// Defers the drawing of the given element, scheduling it to be painted on top of the currently-drawn tree - /// at a later time. The `priority` parameter determines the drawing order relative to other deferred elements, - /// with higher values being drawn on top. - /// - /// When `content_mask` is provided, the deferred element will be clipped to that region during - /// both prepaint and paint. When `None`, no additional clipping is applied. - /// - /// This method should only be called as part of the prepaint phase of element drawing. - pub fn defer_draw( - &mut self, - element: AnyElement, - absolute_offset: Point, - priority: usize, - content_mask: Option, - ) { - self.invalidator.debug_assert_prepaint(); - let parent_node = self.next_frame.dispatch_tree.active_node_id().unwrap(); - self.next_frame.deferred_draws.push(DeferredDraw { - current_view: self.current_view(), - parent_node, - element_id_stack: self.element_id_stack.clone(), - text_style_stack: self.text_style_stack.clone(), - content_mask, - rem_size: self.rem_size(), - priority, - element: Some(element), - absolute_offset, - prepaint_range: PrepaintStateIndex::default()..PrepaintStateIndex::default(), - paint_range: PaintIndex::default()..PaintIndex::default(), - }); - } - - /// Creates a new painting layer for the specified bounds. A "layer" is a batch - /// of geometry that are non-overlapping and have the same draw order. This is typically used - /// for performance reasons. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_layer(&mut self, bounds: Bounds, f: impl FnOnce(&mut Self) -> R) -> R { - self.invalidator.debug_assert_paint(); - - let content_mask = self.content_mask(); - let clipped_bounds = bounds.intersect(&content_mask.bounds); - if !clipped_bounds.is_empty() { - self.next_frame - .scene - .push_layer(self.cover_bounds(clipped_bounds)); - } - - let result = f(self); - - if !clipped_bounds.is_empty() { - self.next_frame.scene.pop_layer(); - } - - result - } - - /// Paint the drop (non-inset) shadows from `shadows` into the scene at the current - /// z-index. Inset shadows are skipped; paint those with [`Self::paint_inset_shadows`] - /// after the element's background so they layer on top of the fill. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_drop_shadows( - &mut self, - bounds: Bounds, - corner_radii: Corners, - shadows: &[BoxShadow], - ) { - self.invalidator.debug_assert_paint(); - - let scale_factor = self.scale_factor(); - let content_mask = self.snapped_content_mask(); - let opacity = self.element_opacity(); - let element_bounds = self.cover_bounds(bounds); - let element_corner_radii = corner_radii.scale(scale_factor); - for shadow in shadows { - if shadow.inset { - continue; - } - let shadow_bounds = (bounds + shadow.offset).dilate(shadow.spread_radius); - self.next_frame.scene.insert_primitive(Shadow { - order: 0, - blur_radius: shadow.blur_radius.scale(scale_factor), - bounds: self.cover_bounds(shadow_bounds), - content_mask, - corner_radii: corner_radii.scale(scale_factor), - color: shadow.color.opacity(opacity), - element_bounds, - element_corner_radii, - inset: 0, - pad: 0, - }); - } - } - - /// Paint the inset shadows from `shadows` into the scene at the current z-index. Should - /// be called after the element's background so the shadow layers on top of the fill. - /// Drop shadows are skipped; paint those with [`Self::paint_drop_shadows`] before the background. - pub fn paint_inset_shadows( - &mut self, - bounds: Bounds, - corner_radii: Corners, - shadows: &[BoxShadow], - ) { - self.invalidator.debug_assert_paint(); - - let scale_factor = self.scale_factor(); - let content_mask = self.snapped_content_mask(); - let opacity = self.element_opacity(); - let element_bounds = self.cover_bounds(bounds); - let element_corner_radii = corner_radii.scale(scale_factor); - for shadow in shadows { - if !shadow.inset { - continue; - } - let hole = (bounds + shadow.offset).dilate(-shadow.spread_radius); - // Clamp at zero so a large spread can't produce negative radii, which would - // break the SDF in the shader. - let zero = Pixels::ZERO; - let hole_corner_radii = Corners { - top_left: (corner_radii.top_left - shadow.spread_radius).max(zero), - top_right: (corner_radii.top_right - shadow.spread_radius).max(zero), - bottom_right: (corner_radii.bottom_right - shadow.spread_radius).max(zero), - bottom_left: (corner_radii.bottom_left - shadow.spread_radius).max(zero), - }; - self.next_frame.scene.insert_primitive(Shadow { - order: 0, - blur_radius: shadow.blur_radius.scale(scale_factor), - bounds: self.cover_bounds(hole), - content_mask, - corner_radii: hole_corner_radii.scale(scale_factor), - color: shadow.color.opacity(opacity), - element_bounds, - element_corner_radii, - inset: 1, - pad: 0, - }); - } - } - - fn largest_border_interior(quad: &Quad) -> Bounds { - let radii = &quad.corner_radii; - let widths = &quad.border_widths; - let edge_radii = Edges { - top: radii.top_left.max(radii.top_right), - right: radii.top_right.max(radii.bottom_right), - bottom: radii.bottom_left.max(radii.bottom_right), - left: radii.top_left.max(radii.bottom_left), - }; - - let antialias_inset = point(ScaledPixels(1.0), ScaledPixels(1.0)); - let inset_bounds = |top_left_inset, bottom_right_inset| { - Bounds::from_corners( - quad.bounds.origin + top_left_inset + antialias_inset, - quad.bounds.bottom_right() - bottom_right_inset - antialias_inset, - ) - }; - - // Rounded corners need only be excluded on one axis. Either candidate - // is empty of border pixels, so use the larger interior. - let horizontal_band = inset_bounds( - point(widths.left, widths.top.max(edge_radii.top)), - point(widths.right, widths.bottom.max(edge_radii.bottom)), - ); - let vertical_band = inset_bounds( - point(widths.left.max(edge_radii.left), widths.top), - point(widths.right.max(edge_radii.right), widths.bottom), - ); - - let area = |bounds: &Bounds| { - bounds.size.width.0.max(0.) * bounds.size.height.0.max(0.) - }; - if area(&horizontal_band) >= area(&vertical_band) { - horizontal_band - } else { - vertical_band - } - } - - /// Paint one or more quads into the scene for the next frame at the current stacking context. - /// Quads are colored rectangular regions with an optional background, border, and corner radius. - /// see [`fill`], [`outline`], and [`quad`] to construct this type. - /// - /// This method should only be called as part of the paint phase of element drawing. - /// - /// Note that the `quad.corner_radii` are allowed to exceed the bounds, creating sharp corners - /// where the circular arcs meet. This will not display well when combined with dashed borders. - /// Use `Corners::clamp_radii_for_quad_size` if the radii should fit within the bounds. - pub fn paint_quad(&mut self, quad: PaintQuad) { - self.invalidator.debug_assert_paint(); - - let opacity = self.element_opacity(); - let snapped_bounds = self.snap_bounds(quad.bounds); - let snapped_border_widths = self.snap_border_widths(quad.border_widths); - let quad = Quad { - order: 0, - bounds: snapped_bounds, - content_mask: self.snapped_content_mask(), - background: quad.background.opacity(opacity), - border_color: quad.border_color.opacity(opacity), - corner_radii: quad.corner_radii.scale(self.scale_factor()), - border_widths: snapped_border_widths, - border_style: quad.border_style, - }; - - if !quad.background.is_transparent() { - self.next_frame.scene.insert_primitive(quad); - return; - } - - // Splitting a border-only quad around its empty interior avoids shading - // every transparent pixel inside large outlines. - let outer_bounds = quad.bounds; - let inner_bounds = Self::largest_border_interior(&quad); - - if inner_bounds.is_empty() { - self.next_frame.scene.insert_primitive(quad); - return; - } - - let strips = [ - // Top - Bounds::from_corners( - outer_bounds.origin, - point(outer_bounds.right(), inner_bounds.top()), - ), - // Bottom - Bounds::from_corners( - point(outer_bounds.left(), inner_bounds.bottom()), - outer_bounds.bottom_right(), - ), - // Left - Bounds::from_corners( - point(outer_bounds.left(), inner_bounds.top()), - inner_bounds.bottom_left(), - ), - // Right - Bounds::from_corners( - inner_bounds.top_right(), - point(outer_bounds.right(), inner_bounds.bottom()), - ), - ]; - - for strip in strips { - let content_mask_bounds = quad.content_mask.bounds.intersect(&strip); - if !content_mask_bounds.is_empty() { - self.next_frame.scene.insert_primitive(Quad { - content_mask: ContentMask { - bounds: content_mask_bounds, - ..quad.content_mask - }, - ..quad - }); - } - } - } - - /// Paint the given `Path` into the scene for the next frame at the current z-index. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_path(&mut self, mut path: Path, color: impl Into) { - self.invalidator.debug_assert_paint(); - - let scale_factor = self.scale_factor(); - let content_mask = self.snapped_content_mask(); - let opacity = self.element_opacity(); - let color: Background = color.into(); - path.color = color.opacity(opacity); - let mut path = path.scale(scale_factor); - path.content_mask = content_mask; - self.next_frame.scene.insert_primitive(path); - } - - /// Paint an underline into the scene for the next frame at the current z-index. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_underline( - &mut self, - origin: Point, - width: Pixels, - style: &UnderlineStyle, - ) { - self.invalidator.debug_assert_paint(); - - let scale_factor = self.scale_factor(); - let thickness = self.snap_stroke(style.thickness); - let height = if style.wavy { - ScaledPixels(thickness.0 * 3.) - } else { - thickness - }; - let bounds = Bounds { - origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))), - size: size(self.snap_stroke(width), height), - }; - let element_opacity = self.element_opacity(); - - let content_mask = self.snapped_content_mask(); - self.next_frame.scene.insert_primitive(Underline { - order: 0, - pad: 0, - bounds, - content_mask, - color: style.color.unwrap_or_default().opacity(element_opacity), - thickness, - wavy: style.wavy.into(), - }); - } - - /// Paint a strikethrough into the scene for the next frame at the current z-index. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_strikethrough( - &mut self, - origin: Point, - width: Pixels, - style: &StrikethroughStyle, - ) { - self.invalidator.debug_assert_paint(); - - let scale_factor = self.scale_factor(); - let height = style.thickness; - let bounds = Bounds { - origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))), - size: size(self.snap_stroke(width), self.snap_stroke(height)), - }; - let opacity = self.element_opacity(); - - let content_mask = self.snapped_content_mask(); - self.next_frame.scene.insert_primitive(Underline { - order: 0, - pad: 0, - bounds, - content_mask, - thickness: self.snap_stroke(style.thickness), - color: style.color.unwrap_or_default().opacity(opacity), - wavy: false.into(), - }); - } - - /// Paints a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index. - /// - /// The y component of the origin is the baseline of the glyph. - /// You should generally prefer to use the [`ShapedLine::paint`](crate::ShapedLine::paint) or - /// [`WrappedLine::paint`](crate::WrappedLine::paint) methods in the [`TextSystem`](crate::TextSystem). - /// This method is only useful if you need to paint a single glyph that has already been shaped. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_glyph( - &mut self, - origin: Point, - font_id: FontId, - glyph_id: GlyphId, - font_size: Pixels, - color: Hsla, - ) -> Result<()> { - self.invalidator.debug_assert_paint(); - - let element_opacity = self.element_opacity(); - let scale_factor = self.scale_factor(); - let glyph_origin = origin.scale(scale_factor); - - let quantized_origin = Point::new( - round_half_toward_zero(glyph_origin.x.0 * SUBPIXEL_VARIANTS_X as f32) - / SUBPIXEL_VARIANTS_X as f32, - round_half_toward_zero(glyph_origin.y.0 * SUBPIXEL_VARIANTS_Y as f32) - / SUBPIXEL_VARIANTS_Y as f32, - ); - let subpixel_variant = Point::new( - (quantized_origin.x.fract() * SUBPIXEL_VARIANTS_X as f32) as u8, - (quantized_origin.y.fract() * SUBPIXEL_VARIANTS_Y as f32) as u8, - ); - let integer_origin = quantized_origin.map(|c| ScaledPixels(c.trunc())); - let subpixel_rendering = self.should_use_subpixel_rendering(font_id, font_size); - let dilation = self.text_system().glyph_dilation_for_color(color); - let params = RenderGlyphParams { - font_id, - glyph_id, - font_size, - subpixel_variant, - scale_factor, - is_emoji: false, - subpixel_rendering, - dilation, - }; - - let raster_bounds = self.text_system().raster_bounds(¶ms)?; - if !raster_bounds.is_zero() { - let tile = self - .sprite_atlas - .get_or_insert_with(¶ms.clone().into(), &mut || { - let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?; - Ok(Some((size, Cow::Owned(bytes)))) - })? - .expect("Callback above only errors or returns Some"); - let bounds = Bounds { - origin: integer_origin + raster_bounds.origin.map(Into::into), - size: tile.bounds.size.map(Into::into), - }; - let content_mask = self.snapped_content_mask(); - - if subpixel_rendering { - self.next_frame.scene.insert_primitive(SubpixelSprite { - order: 0, - pad: 0, - bounds, - content_mask, - color: color.opacity(element_opacity), - tile, - transformation: TransformationMatrix::unit(), - }); - } else { - self.next_frame.scene.insert_primitive(MonochromeSprite { - order: 0, - pad: 0, - bounds, - content_mask, - color: color.opacity(element_opacity), - tile, - transformation: TransformationMatrix::unit(), - }); - } - } - Ok(()) - } - - fn should_use_subpixel_rendering(&self, font_id: FontId, font_size: Pixels) -> bool { - if self.platform_window.background_appearance() != WindowBackgroundAppearance::Opaque { - return false; - } - - if !self.platform_window.is_subpixel_rendering_supported() { - return false; - } - - let mode = match self.text_rendering_mode.get() { - TextRenderingMode::PlatformDefault => self - .text_system() - .recommended_rendering_mode(font_id, font_size), - mode => mode, - }; - - mode == TextRenderingMode::Subpixel - } - - /// Paints an emoji glyph into the scene for the next frame at the current z-index. - /// - /// The y component of the origin is the baseline of the glyph. - /// You should generally prefer to use the [`ShapedLine::paint`](crate::ShapedLine::paint) or - /// [`WrappedLine::paint`](crate::WrappedLine::paint) methods in the [`TextSystem`](crate::TextSystem). - /// This method is only useful if you need to paint a single emoji that has already been shaped. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_emoji( - &mut self, - origin: Point, - font_id: FontId, - glyph_id: GlyphId, - font_size: Pixels, - ) -> Result<()> { - self.invalidator.debug_assert_paint(); - - let scale_factor = self.scale_factor(); - let glyph_origin = origin.scale(scale_factor); - let integer_origin = glyph_origin.map(|c| ScaledPixels(round_half_toward_zero(c.0))); - let params = RenderGlyphParams { - font_id, - glyph_id, - font_size, - subpixel_variant: Default::default(), - scale_factor, - is_emoji: true, - subpixel_rendering: false, - dilation: 0, - }; - - let raster_bounds = self.text_system().raster_bounds(¶ms)?; - if !raster_bounds.is_zero() { - let tile = self - .sprite_atlas - .get_or_insert_with(¶ms.clone().into(), &mut || { - let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?; - Ok(Some((size, Cow::Owned(bytes)))) - })? - .expect("Callback above only errors or returns Some"); - - let bounds = Bounds { - origin: integer_origin + raster_bounds.origin.map(Into::into), - size: tile.bounds.size.map(Into::into), - }; - let content_mask = self.snapped_content_mask(); - let opacity = self.element_opacity(); - - self.next_frame.scene.insert_primitive(PolychromeSprite { - order: 0, - pad: 0, - grayscale: false.into(), - bounds, - corner_radii: Default::default(), - content_mask, - tile, - opacity, - }); - } - Ok(()) - } - - /// Paint a monochrome SVG into the scene for the next frame at the current stacking context. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_svg( - &mut self, - bounds: Bounds, - path: SharedString, - mut data: Option<&[u8]>, - transformation: TransformationMatrix, - color: Hsla, - cx: &App, - ) -> Result<()> { - self.invalidator.debug_assert_paint(); - - let element_opacity = self.element_opacity(); - let bounds = self.snap_bounds(bounds); - - let params = RenderSvgParams { - path, - size: bounds.size.map(|pixels| { - DevicePixels::from((pixels.0 * SMOOTH_SVG_SCALE_FACTOR).ceil() as i32) - }), - }; - - let Some(tile) = - self.sprite_atlas - .get_or_insert_with(¶ms.clone().into(), &mut || { - let Some((size, bytes)) = cx.svg_renderer.render_alpha_mask(¶ms, data)? - else { - return Ok(None); - }; - Ok(Some((size, Cow::Owned(bytes)))) - })? - else { - return Ok(()); - }; - let content_mask = self.snapped_content_mask(); - let svg_bounds = Bounds { - origin: bounds.center() - - Point::new( - ScaledPixels(tile.bounds.size.width.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.), - ScaledPixels(tile.bounds.size.height.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.), - ), - size: tile - .bounds - .size - .map(|value| ScaledPixels(value.0 as f32 / SMOOTH_SVG_SCALE_FACTOR)), - }; - let final_bounds = svg_bounds - .map_origin(|value| ScaledPixels(round_half_toward_zero(value.0))) - .map_size(|size| size.ceil()); - - self.next_frame.scene.insert_primitive(MonochromeSprite { - order: 0, - pad: 0, - bounds: final_bounds, - content_mask, - color: color.opacity(element_opacity), - tile, - transformation, - }); - - Ok(()) - } - - /// Paint an image into the scene for the next frame at the current z-index. - /// This method will panic if the frame_index is not valid - /// - /// This method should only be called as part of the paint phase of element drawing. - /// Paint an image into `bounds`, positioning and scaling it according to `image_bounds`. - /// - /// The visible region rendered is `bounds.intersect(&image_bounds)`, with `corner_radii` - /// applied to `bounds`. - pub fn paint_image( - &mut self, - bounds: Bounds, - image_bounds: Bounds, - corner_radii: Corners, - data: Arc, - frame_index: usize, - grayscale: bool, - ) -> Result<()> { - self.invalidator.debug_assert_paint(); - - let visible_bounds = bounds.intersect(&image_bounds); - if visible_bounds.size.width <= Pixels::ZERO || visible_bounds.size.height <= Pixels::ZERO { - return Ok(()); - } - if image_bounds.size.width <= Pixels::ZERO || image_bounds.size.height <= Pixels::ZERO { - return Ok(()); - } - - let params = RenderImageParams { - image_id: data.id, - frame_index, - }; - - let tile = self - .sprite_atlas - .get_or_insert_with(¶ms.into(), &mut || { - Ok(Some(( - data.size(frame_index), - Cow::Borrowed( - data.as_bytes(frame_index) - .expect("It's the caller's job to pass a valid frame index"), - ), - ))) - })? - .expect("Callback above only returns Some"); - - let visible_bounds_snapped = self.snap_bounds(visible_bounds); - - let sub_tile = if visible_bounds == image_bounds { - tile - } else { - let x_offset_ratio = - (visible_bounds.origin.x - image_bounds.origin.x) / image_bounds.size.width; - let y_offset_ratio = - (visible_bounds.origin.y - image_bounds.origin.y) / image_bounds.size.height; - let width_ratio = visible_bounds.size.width / image_bounds.size.width; - let height_ratio = visible_bounds.size.height / image_bounds.size.height; - - let tile_origin_x = tile.bounds.origin.x.0; - let tile_origin_y = tile.bounds.origin.y.0; - let tile_width = tile.bounds.size.width.0; - let tile_height = tile.bounds.size.height.0; - - let sub_origin_x = tile_origin_x + (x_offset_ratio * tile_width as f32).round() as i32; - let sub_origin_y = tile_origin_y + (y_offset_ratio * tile_height as f32).round() as i32; - let sub_width = (width_ratio * tile_width as f32).round() as i32; - let sub_height = (height_ratio * tile_height as f32).round() as i32; - - let max_x = tile_origin_x + tile_width; - let max_y = tile_origin_y + tile_height; - - let clamped_origin_x = sub_origin_x.clamp(tile_origin_x, max_x); - let clamped_origin_y = sub_origin_y.clamp(tile_origin_y, max_y); - let clamped_width = sub_width.min(max_x - clamped_origin_x).max(0); - let clamped_height = sub_height.min(max_y - clamped_origin_y).max(0); - - AtlasTile { - bounds: Bounds { - origin: point( - DevicePixels(clamped_origin_x), - DevicePixels(clamped_origin_y), - ), - size: size(DevicePixels(clamped_width), DevicePixels(clamped_height)), - }, - ..tile - } - }; - - let content_mask = self.snapped_content_mask(); - let corner_radii = corner_radii - .clamp_radii_for_quad_size(visible_bounds.size) - .scale(self.scale_factor()); - let opacity = self.element_opacity(); - - self.next_frame.scene.insert_primitive(PolychromeSprite { - order: 0, - pad: 0, - grayscale: grayscale.into(), - bounds: visible_bounds_snapped, - content_mask, - corner_radii, - tile: sub_tile, - opacity, - }); - Ok(()) - } - - /// Paint a surface into the scene for the next frame at the current z-index. - /// - /// This method should only be called as part of the paint phase of element drawing. - #[cfg(target_os = "macos")] - pub fn paint_surface(&mut self, bounds: Bounds, image_buffer: CVPixelBuffer) { - use crate::PaintSurface; - - self.invalidator.debug_assert_paint(); - - let bounds = self.snap_bounds(bounds); - let content_mask = self.snapped_content_mask(); - self.next_frame.scene.insert_primitive(PaintSurface { - order: 0, - bounds, - content_mask, - image_buffer, - }); - } - - /// Removes an image from the sprite atlas. - pub fn drop_image(&mut self, data: Arc) -> Result<()> { - for frame_index in 0..data.frame_count() { - let params = RenderImageParams { - image_id: data.id, - frame_index, - }; - - self.sprite_atlas.remove(¶ms.clone().into()); - } - - Ok(()) - } - - /// Returns whether every frame of an image is present in the sprite atlas. - #[cfg(any(test, feature = "test-support"))] - pub fn has_image_atlas_entry(&self, data: &RenderImage) -> bool { - data.frame_count() > 0 - && (0..data.frame_count()).all(|frame_index| { - self.sprite_atlas.contains( - &RenderImageParams { - image_id: data.id, - frame_index, - } - .into(), - ) - }) - } - - /// Add a node to the layout tree for the current frame. Takes the `Style` of the element for which - /// layout is being requested, along with the layout ids of any children. This method is called during - /// calls to the [`Element::request_layout`] trait method and enables any element to participate in layout. - /// - /// This method should only be called as part of the request_layout or prepaint phase of element drawing. - #[must_use] - pub fn request_layout( - &mut self, - style: Style, - children: impl IntoIterator, - cx: &mut App, - ) -> LayoutId { - self.invalidator.debug_assert_prepaint(); - - cx.layout_id_buffer.clear(); - cx.layout_id_buffer.extend(children); - let rem_size = self.rem_size(); - let scale_factor = self.scale_factor(); - - self.layout_engine.as_mut().unwrap().request_layout( - style, - rem_size, - scale_factor, - &cx.layout_id_buffer, - ) - } - - /// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children, - /// this variant takes a function that is invoked during layout so you can use arbitrary logic to - /// determine the element's size. One place this is used internally is when measuring text. - /// - /// The given closure is invoked at layout time with the known dimensions and available space and - /// returns a `Size`. - /// - /// This method should only be called as part of the request_layout or prepaint phase of element drawing. - pub fn request_measured_layout(&mut self, style: Style, measure: F) -> LayoutId - where - F: Fn(Size>, Size, &mut Window, &mut App) -> Size - + 'static, - { - self.invalidator.debug_assert_prepaint(); - - let rem_size = self.rem_size(); - let scale_factor = self.scale_factor(); - self.layout_engine - .as_mut() - .unwrap() - .request_measured_layout(style, rem_size, scale_factor, measure) - } - - /// Compute the layout for the given id within the given available space. - /// This method is called for its side effect, typically by the framework prior to painting. - /// After calling it, you can request the bounds of the given layout node id or any descendant. - /// - /// This method should only be called as part of the prepaint phase of element drawing. - pub fn compute_layout( - &mut self, - layout_id: LayoutId, - available_space: Size, - cx: &mut App, - ) { - self.invalidator.debug_assert_prepaint(); - - let mut layout_engine = self.layout_engine.take().unwrap(); - layout_engine.compute_layout(layout_id, available_space, self, cx); - self.layout_engine = Some(layout_engine); - } - - /// Obtain the bounds computed for the given LayoutId relative to the window. This method will usually be invoked by - /// GPUI itself automatically in order to pass your element its `Bounds` automatically. - /// - /// This method should only be called as part of element drawing. - pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds { - self.invalidator.debug_assert_prepaint(); - - let scale_factor = self.scale_factor(); - let mut bounds = self - .layout_engine - .as_mut() - .unwrap() - .layout_bounds(layout_id, scale_factor) - .map(Into::into); - let snapped_offset = self.pixel_snap_point(self.element_offset()); - bounds.origin += snapped_offset; - bounds - } - - /// This method should be called during `prepaint`. You can use - /// the returned [Hitbox] during `paint` or in an event handler - /// to determine whether the inserted hitbox was the topmost. - /// - /// This method should only be called as part of the prepaint phase of element drawing. - pub fn insert_hitbox(&mut self, bounds: Bounds, behavior: HitboxBehavior) -> Hitbox { - self.invalidator.debug_assert_prepaint(); - - let content_mask = self.content_mask(); - let mut id = self.next_hitbox_id; - self.next_hitbox_id = self.next_hitbox_id.next(); - let hitbox = Hitbox { - id, - bounds, - content_mask, - behavior, - }; - self.next_frame.hitboxes.push(hitbox.clone()); - hitbox - } - - /// Set a hitbox which will act as a control area of the platform window. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn insert_window_control_hitbox(&mut self, area: WindowControlArea, hitbox: Hitbox) { - self.invalidator.debug_assert_paint(); - self.next_frame.window_control_hitboxes.push((area, hitbox)); - } - - /// Sets the key context for the current element. This context will be used to translate - /// keybindings into actions. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn set_key_context(&mut self, context: KeyContext) { - self.invalidator.debug_assert_paint(); - self.next_frame.dispatch_tree.set_key_context(context); - } - - /// Sets the focus handle for the current element. This handle will be used to manage focus state - /// and keyboard event dispatch for the element. - /// - /// This method should only be called as part of the prepaint phase of element drawing. - pub fn set_focus_handle(&mut self, focus_handle: &FocusHandle, _: &App) { - self.invalidator.debug_assert_prepaint(); - if focus_handle.is_focused(self) { - self.next_frame.focus = Some(focus_handle.id); - } - self.next_frame.dispatch_tree.set_focus_id(focus_handle.id); - } - - /// Sets the view id for the current element, which will be used to manage view caching. - /// - /// This method should only be called as part of element prepaint. We plan on removing this - /// method eventually when we solve some issues that require us to construct editor elements - /// directly instead of always using editors via views. - pub fn set_view_id(&mut self, view_id: EntityId) { - self.invalidator.debug_assert_prepaint(); - self.next_frame.dispatch_tree.set_view_id(view_id); - } - - /// Get the entity ID for the currently rendering view - pub fn current_view(&self) -> EntityId { - self.invalidator.debug_assert_paint_or_prepaint(); - self.rendered_entity_stack.last().copied().unwrap() - } - - #[inline] - pub(crate) fn with_rendered_view( - &mut self, - id: EntityId, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.rendered_entity_stack.push(id); - let result = f(self); - self.rendered_entity_stack.pop(); - result - } - - /// Executes the provided function with the specified image cache. - pub fn with_image_cache(&mut self, image_cache: Option, f: F) -> R - where - F: FnOnce(&mut Self) -> R, - { - if let Some(image_cache) = image_cache { - self.image_cache_stack.push(image_cache); - let result = f(self); - self.image_cache_stack.pop(); - result - } else { - f(self) - } - } - - /// Sets an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the - /// platform to receive textual input with proper integration with concerns such - /// as IME interactions. This handler will be active for the upcoming frame until the following frame is - /// rendered. - /// - /// This method should only be called as part of the paint phase of element drawing. - /// - /// [element_input_handler]: crate::ElementInputHandler - pub fn handle_input( - &mut self, - focus_handle: &FocusHandle, - input_handler: impl InputHandler, - cx: &App, - ) { - self.invalidator.debug_assert_paint(); - - if focus_handle.is_focused(self) { - let cx = self.to_async(cx); - self.next_frame - .input_handlers - .push(Some(PlatformInputHandler::new(cx, Box::new(input_handler)))); - } - } - - /// Forwards the focused input handler's [`TextInputConfiguration`] to the - /// platform window when it differs from the last forwarded value. With no - /// input handler the default configuration applies, so a field's - /// preferences don't outlive its focus. - fn apply_text_input_configuration(&mut self, cx: &mut App) { - let configuration = match self.platform_window.take_input_handler() { - Some(mut input_handler) => { - let configuration = input_handler.text_input_configuration(self, cx); - self.platform_window.set_input_handler(input_handler); - configuration - } - None => TextInputConfiguration::default(), - }; - if self.last_text_input_configuration.as_ref() != Some(&configuration) { - self.platform_window - .set_text_input_configuration(configuration.clone()); - self.last_text_input_configuration = Some(configuration); - } - } - - /// Register a mouse event listener on the window for the next frame. The type of event - /// is determined by the first parameter of the given listener. When the next frame is rendered - /// the listener will be cleared. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn on_mouse_event( - &mut self, - mut listener: impl FnMut(&Event, DispatchPhase, &mut Window, &mut App) + 'static, - ) { - self.invalidator.debug_assert_paint(); - - self.next_frame.mouse_listeners.push(Some(Box::new( - move |event: &dyn Any, phase: DispatchPhase, window: &mut Window, cx: &mut App| { - if let Some(event) = event.downcast_ref() { - listener(event, phase, window, cx) - } - }, - ))); - } - - /// Register a key event listener on this node for the next frame. The type of event - /// is determined by the first parameter of the given listener. When the next frame is rendered - /// the listener will be cleared. - /// - /// This is a fairly low-level method, so prefer using event handlers on elements unless you have - /// a specific need to register a listener yourself. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn on_key_event( - &mut self, - listener: impl Fn(&Event, DispatchPhase, &mut Window, &mut App) + 'static, - ) { - self.invalidator.debug_assert_paint(); - - self.next_frame.dispatch_tree.on_key_event(Rc::new( - move |event: &dyn Any, phase, window: &mut Window, cx: &mut App| { - if let Some(event) = event.downcast_ref::() { - listener(event, phase, window, cx) - } - }, - )); - } - - /// Register a modifiers changed event listener on the window for the next frame. - /// - /// This is a fairly low-level method, so prefer using event handlers on elements unless you have - /// a specific need to register a global listener. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn on_modifiers_changed( - &mut self, - listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static, - ) { - self.invalidator.debug_assert_paint(); - - self.next_frame.dispatch_tree.on_modifiers_changed(Rc::new( - move |event: &ModifiersChangedEvent, window: &mut Window, cx: &mut App| { - listener(event, window, cx) - }, - )); - } - - /// Register a listener to be called when the given focus handle or one of its descendants receives focus. - /// This does not fire if the given focus handle - or one of its descendants - was previously focused. - /// Returns a subscription and persists until the subscription is dropped. - pub fn on_focus_in( - &mut self, - handle: &FocusHandle, - cx: &mut App, - mut listener: impl FnMut(&mut Window, &mut App) + 'static, - ) -> Subscription { - let focus_id = handle.id; - let (subscription, activate) = - self.new_focus_listener(Box::new(move |event, window, cx| { - if event.is_focus_in(focus_id) { - listener(window, cx); - } - true - })); - cx.defer(move |_| activate()); - subscription - } - - /// Register a listener to be called when the given focus handle or one of its descendants loses focus. - /// Returns a subscription and persists until the subscription is dropped. - pub fn on_focus_out( - &mut self, - handle: &FocusHandle, - cx: &mut App, - mut listener: impl FnMut(FocusOutEvent, &mut Window, &mut App) + 'static, - ) -> Subscription { - let focus_id = handle.id; - let (subscription, activate) = - self.new_focus_listener(Box::new(move |event, window, cx| { - if let Some(blurred_id) = event.previous_focus_path.last().copied() - && event.is_focus_out(focus_id) - { - let event = FocusOutEvent { - blurred: WeakFocusHandle { - id: blurred_id, - handles: Arc::downgrade(&cx.focus_handles), - }, - }; - listener(event, window, cx) - } - true - })); - cx.defer(move |_| activate()); - subscription - } - - fn reset_cursor_style(&self, cx: &mut App) { - // Set the cursor only if we're the active window. - if self.is_window_hovered() { - let style = self - .rendered_frame - .cursor_style(self) - .unwrap_or(CursorStyle::Arrow); - cx.platform.set_cursor_style(style); - } - } - - /// Dispatch a given keystroke as though the user had typed it. - /// You can create a keystroke with Keystroke::parse(""). - pub fn dispatch_keystroke(&mut self, keystroke: Keystroke, cx: &mut App) -> bool { - let keystroke = keystroke.with_simulated_ime(); - let result = self.dispatch_event( - PlatformInput::KeyDown(KeyDownEvent { - keystroke: keystroke.clone(), - is_held: false, - prefer_character_input: false, - }), - cx, - ); - if !result.propagate { - return true; - } - - if let Some(input) = keystroke.key_char - && let Some(mut input_handler) = self.platform_window.take_input_handler() - { - input_handler.dispatch_input(&input, self, cx); - self.platform_window.set_input_handler(input_handler); - return true; - } - - false - } - - /// Return a key binding string for an action, to display in the UI. Uses the highest precedence - /// binding for the action (last binding added to the keymap). - pub fn keystroke_text_for(&self, action: &dyn Action) -> String { - self.highest_precedence_binding_for_action(action) - .map(|binding| { - binding - .keystrokes() - .iter() - .map(ToString::to_string) - .collect::>() - .join(" ") - }) - .unwrap_or_else(|| action.name().to_string()) - } - - /// Dispatch a mouse, keyboard, or touch event on the window. - #[profiling::function] - pub fn dispatch_event(&mut self, event: PlatformInput, cx: &mut App) -> DispatchEventResult { - #[cfg(feature = "profiler")] - self.window_profiler.begin_input(event.kind_name()); - let update_count_before = self.invalidator.update_count(); - // Track input modality for focus-visible styling and hover suppression. - // Hover is suppressed during keyboard modality so that keyboard navigation - // doesn't show hover highlights on the item under the mouse cursor. - let old_modality = self.last_input_modality; - self.last_input_modality = match &event { - PlatformInput::KeyDown(_) => InputModality::Keyboard, - PlatformInput::MouseMove(_) | PlatformInput::MouseDown(_) => InputModality::Mouse, - PlatformInput::Touch(_) => InputModality::Touch, - _ => self.last_input_modality, - }; - if self.last_input_modality != old_modality { - self.refresh(); - } - - // Handlers may set this to false by calling `stop_propagation`. - cx.propagate_event = true; - // Handlers may set this to true by calling `prevent_default`. - self.default_prevented = false; - - let event = match event { - // Track the mouse position with our own state, since accessing the platform - // API for the mouse position can only occur on the main thread. - PlatformInput::MouseMove(mouse_move) => { - self.mouse_position = mouse_move.position; - self.modifiers = mouse_move.modifiers; - PlatformInput::MouseMove(mouse_move) - } - PlatformInput::MouseDown(mouse_down) => { - self.mouse_position = mouse_down.position; - self.modifiers = mouse_down.modifiers; - PlatformInput::MouseDown(mouse_down) - } - PlatformInput::MouseUp(mouse_up) => { - self.mouse_position = mouse_up.position; - self.modifiers = mouse_up.modifiers; - PlatformInput::MouseUp(mouse_up) - } - PlatformInput::MousePressure(mouse_pressure) => { - PlatformInput::MousePressure(mouse_pressure) - } - PlatformInput::MouseExited(mouse_exited) => { - self.modifiers = mouse_exited.modifiers; - PlatformInput::MouseExited(mouse_exited) - } - PlatformInput::ModifiersChanged(modifiers_changed) => { - self.modifiers = modifiers_changed.modifiers; - self.capslock = modifiers_changed.capslock; - PlatformInput::ModifiersChanged(modifiers_changed) - } - PlatformInput::ScrollWheel(scroll_wheel) => { - self.mouse_position = scroll_wheel.position; - self.modifiers = scroll_wheel.modifiers; - PlatformInput::ScrollWheel(scroll_wheel) - } - PlatformInput::Pinch(pinch) => { - self.mouse_position = pinch.position; - self.modifiers = pinch.modifiers; - PlatformInput::Pinch(pinch) - } - // Translate dragging and dropping of external files from the operating system - // to internal drag and drop events. - PlatformInput::FileDrop(file_drop) => match file_drop { - FileDropEvent::Entered { position, paths } => { - self.mouse_position = position; - let source_window = self.handle.window_id(); - if !cx.restore_platform_drag(source_window) && cx.active_drag.is_none() { - cx.active_drag = Some(AnyDrag { - value: Arc::new(paths.clone()), - view: cx.new(|_| paths).into(), - cursor_offset: position, - cursor_style: None, - external_payload_source: None, - }); - } - PlatformInput::MouseMove(MouseMoveEvent { - position, - pressed_button: Some(MouseButton::Left), - modifiers: Modifiers::default(), - }) - } - FileDropEvent::Pending { position } => { - self.mouse_position = position; - PlatformInput::MouseMove(MouseMoveEvent { - position, - pressed_button: Some(MouseButton::Left), - modifiers: Modifiers::default(), - }) - } - FileDropEvent::Submit { position } => { - cx.activate(true); - self.mouse_position = position; - PlatformInput::MouseUp(MouseUpEvent { - button: MouseButton::Left, - position, - modifiers: Modifiers::default(), - click_count: 1, - }) - } - FileDropEvent::Exited => { - if !cx.hand_restored_drag_to_platform(self.handle.window_id()) { - cx.active_drag.take(); - } - self.refresh(); - PlatformInput::FileDrop(FileDropEvent::Exited) - } - FileDropEvent::Ended => { - cx.end_platform_drag(self.handle.window_id()); - self.refresh(); - PlatformInput::FileDrop(FileDropEvent::Ended) - } - }, - PlatformInput::Touch(touch) => PlatformInput::Touch(touch), - PlatformInput::LongPress(long_press) => { - self.mouse_position = if long_press.phase == crate::TouchPhase::Started { - long_press.start_position - } else { - long_press.position - }; - if long_press.phase == crate::TouchPhase::Started { - self.long_press_capture = None; - } - PlatformInput::LongPress(long_press) - } - PlatformInput::TouchDrag(touch_drag) => { - self.mouse_position = touch_drag.start_position; - PlatformInput::TouchDrag(touch_drag) - } - PlatformInput::KeyDown(_) | PlatformInput::KeyUp(_) => event, - }; - - if let Some(any_mouse_event) = event.mouse_event() { - self.dispatch_mouse_event(any_mouse_event, cx); - } else if let Some(any_key_event) = event.keyboard_event() { - self.dispatch_key_event(any_key_event, cx); - } else if let Some(touch_event) = event.touch_event() { - self.dispatch_touch_event(touch_event, cx); - } - if let PlatformInput::LongPress(long_press) = &event { - match long_press.phase { - crate::TouchPhase::Started if !self.default_prevented => { - self.long_press_capture = None; - } - crate::TouchPhase::Ended | crate::TouchPhase::Cancelled => { - self.long_press_capture = None; - } - crate::TouchPhase::Started | crate::TouchPhase::Moved => {} - } - } - - // Must run after the move is dispatched: the platform owns the gesture afterwards, so this - // is the last chance for drag listeners to see the pointer leave and reset their state. - self.promote_external_drag_to_platform(&event, cx); - - let caused_invalidation = self.invalidator.update_count() > update_count_before; - if caused_invalidation { - self.input_rate_tracker.borrow_mut().record_input(); - } - #[cfg(feature = "profiler")] - self.window_profiler.end_input(caused_invalidation); - - DispatchEventResult { - propagate: cx.propagate_event, - default_prevented: self.default_prevented, - } - } - - fn promote_external_drag_to_platform(&mut self, event: &PlatformInput, cx: &mut App) { - let PlatformInput::MouseMove(mouse_move) = event else { - return; - }; - if mouse_move.pressed_button != Some(MouseButton::Left) { - return; - } - if Bounds::new(Point::default(), self.viewport_size).contains(&mouse_move.position) { - return; - } - if !self.platform_window.can_start_external_drag() { - return; - } - let Some(payload_source) = cx - .active_drag - .as_mut() - .and_then(|drag| drag.external_payload_source.take()) - else { - return; - }; - let Some(payload) = payload_source(self, cx) else { - return; - }; - if self.platform_window.start_external_drag(&payload) - && cx.hand_active_drag_to_platform(self.handle.window_id()) - { - self.refresh(); - } - } - - /// Whether recognized touch pans may use the platform's predicted touch - /// positions ([`TouchEvent::predicted_position`]) to compensate for input - /// latency. Defaults to true. - pub fn touch_prediction_enabled(&self) -> bool { - self.touch_prediction_enabled - } - - /// Sets whether recognized touch pans may use the platform's predicted - /// touch positions. Disabling drops [`TouchEvent::predicted_position`] - /// before gesture recognition, so pans track only raw touch positions. - pub fn set_touch_prediction_enabled(&mut self, enabled: bool) { - self.touch_prediction_enabled = enabled; - } - - /// Runs the portable gesture recognizer over a raw touch event and - /// dispatches whatever it resolves (scroll steps, synthesized taps) - /// through the ordinary mouse-event path. - fn dispatch_touch_event(&mut self, event: &TouchEvent, cx: &mut App) { - let mut event = event.clone(); - if !self.touch_prediction_enabled { - event.predicted_position = None; - } - let recognized_gestures = self.touch_gestures.handle_event(&event); - if event.phase == crate::TouchPhase::Started - && let Some(touch_drag) = self.touch_gestures.offer_touch_drag(event.id) - { - self.dispatch_recognized_touch_gesture(touch_drag, cx); - } - if event.phase == crate::TouchPhase::Started - && self.touch_gestures.pending_long_press().is_some() - { - self.long_press_capture = None; - } - let mut tapped = false; - for gesture in recognized_gestures { - tapped |= matches!(gesture, RecognizedTouchGesture::Tap { .. }); - self.dispatch_recognized_touch_gesture(gesture, cx); - } - if event.phase == crate::TouchPhase::Started { - self.schedule_long_press_timer(cx); - } else if self.touch_gestures.pending_long_press().is_none() { - self.long_press_timer.take(); - } - // The platform's touch-release handler may inspect the input handler - // as soon as this dispatch returns (the web platform decides virtual - // keyboard visibility there, inside the user gesture). Input handlers - // are registered during draw, so draw now to make them reflect any - // focus change the tap just caused. - if tapped && self.invalidator.is_dirty() { - self.draw(cx).clear(cx); - } - if self.touch_gestures.has_momentum() { - self.schedule_touch_momentum_tick(); - } - } - - fn dispatch_recognized_touch_gesture(&mut self, gesture: RecognizedTouchGesture, cx: &mut App) { - match gesture { - RecognizedTouchGesture::Scroll(scroll_wheel) => { - self.mouse_position = scroll_wheel.position; - cx.propagate_event = true; - self.dispatch_mouse_event(&scroll_wheel, cx); - } - RecognizedTouchGesture::Tap { down, up } => { - self.mouse_position = up.position; - cx.propagate_event = true; - self.dispatch_mouse_event(&down, cx); - cx.propagate_event = true; - self.dispatch_mouse_event(&up, cx); - } - RecognizedTouchGesture::TouchDrag(touch_drag) => { - self.mouse_position = touch_drag.start_position; - cx.propagate_event = true; - self.default_prevented = false; - let started = touch_drag.phase == crate::TouchPhase::Started; - self.dispatch_mouse_event(&touch_drag, cx); - if started { - self.touch_gestures - .resolve_touch_drag(self.default_prevented); - } - } - RecognizedTouchGesture::LongPress(long_press) => { - self.mouse_position = if long_press.phase == crate::TouchPhase::Started { - long_press.start_position - } else { - long_press.position - }; - cx.propagate_event = true; - self.default_prevented = false; - let started = long_press.phase == crate::TouchPhase::Started; - let ended = matches!( - long_press.phase, - crate::TouchPhase::Ended | crate::TouchPhase::Cancelled - ); - self.dispatch_mouse_event(&long_press, cx); - if started { - let claimed = self.default_prevented; - self.touch_gestures.resolve_long_press(claimed); - if !claimed { - self.long_press_capture = None; - } - } - if ended { - self.long_press_capture = None; - } - } - } - } - - fn schedule_long_press_timer(&mut self, cx: &mut App) { - self.long_press_timer.take(); - let Some((touch_id, duration)) = self.touch_gestures.pending_long_press() else { - return; - }; - self.long_press_timer = Some(self.spawn(cx, async move |cx| { - cx.background_executor.timer(duration).await; - cx.update(move |window, cx| { - window.long_press_timer.take(); - if let Some(gesture) = window.touch_gestures.offer_long_press(touch_id) { - window.dispatch_recognized_touch_gesture(gesture, cx); - } - }) - .log_err(); - })); - } - - fn schedule_touch_momentum_tick(&mut self) { - self.on_next_frame(|window, cx| { - if let Some(gesture) = window.touch_gestures.tick_momentum() { - window.dispatch_recognized_touch_gesture(gesture, cx); - } - if window.touch_gestures.has_momentum() { - window.schedule_touch_momentum_tick(); - } - }); - } - - fn dispatch_mouse_event(&mut self, event: &dyn Any, cx: &mut App) { - let hit_test = self.rendered_frame.hit_test(self.mouse_position()); - if hit_test != self.mouse_hit_test { - self.mouse_hit_test = hit_test; - self.reset_cursor_style(cx); - } - - #[cfg(any(feature = "inspector", debug_assertions))] - if self.is_inspector_picking(cx) { - self.handle_inspector_mouse_event(event, cx); - // When inspector is picking, all other mouse handling is skipped. - return; - } - - let mut mouse_listeners = mem::take(&mut self.rendered_frame.mouse_listeners); - - // Capture phase, events bubble from back to front. Handlers for this phase are used for - // special purposes, such as detecting events outside of a given Bounds. - for listener in &mut mouse_listeners { - let listener = listener.as_mut().unwrap(); - listener(event, DispatchPhase::Capture, self, cx); - if !cx.propagate_event { - break; - } - } - - // Bubble phase, where most normal handlers do their work. - if cx.propagate_event { - for listener in mouse_listeners.iter_mut().rev() { - let listener = listener.as_mut().unwrap(); - listener(event, DispatchPhase::Bubble, self, cx); - if !cx.propagate_event { - break; - } - } - } - - self.rendered_frame.mouse_listeners = mouse_listeners; - - if cx.has_active_drag() { - if event.is::() { - // If this was a mouse move event, redraw the window so that the - // active drag can follow the mouse cursor. - self.refresh(); - } else if event.is::() { - // If this was a mouse up event, cancel the active drag and redraw - // the window. - cx.active_drag = None; - self.refresh(); - } - } - - // Auto-release pointer capture on mouse up - if event.is::() && self.captured_hitbox.is_some() { - self.captured_hitbox = None; - } - } - - fn dispatch_key_event(&mut self, event: &dyn Any, cx: &mut App) { - if self.invalidator.is_dirty() { - self.draw(cx).clear(cx); - } - - let node_id = self.focus_node_id_in_rendered_frame(self.focus); - let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id); - - let mut keystroke: Option = None; - - if let Some(event) = event.downcast_ref::() { - if event.modifiers.number_of_modifiers() == 0 - && self.pending_modifier.modifiers.number_of_modifiers() == 1 - && !self.pending_modifier.saw_other_input - { - let key = match self.pending_modifier.modifiers { - modifiers if modifiers.shift => Some("shift"), - modifiers if modifiers.control => Some("control"), - modifiers if modifiers.alt => Some("alt"), - modifiers if modifiers.platform => Some("platform"), - modifiers if modifiers.function => Some("function"), - _ => None, - }; - if let Some(key) = key { - keystroke = Some(Keystroke { - key: key.to_string(), - key_char: None, - modifiers: Modifiers::default(), - }); - } - } - - if self.pending_modifier.modifiers.number_of_modifiers() == 0 - && event.modifiers.number_of_modifiers() == 1 - { - self.pending_modifier.saw_other_input = false - } else if event.modifiers.number_of_modifiers() > 1 { - self.pending_modifier.saw_other_input = true - } - self.pending_modifier.modifiers = event.modifiers - } else if let Some(key_down_event) = event.downcast_ref::() { - self.pending_modifier.saw_other_input = true; - keystroke = Some(key_down_event.keystroke.clone()); - if key_down_event.keystroke.key_char.is_some() - && matches!( - cx.cursor_hide_mode, - CursorHideMode::OnTyping | CursorHideMode::OnTypingAndAction - ) - { - cx.platform.hide_cursor_until_mouse_moves(); - } - } - - let Some(keystroke) = keystroke else { - self.finish_dispatch_key_event(event, dispatch_path, self.context_stack(), cx); - return; - }; - - cx.propagate_event = true; - self.dispatch_keystroke_interceptors(event, self.context_stack(), cx); - if !cx.propagate_event { - self.finish_dispatch_key_event(event, dispatch_path, self.context_stack(), cx); - return; - } - - let mut currently_pending = self.pending_input.take().unwrap_or_default(); - if currently_pending.focus.is_some() && currently_pending.focus != self.focus { - currently_pending = PendingInput::default(); - } - - let match_result = self.rendered_frame.dispatch_tree.dispatch_key( - currently_pending.keystrokes, - keystroke, - &dispatch_path, - ); - - if !match_result.to_replay.is_empty() { - self.replay_pending_input(match_result.to_replay, cx); - cx.propagate_event = true; - } - - if !match_result.pending.is_empty() { - let previous_timeout = currently_pending.timeout.take(); - currently_pending.keystrokes = match_result.pending; - currently_pending.focus = self.focus; - - let text_input_requires_timeout = event - .downcast_ref::() - .filter(|key_down| key_down.keystroke.key_char.is_some()) - .and_then(|_| self.platform_window.take_input_handler()) - .map_or(false, |mut input_handler| { - let accepts = input_handler.accepts_text_input(self, cx); - self.platform_window.set_input_handler(input_handler); - accepts - }); - - let needs_timeout = previous_timeout.is_some() - || match_result.pending_has_binding - || text_input_requires_timeout; - currently_pending.timeout = if needs_timeout { - match previous_timeout { - Some(mut timeout) if timeout.is_paused() => { - timeout.reset_duration(PENDING_INPUT_TIMEOUT); - Some(timeout) - } - previous_timeout => { - drop(previous_timeout); - Some(self.new_pending_input_timeout(PENDING_INPUT_TIMEOUT, cx)) - } - } - } else { - None - }; - self.pending_input = Some(currently_pending); - self.pending_input_changed(cx); - cx.propagate_event = false; - return; - } - - let skip_bindings = event - .downcast_ref::() - .filter(|key_down_event| key_down_event.prefer_character_input) - .map(|_| { - self.platform_window - .take_input_handler() - .map_or(false, |mut input_handler| { - let accepts = input_handler.accepts_text_input(self, cx); - self.platform_window.set_input_handler(input_handler); - // If modifiers are not excessive (e.g. AltGr), and the input handler is accepting text input, - // we prefer the text input over bindings. - accepts - }) - }) - .unwrap_or(false); - - if !skip_bindings { - for binding in match_result.bindings { - self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx); - if !cx.propagate_event { - self.dispatch_keystroke_observers( - event, - Some(binding.action), - match_result.context_stack, - cx, - ); - self.pending_input_changed(cx); - return; - } - } - } - - self.finish_dispatch_key_event(event, dispatch_path, match_result.context_stack, cx); - self.pending_input_changed(cx); - } - - fn new_pending_input_timeout(&self, duration: Duration, cx: &App) -> PendingInputTimeout { - let (started_at, task) = self.start_pending_input_timeout(duration, cx); - PendingInputTimeout { - duration, - remaining: duration, - state: PendingInputTimeoutState::Running { started_at, task }, - } - } - - fn start_pending_input_timeout(&self, remaining: Duration, cx: &App) -> (Instant, Task<()>) { - let started_at = cx.background_executor().now(); - let task = self.spawn(cx, async move |cx| { - cx.background_executor.timer(remaining).await; - cx.update(move |window, cx| { - let Some(currently_pending) = window - .pending_input - .take() - .filter(|pending| pending.focus == window.focus) - else { - return; - }; - - let node_id = window.focus_node_id_in_rendered_frame(window.focus); - let dispatch_path = window.rendered_frame.dispatch_tree.dispatch_path(node_id); - - let to_replay = window - .rendered_frame - .dispatch_tree - .flush_dispatch(currently_pending.keystrokes, &dispatch_path); - - window.pending_input_changed(cx); - window.replay_pending_input(to_replay, cx) - }) - .log_err(); - }); - (started_at, task) - } - - fn finish_dispatch_key_event( - &mut self, - event: &dyn Any, - dispatch_path: SmallVec<[DispatchNodeId; 32]>, - context_stack: Vec, - cx: &mut App, - ) { - self.dispatch_key_down_up_event(event, &dispatch_path, cx); - if !cx.propagate_event { - return; - } - - self.dispatch_modifiers_changed_event(event, &dispatch_path, cx); - if !cx.propagate_event { - return; - } - - self.dispatch_keystroke_observers(event, None, context_stack, cx); - } - - pub(crate) fn pending_input_changed(&mut self, cx: &mut App) { - self.pending_input_observers - .clone() - .retain(&(), |callback| callback(self, cx)); - } - - fn defer_pending_input_changed(&self, cx: &mut App) { - // Avoid re-entrant entity updates by deferring observer notifications to the end of the - // current effect cycle, and only for this window. - let window_handle = self.handle; - cx.defer(move |cx| { - window_handle - .update(cx, |_, window, cx| { - window.pending_input_changed(cx); - }) - .ok(); - }); - } - - fn dispatch_key_down_up_event( - &mut self, - event: &dyn Any, - dispatch_path: &SmallVec<[DispatchNodeId; 32]>, - cx: &mut App, - ) { - // Capture phase - for node_id in dispatch_path { - let node = self.rendered_frame.dispatch_tree.node(*node_id); - - for key_listener in node.key_listeners.clone() { - key_listener(event, DispatchPhase::Capture, self, cx); - if !cx.propagate_event { - return; - } - } - } - - // Bubble phase - for node_id in dispatch_path.iter().rev() { - // Handle low level key events - let node = self.rendered_frame.dispatch_tree.node(*node_id); - for key_listener in node.key_listeners.clone() { - key_listener(event, DispatchPhase::Bubble, self, cx); - if !cx.propagate_event { - return; - } - } - } - } - - fn dispatch_modifiers_changed_event( - &mut self, - event: &dyn Any, - dispatch_path: &SmallVec<[DispatchNodeId; 32]>, - cx: &mut App, - ) { - let Some(event) = event.downcast_ref::() else { - return; - }; - for node_id in dispatch_path.iter().rev() { - let node = self.rendered_frame.dispatch_tree.node(*node_id); - for listener in node.modifiers_changed_listeners.clone() { - listener(event, self, cx); - if !cx.propagate_event { - return; - } - } - } - } - - /// Determine whether a potential multi-stroke key binding is in progress on this window. - pub fn has_pending_keystrokes(&self) -> bool { - self.pending_input().is_some() - } - - #[cfg(test)] - pub(crate) fn pending_input_is_none(&self) -> bool { - self.pending_input.is_none() - } - - pub(crate) fn clear_pending_keystrokes(&mut self, cx: &mut App) { - if self.pending_input.take().is_some() { - self.defer_pending_input_changed(cx); - } - } - - /// Returns pending input that can still complete a multi-stroke key binding. Input left over - /// from a previous focus can never complete one. - pub fn pending_input(&self) -> Option> { - self.pending_input - .as_ref() - .filter(|pending_input| pending_input.focus == self.focus) - .map(|pending_input| PendingInputStatus { - keystrokes: pending_input.keystrokes.as_slice(), - timeout: pending_input - .timeout - .as_ref() - .map(PendingInputTimeout::status), - }) - } - - /// Pauses or resumes the current pending input timeout on behalf of `owner`. - /// - /// A paused timeout resumes automatically if `owner` is released. Returns whether the timeout - /// state changed. A timeout paused by one owner cannot be resumed by another. - pub fn set_pending_input_timeout_paused( - &mut self, - owner: &Entity, - paused: bool, - cx: &mut App, - ) -> bool { - let owner_id = owner.entity_id(); - if !paused { - return self.resume_pending_input_timeout(owner_id, cx); - } - - let timeout = self - .pending_input - .as_ref() - .filter(|pending_input| pending_input.focus == self.focus) - .and_then(|pending_input| pending_input.timeout.as_ref()); - let Some(timeout) = timeout else { - return false; - }; - if timeout.is_paused() { - return false; - } - - let release_subscription = self.observe_release(owner, cx, move |_, window, cx| { - window.resume_pending_input_timeout(owner_id, cx); - }); - let now = cx.background_executor().now(); - let changed = self - .pending_input - .as_mut() - .filter(|pending_input| pending_input.focus == self.focus) - .and_then(|pending_input| pending_input.timeout.as_mut()) - .is_some_and(|timeout| { - timeout.pause( - PendingInputTimeoutPause { - owner_id, - _release_subscription: release_subscription, - }, - now, - ) - }); - - if changed { - self.defer_pending_input_changed(cx); - } - changed - } - - fn resume_pending_input_timeout(&mut self, owner_id: EntityId, cx: &mut App) -> bool { - let Some(remaining) = self - .pending_input - .as_ref() - .and_then(|pending_input| pending_input.timeout.as_ref()) - .filter(|timeout| timeout.pause_owner_id() == Some(owner_id)) - .map(|timeout| timeout.remaining) - else { - return false; - }; - - let (started_at, task) = self.start_pending_input_timeout(remaining, cx); - let changed = self - .pending_input - .as_mut() - .and_then(|pending_input| pending_input.timeout.as_mut()) - .is_some_and(|timeout| timeout.resume(owner_id, started_at, task)); - - if changed { - self.defer_pending_input_changed(cx); - } - changed - } - - /// Returns the currently pending input keystrokes that might result in a multi-stroke key binding. - pub fn pending_input_keystrokes(&self) -> Option<&[Keystroke]> { - self.pending_input() - .map(|pending_input| pending_input.keystrokes()) - } - - fn replay_pending_input(&mut self, replays: SmallVec<[Replay; 1]>, cx: &mut App) { - let node_id = self.focus_node_id_in_rendered_frame(self.focus); - let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id); - - 'replay: for replay in replays { - let event = KeyDownEvent { - keystroke: replay.keystroke.clone(), - is_held: false, - prefer_character_input: true, - }; - - cx.propagate_event = true; - for binding in replay.bindings { - self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx); - if !cx.propagate_event { - self.dispatch_keystroke_observers( - &event, - Some(binding.action), - Vec::default(), - cx, - ); - continue 'replay; - } - } - - self.dispatch_key_down_up_event(&event, &dispatch_path, cx); - if !cx.propagate_event { - continue 'replay; - } - if let Some(input) = replay.keystroke.key_char.as_ref().cloned() - && let Some(mut input_handler) = self.platform_window.take_input_handler() - { - input_handler.dispatch_input(&input, self, cx); - self.platform_window.set_input_handler(input_handler) - } - } - } - - fn focus_node_id_in_rendered_frame(&self, focus_id: Option) -> DispatchNodeId { - focus_id - .and_then(|focus_id| { - self.rendered_frame - .dispatch_tree - .focusable_node_id(focus_id) - }) - .unwrap_or_else(|| self.rendered_frame.dispatch_tree.root_node_id()) - } - - fn dispatch_action_on_node( - &mut self, - node_id: DispatchNodeId, - action: &dyn Action, - cx: &mut App, - ) { - self.dispatch_action_on_node_inner(node_id, action, cx); - - if !cx.propagate_event - && cx.cursor_hide_mode == CursorHideMode::OnTypingAndAction - && self.last_input_was_keyboard() - { - cx.platform.hide_cursor_until_mouse_moves(); - } - } - - fn dispatch_action_on_node_inner( - &mut self, - node_id: DispatchNodeId, - action: &dyn Action, - cx: &mut App, - ) { - let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id); - - // Capture phase for global actions. - cx.propagate_event = true; - if let Some(mut global_listeners) = cx - .global_action_listeners - .remove(&action.as_any().type_id()) - { - for listener in &global_listeners { - #[cfg(feature = "profiler")] - self.window_profiler.begin_action_handler(action, cx); - listener(action.as_any(), DispatchPhase::Capture, cx); - #[cfg(feature = "profiler")] - self.window_profiler.end_action_handler(); - if !cx.propagate_event { - break; - } - } - - global_listeners.extend( - cx.global_action_listeners - .remove(&action.as_any().type_id()) - .unwrap_or_default(), - ); - - cx.global_action_listeners - .insert(action.as_any().type_id(), global_listeners); - } - - if !cx.propagate_event { - return; - } - - // Capture phase for window actions. - for node_id in &dispatch_path { - let node = self.rendered_frame.dispatch_tree.node(*node_id); - for DispatchActionListener { - action_type, - listener, - } in node.action_listeners.clone() - { - let any_action = action.as_any(); - if action_type == any_action.type_id() { - #[cfg(feature = "profiler")] - self.window_profiler.begin_action_handler(action, cx); - listener(any_action, DispatchPhase::Capture, self, cx); - #[cfg(feature = "profiler")] - self.window_profiler.end_action_handler(); - - if !cx.propagate_event { - return; - } - } - } - } - - // Bubble phase for window actions. - for node_id in dispatch_path.iter().rev() { - let node = self.rendered_frame.dispatch_tree.node(*node_id); - for DispatchActionListener { - action_type, - listener, - } in node.action_listeners.clone() - { - let any_action = action.as_any(); - if action_type == any_action.type_id() { - cx.propagate_event = false; // Actions stop propagation by default during the bubble phase - #[cfg(feature = "profiler")] - self.window_profiler.begin_action_handler(action, cx); - listener(any_action, DispatchPhase::Bubble, self, cx); - #[cfg(feature = "profiler")] - self.window_profiler.end_action_handler(); - - if !cx.propagate_event { - return; - } - } - } - } - - // Bubble phase for global actions. - if let Some(mut global_listeners) = cx - .global_action_listeners - .remove(&action.as_any().type_id()) - { - for listener in global_listeners.iter().rev() { - cx.propagate_event = false; // Actions stop propagation by default during the bubble phase - - #[cfg(feature = "profiler")] - self.window_profiler.begin_action_handler(action, cx); - listener(action.as_any(), DispatchPhase::Bubble, cx); - #[cfg(feature = "profiler")] - self.window_profiler.end_action_handler(); - if !cx.propagate_event { - break; - } - } - - global_listeners.extend( - cx.global_action_listeners - .remove(&action.as_any().type_id()) - .unwrap_or_default(), - ); - - cx.global_action_listeners - .insert(action.as_any().type_id(), global_listeners); - } - } - - /// Register the given handler to be invoked whenever the global of the given type - /// is updated. - pub fn observe_global( - &mut self, - cx: &mut App, - f: impl Fn(&mut Window, &mut App) + 'static, - ) -> Subscription { - let window_handle = self.handle; - let (subscription, activate) = cx.global_observers.insert( - TypeId::of::(), - Box::new(move |cx| { - window_handle - .update(cx, |_, window, cx| f(window, cx)) - .is_ok() - }), - ); - cx.defer(move |_| activate()); - subscription - } - - /// Focus the current window and bring it to the foreground at the platform level. - pub fn activate_window(&self) { - self.platform_window.activate(); - } - - /// Requests that the operating system draw attention to this window. - pub fn request_attention(&self) { - self.platform_window.request_attention(); - } - - /// Minimize the current window at the platform level. - pub fn minimize_window(&self) { - self.platform_window.minimize(); - } - - /// Toggle full screen status on the current window at the platform level. - pub fn toggle_fullscreen(&self) { - self.platform_window.toggle_fullscreen(); - } - - /// Toggle simple (borderless) fullscreen, where the window covers the entire - /// screen including the menu bar and, on notched displays, the area around the - /// notch. Unlike [`Window::toggle_fullscreen`], this does not move the window - /// into its own Mission Control space. Only has an effect on macOS. - pub fn toggle_simple_fullscreen(&self) { - self.platform_window.toggle_simple_fullscreen(); - } - - /// Updates the IME panel position suggestions for languages like japanese, chinese. - pub fn invalidate_character_coordinates(&self) { - self.on_next_frame(|window, cx| { - if let Some(mut input_handler) = window.platform_window.take_input_handler() { - if let Some(bounds) = input_handler.selected_bounds(window, cx) { - window.platform_window.update_ime_position(bounds); - } - window.platform_window.set_input_handler(input_handler); - } - }); - } - - /// Present a platform dialog. - /// The provided message will be presented, along with buttons for each answer. - /// When a button is clicked, the returned Receiver will receive the index of the clicked button. - pub fn prompt( - &mut self, - level: PromptLevel, - message: &str, - detail: Option<&str>, - answers: &[T], - cx: &mut App, - ) -> oneshot::Receiver - where - T: Clone + Into, - { - let prompt_builder = cx.prompt_builder.take(); - let Some(prompt_builder) = prompt_builder else { - unreachable!("Re-entrant window prompting is not supported by GPUI"); - }; - - let answers = answers - .iter() - .map(|answer| answer.clone().into()) - .collect::>(); - - let receiver = match &prompt_builder { - PromptBuilder::Default => self - .platform_window - .prompt(level, message, detail, &answers) - .unwrap_or_else(|| { - self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx) - }), - PromptBuilder::Custom(_) => { - self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx) - } - }; - - cx.prompt_builder = Some(prompt_builder); - - receiver - } - - fn build_custom_prompt( - &mut self, - prompt_builder: &PromptBuilder, - level: PromptLevel, - message: &str, - detail: Option<&str>, - answers: &[PromptButton], - cx: &mut App, - ) -> oneshot::Receiver { - let (sender, receiver) = oneshot::channel(); - let handle = PromptHandle::new(sender); - let handle = (prompt_builder)(level, message, detail, answers, handle, self, cx); - self.prompt = Some(handle); - receiver - } - - /// Returns whether a prompt rendered by GPUI is currently active in this window. - /// - /// This is only true for prompts rendered in the window (see - /// [`App::set_prompt_builder`]), not for platform-native prompt dialogs. - pub fn has_active_prompt(&self) -> bool { - self.prompt.is_some() - } - - /// Returns the current context stack. - pub fn context_stack(&self) -> Vec { - let node_id = self.focus_node_id_in_rendered_frame(self.focus); - let dispatch_tree = &self.rendered_frame.dispatch_tree; - dispatch_tree - .dispatch_path(node_id) - .iter() - .filter_map(move |&node_id| dispatch_tree.node(node_id).context.clone()) - .collect() - } - - /// Returns all available actions for the focused element. - pub fn available_actions(&self, cx: &App) -> Vec> { - let node_id = self.focus_node_id_in_rendered_frame(self.focus); - let mut actions = self.rendered_frame.dispatch_tree.available_actions(node_id); - for action_type in cx.global_action_listeners.keys() { - if let Err(ix) = actions.binary_search_by_key(action_type, |a| a.as_any().type_id()) { - let action = cx.actions.build_action_type(action_type).ok(); - if let Some(action) = action { - actions.insert(ix, action); - } - } - } - actions - } - - /// Returns key bindings that invoke an action on the currently focused element. Bindings are - /// returned in the order they were added. For display, the last binding should take precedence. - pub fn bindings_for_action(&self, action: &dyn Action) -> Vec { - self.rendered_frame - .dispatch_tree - .bindings_for_action(action, &self.rendered_frame.dispatch_tree.context_stack) - } - - /// Returns the highest precedence key binding that invokes an action on the currently focused - /// element. This is more efficient than getting the last result of `bindings_for_action`. - pub fn highest_precedence_binding_for_action(&self, action: &dyn Action) -> Option { - self.rendered_frame - .dispatch_tree - .highest_precedence_binding_for_action( - action, - &self.rendered_frame.dispatch_tree.context_stack, - ) - } - - /// Returns the key bindings for an action in a context. - pub fn bindings_for_action_in_context( - &self, - action: &dyn Action, - context: KeyContext, - ) -> Vec { - let dispatch_tree = &self.rendered_frame.dispatch_tree; - dispatch_tree.bindings_for_action(action, &[context]) - } - - /// Returns the highest precedence key binding for an action in a context. This is more - /// efficient than getting the last result of `bindings_for_action_in_context`. - pub fn highest_precedence_binding_for_action_in_context( - &self, - action: &dyn Action, - context: KeyContext, - ) -> Option { - let dispatch_tree = &self.rendered_frame.dispatch_tree; - dispatch_tree.highest_precedence_binding_for_action(action, &[context]) - } - - /// Returns any bindings that would invoke an action on the given focus handle if it were - /// focused. Bindings are returned in the order they were added. For display, the last binding - /// should take precedence. - pub fn bindings_for_action_in( - &self, - action: &dyn Action, - focus_handle: &FocusHandle, - ) -> Vec { - let dispatch_tree = &self.rendered_frame.dispatch_tree; - let Some(context_stack) = self.context_stack_for_focus_handle(focus_handle) else { - return vec![]; - }; - dispatch_tree.bindings_for_action(action, &context_stack) - } - - /// Returns the highest precedence key binding that would invoke an action on the given focus - /// handle if it were focused. This is more efficient than getting the last result of - /// `bindings_for_action_in`. - pub fn highest_precedence_binding_for_action_in( - &self, - action: &dyn Action, - focus_handle: &FocusHandle, - ) -> Option { - let dispatch_tree = &self.rendered_frame.dispatch_tree; - let context_stack = self.context_stack_for_focus_handle(focus_handle)?; - dispatch_tree.highest_precedence_binding_for_action(action, &context_stack) - } - - /// Find the bindings that can follow the current input sequence for the current context stack. - pub fn possible_bindings_for_input(&self, input: &[Keystroke]) -> Vec { - self.rendered_frame - .dispatch_tree - .possible_next_bindings_for_input(input, &self.context_stack()) - } - - fn context_stack_for_focus_handle( - &self, - focus_handle: &FocusHandle, - ) -> Option> { - let dispatch_tree = &self.rendered_frame.dispatch_tree; - let node_id = dispatch_tree.focusable_node_id(focus_handle.id)?; - let context_stack: Vec<_> = dispatch_tree - .dispatch_path(node_id) - .into_iter() - .filter_map(|node_id| dispatch_tree.node(node_id).context.clone()) - .collect(); - Some(context_stack) - } - - /// Returns a generic event listener that invokes the given listener with the view and context associated with the given view handle. - pub fn listener_for( - &self, - view: &Entity, - f: impl Fn(&mut T, &E, &mut Window, &mut Context) + 'static, - ) -> impl Fn(&E, &mut Window, &mut App) + 'static { - let view = view.downgrade(); - move |e: &E, window: &mut Window, cx: &mut App| { - view.update(cx, |view, cx| f(view, e, window, cx)).ok(); - } - } - - /// Returns a generic handler that invokes the given handler with the view and context associated with the given view handle. - pub fn handler_for) + 'static>( - &self, - entity: &Entity, - f: Callback, - ) -> impl Fn(&mut Window, &mut App) + 'static { - let entity = entity.downgrade(); - move |window: &mut Window, cx: &mut App| { - entity.update(cx, |entity, cx| f(entity, window, cx)).ok(); - } - } - - /// Register a callback that can interrupt the closing of the current window based the returned boolean. - /// If the callback returns false, the window won't be closed. - pub fn on_window_should_close( - &self, - cx: &App, - f: impl Fn(&mut Window, &mut App) -> bool + 'static, - ) { - let mut cx = self.to_async(cx); - self.platform_window.on_should_close(Box::new(move || { - cx.update(|window, cx| f(window, cx)).unwrap_or(true) - })) - } - - /// Register an action listener on this node for the next frame. The type of action - /// is determined by the first parameter of the given listener. When the next frame is rendered - /// the listener will be cleared. - /// - /// This is a fairly low-level method, so prefer using action handlers on elements unless you have - /// a specific need to register a listener yourself. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn on_action( - &mut self, - action_type: TypeId, - listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static, - ) { - self.invalidator.debug_assert_paint(); - - self.next_frame - .dispatch_tree - .on_action(action_type, Rc::new(listener)); - } - - /// Register a capturing action listener on this node for the next frame if the condition is true. - /// The type of action is determined by the first parameter of the given listener. When the next - /// frame is rendered the listener will be cleared. - /// - /// This is a fairly low-level method, so prefer using action handlers on elements unless you have - /// a specific need to register a listener yourself. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn on_action_when( - &mut self, - condition: bool, - action_type: TypeId, - listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static, - ) { - self.invalidator.debug_assert_paint(); - - if condition { - self.next_frame - .dispatch_tree - .on_action(action_type, Rc::new(listener)); - } - } - - /// Read information about the GPU backing this window. - /// Currently returns None on Mac and Windows. - pub fn gpu_specs(&self) -> Option { - self.platform_window.gpu_specs() - } - - /// Perform titlebar double-click action. - /// This is macOS specific. - pub fn titlebar_double_click(&self) { - self.platform_window - .titlebar_double_click(self.is_resizable, self.is_minimizable); - } - - /// Gets the window's title at the platform level. - /// This is macOS specific. - pub fn window_title(&self) -> String { - self.platform_window.get_title() - } - - /// Returns a list of all tabbed windows and their titles. - /// This is macOS specific. - pub fn tabbed_windows(&self) -> Option> { - self.platform_window.tabbed_windows() - } - - /// Returns the tab bar visibility. - /// This is macOS specific. - pub fn tab_bar_visible(&self) -> bool { - self.platform_window.tab_bar_visible() - } - - /// Merges all open windows into a single tabbed window. - /// This is macOS specific. - pub fn merge_all_windows(&self) { - self.platform_window.merge_all_windows() - } - - /// Moves the tab to a new containing window. - /// This is macOS specific. - pub fn move_tab_to_new_window(&self) { - self.platform_window.move_tab_to_new_window() - } - - /// Shows or hides the window tab overview. - /// This is macOS specific. - pub fn toggle_window_tab_overview(&self) { - self.platform_window.toggle_window_tab_overview() - } - - /// Sets the tabbing identifier for the window. - /// This is macOS specific. - pub fn set_tabbing_identifier(&self, tabbing_identifier: Option) { - self.platform_window - .set_tabbing_identifier(tabbing_identifier) - } - - /// Request the OS to play an alert sound. On some platforms this is associated - /// with the window, for others it's just a simple global function call. - pub fn play_system_bell(&self) { - self.platform_window.play_system_bell() - } - - /// Returns whether accessibility features are active for this frame, - /// i.e. whether assistive technology (such as a screen reader) is - /// connected and an accessibility tree is being built. - /// - /// Use this to skip computing data during rendering that is only - /// observable through the accessibility tree. When accessibility is - /// activated, a redraw is forced, so gated work is recomputed before the - /// next tree update is sent to the platform. - /// - /// See the [accessibility guide](crate::_accessibility) for an overview. - pub fn is_a11y_active(&self) -> bool { - self.a11y.is_active() - } - - /// Debug representation of the last frame's accessibility information. - pub fn debug_a11y_tree_json(&self) -> Option { - self.a11y.debug_tree_json() - } - - /// Register a listener for an accessibility action on a specific node. - /// The listener will be called when a screen reader requests the given - /// action on the node identified by `node_id`. - /// - /// See the [accessibility guide](crate::_accessibility) for an overview. - pub fn on_a11y_action( - &mut self, - node_id: accesskit::NodeId, - action: accesskit::Action, - listener: impl FnMut(Option<&accesskit::ActionData>, &mut Window, &mut App) + 'static, - ) { - self.a11y - .action_listeners - .entry(node_id) - .or_default() - .push((action, Box::new(listener))); - } - - #[cfg(not(target_family = "wasm"))] - pub(crate) fn handle_a11y_action(&mut self, request: accesskit::ActionRequest, cx: &mut App) { - // Take listeners out temporarily so the closures can borrow Window - // mutably, then restore them afterward. - if let Some(mut listeners) = self.a11y.action_listeners.remove(&request.target_node) { - let extra_data = request.data.as_ref(); - let mut matched = false; - for (action, listener) in &mut listeners { - if *action == request.action { - listener(extra_data, self, cx); - matched = true; - } - } - self.a11y - .action_listeners - .insert(request.target_node, listeners); - if matched { - return; - } - } - - // Fall back to built-in action handling. - match request.action { - accesskit::Action::Click => { - if let Some(bounds) = self.a11y.node_bounds.get(&request.target_node).copied() { - let center = bounds.center(); - let mouse_down = PlatformInput::MouseDown(crate::MouseDownEvent { - button: MouseButton::Left, - position: center, - modifiers: Modifiers::default(), - click_count: 1, - first_mouse: false, - }); - let mouse_up = PlatformInput::MouseUp(MouseUpEvent { - button: MouseButton::Left, - position: center, - modifiers: Modifiers::default(), - click_count: 1, - }); - self.dispatch_event(mouse_down, cx); - self.dispatch_event(mouse_up, cx); - } - } - accesskit::Action::Focus => { - if let Some(focus_id) = self.a11y.focus_ids.get(&request.target_node).copied() - && let Some(handle) = FocusHandle::for_id(focus_id, &cx.focus_handles) - { - self.focus(&handle, cx); - } - } - accesskit::Action::Blur => { - self.blur(cx); - } - _ => { - log::debug!( - "Unhandled a11y action: {:?} on {:?}", - request.action, - request.target_node - ); - } - } - } - - /// Toggles the inspector mode on this window. - #[cfg(any(feature = "inspector", debug_assertions))] - pub fn toggle_inspector(&mut self, cx: &mut App) { - self.inspector = match self.inspector { - None => Some(cx.new(|_| Inspector::new())), - Some(_) => None, - }; - self.refresh(); - } - - /// Returns true if the window is in inspector mode. - pub fn is_inspector_picking(&self, _cx: &App) -> bool { - #[cfg(any(feature = "inspector", debug_assertions))] - { - if let Some(inspector) = &self.inspector { - return inspector.read(_cx).is_picking(); - } - } - false - } - - /// Executes the provided function with mutable access to an inspector state. - #[cfg(any(feature = "inspector", debug_assertions))] - pub fn with_inspector_state( - &mut self, - _inspector_id: Option<&crate::InspectorElementId>, - cx: &mut App, - f: impl FnOnce(&mut Option, &mut Self) -> R, - ) -> R { - if let Some(inspector_id) = _inspector_id - && let Some(inspector) = &self.inspector - { - let inspector = inspector.clone(); - let active_element_id = inspector.read(cx).active_element_id(); - if Some(inspector_id) == active_element_id { - return inspector.update(cx, |inspector, _cx| { - inspector.with_active_element_state(self, f) - }); - } - } - f(&mut None, self) - } - - #[cfg(any(feature = "inspector", debug_assertions))] - pub(crate) fn build_inspector_element_id( - &mut self, - path: crate::InspectorElementPath, - ) -> crate::InspectorElementId { - self.invalidator.debug_assert_paint_or_prepaint(); - let path = Rc::new(path); - let next_instance_id = self - .next_frame - .next_inspector_instance_ids - .entry(path.clone()) - .or_insert(0); - let instance_id = *next_instance_id; - *next_instance_id += 1; - crate::InspectorElementId { path, instance_id } - } - - #[cfg(any(feature = "inspector", debug_assertions))] - fn prepaint_inspector(&mut self, inspector_width: Pixels, cx: &mut App) -> Option { - if let Some(inspector) = self.inspector.take() { - let mut inspector_element = AnyView::from(inspector.clone()).into_any_element(); - inspector_element.prepaint_as_root( - point(self.viewport_size.width - inspector_width, px(0.0)), - size(inspector_width, self.viewport_size.height).into(), - self, - cx, - ); - self.inspector = Some(inspector); - Some(inspector_element) - } else { - None - } - } - - #[cfg(any(feature = "inspector", debug_assertions))] - fn paint_inspector(&mut self, mut inspector_element: Option, cx: &mut App) { - if let Some(mut inspector_element) = inspector_element { - inspector_element.paint(self, cx); - }; - } - - /// Registers a hitbox that can be used for inspector picking mode, allowing users to select and - /// inspect UI elements by clicking on them. - #[cfg(any(feature = "inspector", debug_assertions))] - pub fn insert_inspector_hitbox( - &mut self, - hitbox_id: HitboxId, - inspector_id: Option<&crate::InspectorElementId>, - cx: &App, - ) { - self.invalidator.debug_assert_paint_or_prepaint(); - if !self.is_inspector_picking(cx) { - return; - } - if let Some(inspector_id) = inspector_id { - self.next_frame - .inspector_hitboxes - .insert(hitbox_id, inspector_id.clone()); - } - } - - #[cfg(any(feature = "inspector", debug_assertions))] - fn paint_inspector_hitbox(&mut self, cx: &App) { - if let Some(inspector) = self.inspector.as_ref() { - let inspector = inspector.read(cx); - if let Some((hitbox_id, _)) = self.hovered_inspector_hitbox(inspector, &self.next_frame) - && let Some(hitbox) = self - .next_frame - .hitboxes - .iter() - .find(|hitbox| hitbox.id == hitbox_id) - { - self.paint_quad(crate::fill(hitbox.bounds, crate::rgba(0x61afef4d))); - } - } - } - - #[cfg(any(feature = "inspector", debug_assertions))] - fn handle_inspector_mouse_event(&mut self, event: &dyn Any, cx: &mut App) { - let Some(inspector) = self.inspector.clone() else { - return; - }; - if event.downcast_ref::().is_some() { - inspector.update(cx, |inspector, _cx| { - if let Some((_, inspector_id)) = - self.hovered_inspector_hitbox(inspector, &self.rendered_frame) - { - inspector.hover(inspector_id, self); - } - }); - } else if event.downcast_ref::().is_some() { - inspector.update(cx, |inspector, _cx| { - if let Some((_, inspector_id)) = - self.hovered_inspector_hitbox(inspector, &self.rendered_frame) - { - inspector.select(inspector_id, self); - } - }); - } else if let Some(event) = event.downcast_ref::() { - // This should be kept in sync with SCROLL_LINES in x11 platform. - const SCROLL_LINES: f32 = 3.0; - const SCROLL_PIXELS_PER_LAYER: f32 = 36.0; - let delta_y = event - .delta - .pixel_delta(px(SCROLL_PIXELS_PER_LAYER / SCROLL_LINES)) - .y; - if let Some(inspector) = self.inspector.clone() { - inspector.update(cx, |inspector, _cx| { - if let Some(depth) = inspector.pick_depth.as_mut() { - *depth += f32::from(delta_y) / SCROLL_PIXELS_PER_LAYER; - let max_depth = self.mouse_hit_test.ids.len() as f32 - 0.5; - if *depth < 0.0 { - *depth = 0.0; - } else if *depth > max_depth { - *depth = max_depth; - } - if let Some((_, inspector_id)) = - self.hovered_inspector_hitbox(inspector, &self.rendered_frame) - { - inspector.set_active_element_id(inspector_id, self); - } - } - }); - } - } - } - - #[cfg(any(feature = "inspector", debug_assertions))] - fn hovered_inspector_hitbox( - &self, - inspector: &Inspector, - frame: &Frame, - ) -> Option<(HitboxId, crate::InspectorElementId)> { - if let Some(pick_depth) = inspector.pick_depth { - let depth = (pick_depth as i64).try_into().unwrap_or(0); - let max_skipped = self.mouse_hit_test.ids.len().saturating_sub(1); - let skip_count = (depth as usize).min(max_skipped); - for hitbox_id in self.mouse_hit_test.ids.iter().skip(skip_count) { - if let Some(inspector_id) = frame.inspector_hitboxes.get(hitbox_id) { - return Some((*hitbox_id, inspector_id.clone())); - } - } - } - None - } - - /// For testing: set the current modifier keys state. - /// This does not generate any events. - #[cfg(any(test, feature = "test-support"))] - pub fn set_modifiers(&mut self, modifiers: Modifiers) { - self.modifiers = modifiers; - } - - /// For testing: simulate a mouse move event to the given position. - /// This dispatches the event through the normal event handling path, - /// which will trigger hover states and tooltips. - #[cfg(any(test, feature = "test-support"))] - pub fn simulate_mouse_move(&mut self, position: Point, cx: &mut App) { - let event = PlatformInput::MouseMove(MouseMoveEvent { - position, - modifiers: self.modifiers, - pressed_button: None, - }); - let _ = self.dispatch_event(event, cx); - } -} - -// #[derive(Clone, Copy, Eq, PartialEq, Hash)] -slotmap::new_key_type! { - /// A unique identifier for a window. - pub struct WindowId; -} - -impl WindowId { - /// Converts this window ID to a `u64`. - pub fn as_u64(&self) -> u64 { - self.0.as_ffi() - } -} - -impl From for WindowId { - fn from(value: u64) -> Self { - WindowId(slotmap::KeyData::from_ffi(value)) - } -} - -/// A handle to a window with a specific root view type. -/// Note that this does not keep the window alive on its own. -#[derive(Deref, DerefMut)] -pub struct WindowHandle { - #[deref] - #[deref_mut] - pub(crate) any_handle: AnyWindowHandle, - state_type: PhantomData V>, -} - -impl Debug for WindowHandle { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("WindowHandle") - .field("any_handle", &self.any_handle.id.as_u64()) - .finish() - } -} - -impl WindowHandle { - /// Creates a new handle from a window ID. - /// This does not check if the root type of the window is `V`. - pub fn new(id: WindowId) -> Self { - WindowHandle { - any_handle: AnyWindowHandle { - id, - state_type: TypeId::of::(), - root_entity_type_name: std::any::type_name::(), - }, - state_type: PhantomData, - } - } - - /// Get the root view out of this window. - /// - /// This will fail if the window is closed or if the root view's type does not match `V`. - #[cfg(any(test, feature = "test-support"))] - pub fn root(&self, cx: &mut C) -> Result> - where - C: AppContext, - { - cx.update_window(self.any_handle, |root_view, _, _| { - root_view - .downcast::() - .map_err(|_| anyhow!("the type of the window's root view has changed")) - })? - } - - /// Updates the root view of this window. - /// - /// This will fail if the window has been closed or if the root view's type does not match - pub fn update( - &self, - cx: &mut C, - update: impl FnOnce(&mut V, &mut Window, &mut Context) -> R, - ) -> Result - where - C: AppContext, - { - cx.update_window(self.any_handle, |root_view, window, cx| { - let view = root_view - .downcast::() - .map_err(|_| anyhow!("the type of the window's root view has changed"))?; - - Ok(view.update(cx, |view, cx| update(view, window, cx))) - })? - } - - /// Read the root view out of this window. - /// - /// This will fail if the window is closed or if the root view's type does not match `V`. - pub fn read<'a>(&self, cx: &'a App) -> Result<&'a V> { - let x = cx - .windows - .get(self.id) - .and_then(|window| { - window - .as_deref() - .and_then(|window| window.root.clone()) - .map(|root_view| root_view.downcast::()) - }) - .context("window not found")? - .map_err(|_| anyhow!("the type of the window's root view has changed"))?; - - Ok(x.read(cx)) - } - - /// Read the root view out of this window, with a callback - /// - /// This will fail if the window is closed or if the root view's type does not match `V`. - pub fn read_with(&self, cx: &C, read_with: impl FnOnce(&V, &App) -> R) -> Result - where - C: AppContext, - { - cx.read_window(self, |root_view, cx| read_with(root_view.read(cx), cx)) - } - - /// Read the root view pointer off of this window. - /// - /// This will fail if the window is closed or if the root view's type does not match `V`. - pub fn entity(&self, cx: &C) -> Result> - where - C: AppContext, - { - cx.read_window(self, |root_view, _cx| root_view) - } - - /// Check if this window is 'active'. - /// - /// Will return `None` if the window is closed or currently - /// borrowed. - pub fn is_active(&self, cx: &mut App) -> Option { - cx.update_window(self.any_handle, |_, window, _| window.is_window_active()) - .ok() - } -} - -impl Copy for WindowHandle {} - -impl Clone for WindowHandle { - fn clone(&self) -> Self { - *self - } -} - -impl PartialEq for WindowHandle { - fn eq(&self, other: &Self) -> bool { - self.any_handle == other.any_handle - } -} - -impl Eq for WindowHandle {} - -impl Hash for WindowHandle { - fn hash(&self, state: &mut H) { - self.any_handle.hash(state); - } -} - -impl From> for AnyWindowHandle { - fn from(val: WindowHandle) -> Self { - val.any_handle - } -} - -/// A handle to a window with any root view type, which can be downcast to a window with a specific root view type. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct AnyWindowHandle { - pub(crate) id: WindowId, - state_type: TypeId, - root_entity_type_name: &'static str, -} - -impl AnyWindowHandle { - /// Get the ID of this window. - pub fn window_id(&self) -> WindowId { - self.id - } - - /// Returns the name of the window's declared root entity type. - pub fn root_entity_type_name(&self) -> &'static str { - self.root_entity_type_name - } - - /// Attempt to convert this handle to a window handle with a specific root view type. - /// If the types do not match, this will return `None`. - pub fn downcast(&self) -> Option> { - if TypeId::of::() == self.state_type { - Some(WindowHandle { - any_handle: *self, - state_type: PhantomData, - }) - } else { - None - } - } - - /// Updates the state of the root view of this window. - /// - /// This will fail if the window has been closed. - pub fn update( - self, - cx: &mut C, - update: impl FnOnce(AnyView, &mut Window, &mut App) -> R, - ) -> Result - where - C: AppContext, - { - cx.update_window(self, update) - } - - /// Read the state of the root view of this window. - /// - /// This will fail if the window has been closed. - pub fn read(self, cx: &C, read: impl FnOnce(Entity, &App) -> R) -> Result - where - C: AppContext, - T: 'static, - { - let view = self - .downcast::() - .context("the type of the window's root view has changed")?; - - cx.read_window(&view, read) - } -} - -impl HasWindowHandle for Window { - fn window_handle(&self) -> Result, HandleError> { - self.platform_window.window_handle() - } -} - -impl HasDisplayHandle for Window { - fn display_handle( - &self, - ) -> std::result::Result, HandleError> { - self.platform_window.display_handle() - } -} - -/// An identifier for an [`Element`]. -/// -/// Can be constructed with a string, a number, or both, as well -/// as other internal representations. -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub enum ElementId { - /// The ID of a View element - View(EntityId), - /// An integer ID. - Integer(u64), - /// A string based ID. - Name(SharedString), - /// A UUID. - Uuid(Uuid), - /// An ID that's equated with a focus handle. - FocusHandle(FocusId), - /// A combination of a name and an integer. - NamedInteger(SharedString, u64), - /// A path. - Path(Arc), - /// A code location. - CodeLocation(core::panic::Location<'static>), - /// A labeled child of an element. - NamedChild(Arc, SharedString), - /// A byte array ID (used for text-anchors) - OpaqueId([u8; 20]), -} - -impl ElementId { - /// Constructs an `ElementId::NamedInteger` from a name and `usize`. - pub fn named_usize(name: impl Into, integer: usize) -> ElementId { - Self::NamedInteger(name.into(), integer as u64) - } -} - -impl Display for ElementId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ElementId::View(entity_id) => write!(f, "view-{}", entity_id)?, - ElementId::Integer(ix) => write!(f, "{}", ix)?, - ElementId::Name(name) => write!(f, "{}", name)?, - ElementId::FocusHandle(_) => write!(f, "FocusHandle")?, - ElementId::NamedInteger(s, i) => write!(f, "{}-{}", s, i)?, - ElementId::Uuid(uuid) => write!(f, "{}", uuid)?, - ElementId::Path(path) => write!(f, "{}", path.display())?, - ElementId::CodeLocation(location) => write!(f, "{}", location)?, - ElementId::NamedChild(id, name) => write!(f, "{}-{}", id, name)?, - ElementId::OpaqueId(opaque_id) => write!(f, "{:x?}", opaque_id)?, - } - - Ok(()) - } -} - -impl TryInto for ElementId { - type Error = anyhow::Error; - - fn try_into(self) -> anyhow::Result { - if let ElementId::Name(name) = self { - Ok(name) - } else { - anyhow::bail!("element id is not string") - } - } -} - -impl From for ElementId { - fn from(id: usize) -> Self { - ElementId::Integer(id as u64) - } -} - -impl From for ElementId { - fn from(id: i32) -> Self { - Self::Integer(id as u64) - } -} - -impl From for ElementId { - fn from(name: SharedString) -> Self { - ElementId::Name(name) - } -} - -impl From for ElementId { - fn from(name: String) -> Self { - ElementId::Name(name.into()) - } -} - -impl From> for ElementId { - fn from(name: Arc) -> Self { - ElementId::Name(name.into()) - } -} - -impl From> for ElementId { - fn from(path: Arc) -> Self { - ElementId::Path(path) - } -} - -impl From<&'static str> for ElementId { - fn from(name: &'static str) -> Self { - ElementId::Name(SharedString::new_static(name)) - } -} - -impl<'a> From<&'a FocusHandle> for ElementId { - fn from(handle: &'a FocusHandle) -> Self { - ElementId::FocusHandle(handle.id) - } -} - -impl From<(&'static str, EntityId)> for ElementId { - fn from((name, id): (&'static str, EntityId)) -> Self { - ElementId::NamedInteger(SharedString::new_static(name), id.as_u64()) - } -} - -impl From<(&'static str, usize)> for ElementId { - fn from((name, id): (&'static str, usize)) -> Self { - ElementId::NamedInteger(SharedString::new_static(name), id as u64) - } -} - -impl From<(SharedString, usize)> for ElementId { - fn from((name, id): (SharedString, usize)) -> Self { - ElementId::NamedInteger(name, id as u64) - } -} - -impl From<(&'static str, u64)> for ElementId { - fn from((name, id): (&'static str, u64)) -> Self { - ElementId::NamedInteger(SharedString::new_static(name), id) - } -} - -impl From for ElementId { - fn from(value: Uuid) -> Self { - Self::Uuid(value) - } -} - -impl From<(&'static str, u32)> for ElementId { - fn from((name, id): (&'static str, u32)) -> Self { - ElementId::NamedInteger(SharedString::new_static(name), u64::from(id)) - } -} - -impl> From<(ElementId, T)> for ElementId { - fn from((id, name): (ElementId, T)) -> Self { - ElementId::NamedChild(Arc::new(id), name.into()) - } -} - -impl From<&'static core::panic::Location<'static>> for ElementId { - fn from(location: &'static core::panic::Location<'static>) -> Self { - ElementId::CodeLocation(*location) - } -} - -impl From<[u8; 20]> for ElementId { - fn from(opaque_id: [u8; 20]) -> Self { - ElementId::OpaqueId(opaque_id) - } -} - -/// A rectangle to be rendered in the window at the given position and size. -/// Passed as an argument [`Window::paint_quad`]. -#[derive(Clone)] -pub struct PaintQuad { - /// The bounds of the quad within the window. - pub bounds: Bounds, - /// The radii of the quad's corners. - pub corner_radii: Corners, - /// The background color of the quad. - pub background: Background, - /// The widths of the quad's borders. - pub border_widths: Edges, - /// The color of the quad's borders. - pub border_color: Hsla, - /// The style of the quad's borders. - pub border_style: BorderStyle, -} - -impl PaintQuad { - /// Sets the corner radii of the quad. - pub fn corner_radii(self, corner_radii: impl Into>) -> Self { - PaintQuad { - corner_radii: corner_radii.into(), - ..self - } - } - - /// Sets the border widths of the quad. - pub fn border_widths(self, border_widths: impl Into>) -> Self { - PaintQuad { - border_widths: border_widths.into(), - ..self - } - } - - /// Sets the border color of the quad. - pub fn border_color(self, border_color: impl Into) -> Self { - PaintQuad { - border_color: border_color.into(), - ..self - } - } - - /// Sets the background color of the quad. - pub fn background(self, background: impl Into) -> Self { - PaintQuad { - background: background.into(), - ..self - } - } -} - -/// Creates a quad with the given parameters. -pub fn quad( - bounds: Bounds, - corner_radii: impl Into>, - background: impl Into, - border_widths: impl Into>, - border_color: impl Into, - border_style: BorderStyle, -) -> PaintQuad { - PaintQuad { - bounds, - corner_radii: corner_radii.into(), - background: background.into(), - border_widths: border_widths.into(), - border_color: border_color.into(), - border_style, - } -} - -/// Creates a filled quad with the given bounds and background color. -pub fn fill(bounds: impl Into>, background: impl Into) -> PaintQuad { - PaintQuad { - bounds: bounds.into(), - corner_radii: (0.).into(), - background: background.into(), - border_widths: (0.).into(), - border_color: transparent_black(), - border_style: BorderStyle::default(), - } -} - -/// Creates a rectangle outline with the given bounds, border color, and a 1px border width -pub fn outline( - bounds: impl Into>, - border_color: impl Into, - border_style: BorderStyle, -) -> PaintQuad { - PaintQuad { - bounds: bounds.into(), - corner_radii: (0.).into(), - background: transparent_black().into(), - border_widths: (1.).into(), - border_color: border_color.into(), - border_style, - } -} - -#[cfg(test)] -mod tests { - use std::{ - cell::{Cell, RefCell}, - path::PathBuf, - rc::Rc, - time::Duration, - }; - - use crate::{ - canvas, div, point, px, size, AnyWindowHandle, AppContext as _, Bounds, ContentMask, - Context, Corners, DispatchPhase, DragMoveEvent, Empty, ExternalDragPayload, ExternalPaths, - FileDragPaths, FileDropEvent, FocusHandle, InputEvent as _, InteractiveElement as _, - IntoElement, LongPressEvent, MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, - Pixels, Point, Render, RequestFrameOptions, StatefulInteractiveElement as _, Styled, - TestAppContext, TouchDragEvent, TouchEvent, TouchId, TouchPhase, Window, WindowAppearance, - WindowOptions, - }; - - struct EmptyView; - - #[test] - fn rounded_content_mask_intersection_keeps_the_original_curve() { - let outer = ContentMask { - bounds: Bounds::from_corners(point(px(0.), px(0.)), point(px(100.), px(100.))), - corner_radii: Corners::all(px(16.)), - ..Default::default() - }; - let inset = ContentMask { - bounds: Bounds::from_corners(point(px(4.), px(4.)), point(px(96.), px(96.))), - ..Default::default() - }; - - let intersection = outer.intersect(&inset); - assert_eq!(intersection.bounds.origin, point(px(4.), px(4.))); - assert_eq!(intersection.rounded_clips[0].bounds, outer.bounds); - assert!(intersection.contains(point(px(4.5), px(8.5)))); - } - - impl Render for EmptyView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - } - } - - struct OpensWindowOnPaint { - opened: Rc>, - } - - impl Render for OpensWindowOnPaint { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let opened = self.opened.clone(); - div() - .size_full() - .child(canvas( - |_, _, _| {}, - move |_, _, _window, cx| { - if !opened.replace(true) { - cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| EmptyView)) - .unwrap(); - } - }, - )) - // Siblings painted after the canvas: their elements were - // allocated in the arena before the nested draw, so they detect - // a mid-draw arena clear when painted afterwards. - .child(div().child("after")) - } - } - - /// Opening a window synchronously draws it and requests an element arena - /// clear. When that happens from within another window's draw (here: from - /// an element's paint), the clear must be deferred until the outer draw - /// finishes, or the outer draw's arena-allocated elements would be freed - /// out from under it. - #[test] - fn test_window_opened_during_draw_defers_arena_clear() { - let mut cx = TestAppContext::single(); - - let opened = Rc::new(Cell::new(false)); - // add_window draws once, which runs the nested open_window mid-draw. - let window = cx.add_window({ - let opened = opened.clone(); - move |_, _| OpensWindowOnPaint { opened } - }); - - assert!(opened.get()); - assert_eq!(cx.windows().len(), 2); - - // The deferred clear must actually run once the outer draw unwinds: - // subsequent draws of both windows work against a fresh arena. - cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - } - - /// Platforms that stop requesting frames for idle windows (currently web) - /// rely on the frame waker firing whenever frame demand arises; a demand - /// source that skips the waker shows up there as a window that silently - /// stops repainting until unrelated activity wakes it. - #[gpui::test] - fn test_frame_waker_fires_on_frame_demand(cx: &mut TestAppContext) { - let window = cx.add_window(|_, _| EmptyView); - let test_window = cx.test_window(window.into()); - - // Windows start dirty, and that can predate waker installation; - // installing the waker must deliver the pending wake or the first - // frame would never be requested. - assert!( - test_window.frame_wake_count() >= 1, - "opening a window must wake the frame source for the initial frame" - ); - - // Serve outstanding demand (present the frame drawn by `add_window`). - test_window.simulate_frame_request(RequestFrameOptions::default()); - - // An idle window must not wake on clean frames or plain updates, or - // the frame source could never stop. - let baseline = test_window.frame_wake_count(); - test_window.simulate_frame_request(RequestFrameOptions::default()); - window.update(cx, |_, _, _| {}).unwrap(); - assert_eq!( - test_window.frame_wake_count(), - baseline, - "clean frames and non-notifying updates must not wake the frame source" - ); - - // Notifying a view in an idle window is the core demand signal. - window.update(cx, |_, _, cx| cx.notify()).unwrap(); - assert!( - test_window.frame_wake_count() > baseline, - "notifying a view in an idle window must wake the frame source" - ); - - // Serving that demand returns to idle without further wakes. - test_window.simulate_frame_request(RequestFrameOptions::default()); - let baseline = test_window.frame_wake_count(); - test_window.simulate_frame_request(RequestFrameOptions::default()); - assert_eq!( - test_window.frame_wake_count(), - baseline, - "serving demand must return the window to idle" - ); - - // Next-frame callbacks create demand without dirtying the window. - window - .update(cx, |_, window, _| window.on_next_frame(|_, _| {})) - .unwrap(); - assert!( - test_window.frame_wake_count() > baseline, - "scheduling a next-frame callback in an idle window must wake the frame source" - ); - } - - /// A frame request that arrives while next-frame callbacks are pending - /// must never strand them: either the frame runs them, or (when the - /// inactive-window frame-rate throttle defers the frame) the waker fires - /// so another request is delivered. - #[gpui::test] - fn test_pending_next_frame_callbacks_are_not_stranded(cx: &mut TestAppContext) { - let window = cx.add_window(|_, _| EmptyView); - let test_window = cx.test_window(window.into()); - // Establish a recent last-frame time so the inactive-window throttle - // can engage on the next request. - test_window.simulate_frame_request(RequestFrameOptions::default()); - - let callback_ran = Rc::new(Cell::new(false)); - window - .update(cx, { - let callback_ran = callback_ran.clone(); - move |_, window, _| { - window.on_next_frame(move |_, _| callback_ran.set(true)); - } - }) - .unwrap(); - - let baseline = test_window.frame_wake_count(); - test_window.simulate_frame_request(RequestFrameOptions::default()); - // The test window is inactive, so this request throttles to ~30fps - // when it lands within the throttle interval of the previous frame - // (the common case here, but timing-dependent): the callback is - // deferred and the waker must re-arm the frame source. On a slow run - // the request instead lands outside the interval and runs the - // callback directly. - assert!( - test_window.frame_wake_count() > baseline || callback_ran.get(), - "a frame request with pending next-frame callbacks must either run them or re-arm the frame source" - ); - } - - #[gpui::test] - fn test_window_reports_no_raw_handle_instead_of_panicking(cx: &mut TestAppContext) { - use raw_window_handle::{HandleError, HasDisplayHandle as _, HasWindowHandle as _}; - - let window = cx.add_window(|_, _| EmptyView); - window - .update(cx, |_, window, _| { - assert!(matches!( - window.window_handle(), - Err(HandleError::NotSupported) - )); - assert!(matches!( - window.display_handle(), - Err(HandleError::NotSupported) - )); - }) - .unwrap(); - } - - #[gpui::test] - fn test_appearance_change_runs_after_app_update(cx: &mut TestAppContext) { - let window = cx.add_window(|_, _| EmptyView); - let observed_appearance = Rc::new(Cell::new(None)); - let _subscription = window - .update(cx, { - let observed_appearance = observed_appearance.clone(); - move |_, window, _| { - window.observe_window_appearance(move |window, _| { - observed_appearance.set(Some(window.appearance())); - }) - } - }) - .unwrap(); - let test_window = cx.test_window(window.into()); - - cx.update(|_| { - test_window.simulate_appearance_change(WindowAppearance::Dark); - assert_eq!(observed_appearance.get(), None); - }); - cx.run_until_parked(); - - assert_eq!(observed_appearance.get(), Some(WindowAppearance::Dark)); - } - - #[gpui::test] - fn queued_frame_callback_wakes_a_parked_render_loop(cx: &mut TestAppContext) { - let window = cx.add_window(|_, _| Empty); - let test_window = cx.test_window(window.into()); - - assert!(test_window.simulate_scheduled_frame()); - assert!(test_window.simulate_scheduled_frame()); - assert!(!test_window.frame_scheduled()); - - cx.update_window(window.into(), |_, window, _| { - window.active.set(true); - window.on_next_frame(|_, _| {}); - }) - .unwrap(); - assert!( - test_window.frame_scheduled(), - "queuing work on a parked window must wake the render loop" - ); - - assert!(test_window.simulate_scheduled_frame()); - assert!( - test_window.frame_scheduled(), - "presenting the frame must await one compositor callback" - ); - assert!(test_window.simulate_scheduled_frame()); - assert!(!test_window.frame_scheduled()); - } - - #[gpui::test] - fn pending_presentation_wakes_a_parked_render_loop(cx: &mut TestAppContext) { - let window = cx.add_window(|_, _| Empty); - let test_window = cx.test_window(window.into()); - - assert!(test_window.simulate_scheduled_frame()); - assert!(test_window.simulate_scheduled_frame()); - assert!(!test_window.frame_scheduled()); - - cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - - assert!( - test_window.frame_scheduled(), - "a rendered scene awaiting presentation must wake the render loop" - ); - } - - #[gpui::test] - fn callback_queued_during_a_frame_requests_a_follow_up(cx: &mut TestAppContext) { - let window = cx.add_window(|_, _| Empty); - let test_window = cx.test_window(window.into()); - - let callback_ran = Rc::new(Cell::new(false)); - cx.update_window(window.into(), |_, window, _| { - // Inactive windows are frame-rate throttled, which would defer the - // ticks this test drives manually. - window.active.set(true); - let callback_ran = callback_ran.clone(); - window.on_next_frame(move |window, _| { - window.on_next_frame(move |_, _| callback_ran.set(true)); - }); - }) - .unwrap(); - - assert!(test_window.simulate_scheduled_frame()); - assert!(!callback_ran.get()); - assert!( - test_window.frame_scheduled(), - "a callback queued mid-frame must schedule a follow-up before the loop parks" - ); - - assert!(test_window.simulate_scheduled_frame()); - assert!(callback_ran.get()); - } - - struct RootView { - explicit_size: bool, - child_bounds: Rc>>, - } - - impl Render for RootView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - let child_bounds = self.child_bounds.clone(); - let root = div().flex().flex_col().child( - canvas( - move |bounds, _, _| child_bounds.set(bounds), - |_, _, _, _| {}, - ) - .size_full(), - ); - if self.explicit_size { - root.w(px(300.)).h(px(200.)) - } else { - root - } - } - } - - #[test] - fn auto_sized_window_root_fills_the_window() { - let mut cx = TestAppContext::single(); - let child_bounds = Rc::new(Cell::new(Bounds::default())); - let window = cx.add_window({ - let child_bounds = child_bounds.clone(); - move |_, _| RootView { - explicit_size: false, - child_bounds, - } - }); - - let viewport_size = cx - .update_window(window.into(), |_, window, cx| { - window.draw(cx).clear(cx); - window.viewport_size() - }) - .unwrap(); - - assert_eq!(child_bounds.get().size, viewport_size); - } - - #[test] - fn explicitly_sized_window_root_keeps_its_size() { - let mut cx = TestAppContext::single(); - let child_bounds = Rc::new(Cell::new(Bounds::default())); - let window = cx.add_window({ - let child_bounds = child_bounds.clone(); - move |_, _| RootView { - explicit_size: true, - child_bounds, - } - }); - - cx.update_window(window.into(), |_, window, cx| { - window.draw(cx).clear(cx); - }) - .unwrap(); - - assert_eq!(child_bounds.get().size, size(px(300.), px(200.))); - } - - struct FileDragView { - path: PathBuf, - observed_drag_moves: Rc>>>, - observed_drops: Rc>>, - } - - impl Render for FileDragView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - div() - .id("file-drag") - .size_full() - .on_drag(self.path.clone(), |_, _, _, cx| cx.new(|_| Empty)) - .external_drag_payload(|path: &PathBuf, _, _| { - Some(ExternalDragPayload::Files(FileDragPaths::new([( - path.clone(), - true, - )]))) - }) - .on_drag_move({ - let observed_drag_moves = self.observed_drag_moves.clone(); - move |event: &DragMoveEvent, _, _| { - observed_drag_moves.borrow_mut().push(event.event.position); - } - }) - .on_drop({ - let observed_drops = self.observed_drops.clone(); - move |path: &PathBuf, _, _| observed_drops.borrow_mut().push(path.clone()) - }) - } - } - - #[gpui::test] - fn file_drag_is_promoted_once_and_restored_in_source_window(cx: &mut TestAppContext) { - struct Drag { - window: AnyWindowHandle, - observed_drag_moves: Rc>>>, - observed_drops: Rc>>, - } - - fn start_drag(cx: &mut TestAppContext, path: PathBuf, platform_result: bool) -> Drag { - let observed_drag_moves = Rc::new(RefCell::new(Vec::new())); - let observed_drops = Rc::new(RefCell::new(Vec::new())); - let window: AnyWindowHandle = cx - .add_window({ - let observed_drag_moves = observed_drag_moves.clone(); - let observed_drops = observed_drops.clone(); - move |_, _| FileDragView { - path, - observed_drag_moves, - observed_drops, - } - }) - .into(); - cx.test_window(window) - .set_start_external_drag_result(platform_result); - - let update_result = cx.update_window(window, |_, window, cx| { - window.draw(cx).clear(cx); - window.dispatch_event( - MouseDownEvent { - position: point(px(10.), px(10.)), - button: MouseButton::Left, - modifiers: Default::default(), - click_count: 1, - first_mouse: false, - } - .to_platform_input(), - cx, - ); - window.dispatch_event( - MouseMoveEvent { - position: point(px(20.), px(20.)), - pressed_button: Some(MouseButton::Left), - modifiers: Default::default(), - } - .to_platform_input(), - cx, - ); - assert!(cx.active_drag.is_some()); - }); - assert!( - update_result.is_ok(), - "failed to start drag: {update_result:?}" - ); - - assert!(cx.test_window(window).external_drag_files().is_empty()); - Drag { - window, - observed_drag_moves, - observed_drops, - } - } - - let successful_path = PathBuf::from("/tmp/successful-drag"); - let successful = start_drag(cx, successful_path.clone(), true); - let outside_position = point(px(-1.), px(20.)); - let update_result = cx.update_window(successful.window, |_, window, cx| { - window.dispatch_event( - MouseMoveEvent { - position: outside_position, - pressed_button: Some(MouseButton::Left), - modifiers: Default::default(), - } - .to_platform_input(), - cx, - ); - assert!(cx.active_drag.is_none()); - }); - assert!( - update_result.is_ok(), - "failed to promote drag: {update_result:?}" - ); - assert_eq!( - cx.test_window(successful.window).external_drag_files(), - [(successful_path.clone(), true)] - ); - // Views must still see the move that leaves the window, otherwise they never learn to tear - // down the drag state they built up while the pointer was inside. - assert_eq!( - successful.observed_drag_moves.borrow().last(), - Some(&outside_position) - ); - - let destination: AnyWindowHandle = cx.add_window(|_, _| EmptyView).into(); - let reentry_position = point(px(30.), px(30.)); - let external_paths = || ExternalPaths([successful_path.clone()].into_iter().collect()); - let update_result = cx.update_window(destination, |_, window, cx| { - window.dispatch_event( - FileDropEvent::Entered { - position: reentry_position, - paths: external_paths(), - } - .to_platform_input(), - cx, - ); - assert!(cx - .active_drag - .as_ref() - .is_some_and(|drag| drag.value.downcast_ref::().is_some())); - window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx); - assert!(cx.active_drag.is_none()); - }); - assert!( - update_result.is_ok(), - "failed to handle drag in destination window: {update_result:?}" - ); - - let update_result = cx.update_window(successful.window, |_, window, cx| { - window.dispatch_event( - FileDropEvent::Entered { - position: reentry_position, - paths: external_paths(), - } - .to_platform_input(), - cx, - ); - assert!(cx - .active_drag - .as_ref() - .is_some_and(|drag| drag.value.downcast_ref::().is_some())); - assert_eq!( - successful.observed_drag_moves.borrow().last(), - Some(&reentry_position) - ); - - window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx); - assert!(cx.active_drag.is_none()); - - window.dispatch_event( - FileDropEvent::Entered { - position: reentry_position, - paths: external_paths(), - } - .to_platform_input(), - cx, - ); - assert!(cx - .active_drag - .as_ref() - .is_some_and(|drag| drag.value.downcast_ref::().is_some())); - - window.dispatch_event( - FileDropEvent::Submit { - position: reentry_position, - } - .to_platform_input(), - cx, - ); - assert_eq!( - successful.observed_drops.borrow().as_slice(), - std::slice::from_ref(&successful_path) - ); - assert!(cx.active_drag.is_none()); - - window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx); - assert!(cx.active_drag.is_none()); - window.dispatch_event(FileDropEvent::Ended.to_platform_input(), cx); - assert!(cx.active_drag.is_none()); - - window.dispatch_event( - FileDropEvent::Entered { - position: reentry_position, - paths: external_paths(), - } - .to_platform_input(), - cx, - ); - assert!(cx - .active_drag - .as_ref() - .is_some_and(|drag| drag.value.downcast_ref::().is_some())); - window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx); - }); - assert!( - update_result.is_ok(), - "failed to restore drag in source window: {update_result:?}" - ); - - let cancelled_path = PathBuf::from("/tmp/cancelled-drag"); - let cancelled = start_drag(cx, cancelled_path.clone(), true); - let update_result = cx.update_window(cancelled.window, |_, window, cx| { - window.dispatch_event( - MouseMoveEvent { - position: outside_position, - pressed_button: Some(MouseButton::Left), - modifiers: Default::default(), - } - .to_platform_input(), - cx, - ); - assert!(cx.active_drag.is_none()); - - window.dispatch_event( - FileDropEvent::Entered { - position: reentry_position, - paths: ExternalPaths([cancelled_path].into_iter().collect()), - } - .to_platform_input(), - cx, - ); - assert!(cx - .active_drag - .as_ref() - .is_some_and(|drag| drag.value.downcast_ref::().is_some())); - assert!(cx.stop_active_drag(window)); - assert!(cx.active_drag.is_none()); - }); - assert!( - update_result.is_ok(), - "failed to cancel restored drag: {update_result:?}" - ); - assert!(!cx.update(|cx| cx.end_platform_drag(cancelled.window.window_id()))); - - let removed_path = PathBuf::from("/tmp/removed-window-drag"); - let removed = start_drag(cx, removed_path, true); - let removed_window_id = removed.window.window_id(); - let update_result = cx.update_window(removed.window, |_, window, cx| { - window.dispatch_event( - MouseMoveEvent { - position: outside_position, - pressed_button: Some(MouseButton::Left), - modifiers: Default::default(), - } - .to_platform_input(), - cx, - ); - assert!(cx.active_drag.is_none()); - window.remove_window(); - }); - assert!( - update_result.is_ok(), - "failed to remove drag source window: {update_result:?}" - ); - assert!(!cx.update(|cx| cx.end_platform_drag(removed_window_id))); - - let failed_path = PathBuf::from("/tmp/failed-drag"); - let failed = start_drag(cx, failed_path.clone(), false); - let update_result = cx.update_window(failed.window, |_, window, cx| { - for x_position in [-1., -2.] { - window.dispatch_event( - MouseMoveEvent { - position: point(px(x_position), px(20.)), - pressed_button: Some(MouseButton::Left), - modifiers: Default::default(), - } - .to_platform_input(), - cx, - ); - } - assert!(cx.active_drag.is_some()); - }); - assert!( - update_result.is_ok(), - "failed to retain drag after platform failure: {update_result:?}" - ); - assert_eq!( - cx.test_window(failed.window).external_drag_files(), - [(failed_path, true)] - ); - } - - struct FocusForwarder { - a: FocusHandle, - b: FocusHandle, - } - - impl Render for FocusForwarder { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - div() - .size_full() - .child(div().w(px(50.)).h(px(50.)).track_focus(&self.a)) - .child(div().w(px(50.)).h(px(50.)).track_focus(&self.b)) - } - } - - /// When a focus listener moves focus again (e.g. a dock forwarding focus to its - /// active panel), the resulting focus events must be dispatched without waiting - /// for an unrelated redraw of the window. - #[gpui::test] - fn test_focus_moved_by_focus_listener_is_dispatched(cx: &mut TestAppContext) { - let b_focus_count = Rc::new(Cell::new(0)); - let window = cx.add_window({ - let b_focus_count = b_focus_count.clone(); - move |window, cx| { - let a = cx.focus_handle(); - let b = cx.focus_handle(); - cx.on_focus(&a, window, |this: &mut FocusForwarder, window, cx| { - let b = this.b.clone(); - window.focus(&b, cx); - }) - .detach(); - cx.on_focus(&b, window, move |_, _, _| { - b_focus_count.set(b_focus_count.get() + 1); - }) - .detach(); - FocusForwarder { a, b } - } - }); - - window - .update(cx, |_, window, _| window.activate_window()) - .unwrap(); - cx.executor().run_until_parked(); - - window - .update(cx, |this, window, cx| { - let a = this.a.clone(); - window.focus(&a, cx); - }) - .unwrap(); - cx.executor().run_until_parked(); - - window - .update(cx, |this, window, _| { - assert!(this.b.is_focused(window)); - }) - .unwrap(); - assert_eq!(b_focus_count.get(), 1); - } - - #[gpui::test] - fn claimed_touch_drag_receives_movement_and_release(cx: &mut TestAppContext) { - let events = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let events = events.clone(); - move |_, _| TouchDragListener { events } - }); - let touch = TouchId(1); - - dispatch_touch(window, cx, touch, TouchPhase::Started, 10.); - dispatch_touch(window, cx, touch, TouchPhase::Moved, 30.); - dispatch_touch(window, cx, touch, TouchPhase::Ended, 40.); - - assert_eq!( - events.borrow().as_slice(), - [ - (TouchPhase::Started, px(10.)), - (TouchPhase::Moved, px(30.)), - (TouchPhase::Ended, px(40.)), - ] - ); - } - - struct TouchDragListener { - events: Rc>>, - } - - impl Render for TouchDragListener { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let events = self.events.clone(); - canvas( - |_, _, _| {}, - move |_, _, window, _| { - window.on_mouse_event(move |event: &TouchDragEvent, phase, window, _cx| { - if phase != DispatchPhase::Bubble { - return; - } - events.borrow_mut().push((event.phase, event.position.x)); - if event.phase == TouchPhase::Started { - window.prevent_default(); - } - }); - }, - ) - } - } - - #[gpui::test] - fn long_press_is_claimed_only_when_started_prevents_default(cx: &mut TestAppContext) { - for response in [ - LongPressResponse::PreventDefault, - LongPressResponse::StopPropagation, - LongPressResponse::None, - ] { - let phases = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let phases = phases.clone(); - move |_, _| LongPressListener { phases, response } - }); - dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.); - cx.executor().advance_clock(Duration::from_millis(501)); - cx.executor().run_until_parked(); - window - .update(cx, |_, window, _| { - assert_eq!( - window.long_press_capture.is_some(), - response == LongPressResponse::PreventDefault - ); - }) - .unwrap(); - dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.); - dispatch_touch(window, cx, TouchId(1), TouchPhase::Ended, 2.); - window - .update(cx, |_, window, _| { - assert!(window.long_press_capture.is_none()); - }) - .unwrap(); - - let phases = phases.borrow(); - if response == LongPressResponse::PreventDefault { - assert_eq!( - phases.as_slice(), - [TouchPhase::Started, TouchPhase::Moved, TouchPhase::Ended] - ); - } else { - assert_eq!(phases.as_slice(), [TouchPhase::Started]); - } - } - } - - #[gpui::test] - fn stale_default_prevention_does_not_claim_long_press(cx: &mut TestAppContext) { - let phases = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let phases = phases.clone(); - move |_, _| LongPressListener { - phases, - response: LongPressResponse::None, - } - }); - window - .update(cx, |_, window, _| { - window.prevent_default(); - }) - .unwrap(); - - dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.); - cx.executor().advance_clock(Duration::from_millis(501)); - cx.executor().run_until_parked(); - dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.); - - assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]); - } - - #[gpui::test] - fn resolved_touch_cancels_scheduled_long_press(cx: &mut TestAppContext) { - for (phase, position) in [ - (TouchPhase::Ended, 0.), - (TouchPhase::Cancelled, 0.), - (TouchPhase::Moved, 20.), - ] { - let phases = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let phases = phases.clone(); - move |_, _| LongPressListener { - phases, - response: LongPressResponse::PreventDefault, - } - }); - dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.); - dispatch_touch(window, cx, TouchId(1), phase, position); - cx.executor().advance_clock(Duration::from_millis(501)); - cx.executor().run_until_parked(); - - assert!(phases.borrow().is_empty(), "{phase:?} allowed long press"); - } - } - - #[gpui::test] - fn stale_long_press_timer_cannot_affect_replacement_touch(cx: &mut TestAppContext) { - let phases = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let phases = phases.clone(); - move |_, _| LongPressListener { - phases, - response: LongPressResponse::PreventDefault, - } - }); - let first_touch = TouchId(1); - dispatch_touch(window, cx, first_touch, TouchPhase::Started, 0.); - cx.executor().advance_clock(Duration::from_millis(250)); - dispatch_touch(window, cx, first_touch, TouchPhase::Cancelled, 0.); - dispatch_touch(window, cx, TouchId(2), TouchPhase::Started, 10.); - - cx.executor().advance_clock(Duration::from_millis(251)); - cx.executor().run_until_parked(); - assert!(phases.borrow().is_empty()); - - cx.executor().advance_clock(Duration::from_millis(250)); - cx.executor().run_until_parked(); - assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]); - } - - #[derive(Clone, Copy, PartialEq)] - enum LongPressResponse { - PreventDefault, - StopPropagation, - None, - } - - struct LongPressListener { - phases: Rc>>, - response: LongPressResponse, - } - - impl Render for LongPressListener { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let entity = cx.entity(); - let phases = self.phases.clone(); - let response = self.response; - canvas( - |_, _, _| {}, - move |_, _, window, _| { - window.on_mouse_event(move |event: &LongPressEvent, phase, window, cx| { - if phase != DispatchPhase::Bubble { - return; - } - phases.borrow_mut().push(event.phase); - match response { - LongPressResponse::PreventDefault => { - window.capture_long_press(&entity); - window.prevent_default(); - } - LongPressResponse::StopPropagation => cx.stop_propagation(), - LongPressResponse::None => {} - } - }); - }, - ) - } - } - - fn dispatch_touch( - window: crate::WindowHandle, - cx: &mut TestAppContext, - id: TouchId, - phase: TouchPhase, - x: f32, - ) { - window - .update(cx, |_, window, cx| { - window.dispatch_event( - TouchEvent { - id, - phase, - position: point(px(x), px(0.)), - predicted_position: None, - force: None, - } - .to_platform_input(), - cx, - ); - }) - .unwrap(); - } -} diff --git a/crates/gpui_pre_wgpu/Cargo.lock b/crates/gpui_pre_wgpu/Cargo.lock deleted file mode 100644 index f9cd39b0..00000000 --- a/crates/gpui_pre_wgpu/Cargo.lock +++ /dev/null @@ -1,4715 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "accesskit" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b7f7f85a7e5f68090000ed7622545829afd484d210358702ae4cb97dd0c320" -dependencies = [ - "enumn", - "uuid", -] - -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "aligned" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" -dependencies = [ - "as-slice", -] - -[[package]] -name = "aligned-vec" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" -dependencies = [ - "equator", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" - -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" - -[[package]] -name = "arg_enum_proc_macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "as-slice" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "ash" -version = "0.38.0+1.3.281" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" -dependencies = [ - "libloading", -] - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-compression" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a89bce6054c720275ac2432fbba080a66a2106a44a1b804553930ca6909f4e0" -dependencies = [ - "compression-codecs", - "compression-core", - "futures-core", - "futures-io", - "pin-project-lite", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "av-scenechange" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" -dependencies = [ - "aligned", - "anyhow", - "arg_enum_proc_macro", - "arrayvec", - "log", - "num-rational", - "num-traits", - "pastey", - "rayon", - "thiserror 2.0.17", - "v_frame", - "y4m", -] - -[[package]] -name = "av1-grain" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags 2.13.1", - "cexpr", - "clang-sys", - "itertools 0.11.0", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 2.1.1", - "shlex 1.3.0", - "syn 2.0.117", -] - -[[package]] -name = "bit-set" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" - -[[package]] -name = "bit_field" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" - -[[package]] -name = "bitstream-io" -version = "4.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" -dependencies = [ - "core2", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block2" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" -dependencies = [ - "objc2", -] - -[[package]] -name = "borsh" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" -dependencies = [ - "cfg_aliases", -] - -[[package]] -name = "built" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytemuck" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "bzip2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" -dependencies = [ - "libbz2-rs-sys", -] - -[[package]] -name = "cc" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex 2.0.1", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "cgl" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" -dependencies = [ - "libc", -] - -[[package]] -name = "chrono" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "codespan-reporting" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7a06c0b31fff5ff2e1e7d37dbf940864e2a974b336e1a2938d10af6e8fb283" -dependencies = [ - "serde", - "termcolor", - "unicode-width", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "compression-codecs" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8a506ec4b81c460798f572caead636d57d3d7e940f998160f52bd254bf2d23" -dependencies = [ - "bzip2", - "compression-core", - "flate2", - "memchr", -] - -[[package]] -name = "compression-core" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "core-graphics" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" -dependencies = [ - "bitflags 2.13.1", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" -dependencies = [ - "bitflags 2.13.1", - "core-foundation", - "libc", -] - -[[package]] -name = "core-graphics2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4416167a69126e617f8d0a214af0e3c1dbdeffcb100ddf72dcd1a1ac9893c146" -dependencies = [ - "bitflags 2.13.1", - "block", - "cfg-if", - "core-foundation", - "libc", -] - -[[package]] -name = "core-text" -version = "21.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a593227b66cbd4007b2a050dfdd9e1d1318311409c8d600dc82ba1b15ca9c130" -dependencies = [ - "core-foundation", - "core-graphics", - "foreign-types", - "libc", -] - -[[package]] -name = "core-video" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139679cc63eb9504bdbe37e37874b0247136177655f0008588781e90863afa62" -dependencies = [ - "block", - "core-foundation", - "core-graphics2", - "io-surface", - "libc", - "metal", -] - -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - -[[package]] -name = "core_maths" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" -dependencies = [ - "libm", -] - -[[package]] -name = "cosmic-text" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73" -dependencies = [ - "bitflags 2.13.1", - "fontdb", - "harfrust", - "linebender_resource_handle", - "log", - "rangemap", - "rustc-hash 2.1.1", - "self_cell", - "skrifa 0.40.0", - "smol_str", - "swash", - "sys-locale", - "unicode-bidi", - "unicode-linebreak", - "unicode-script", - "unicode-segmentation", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - -[[package]] -name = "ctor" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d83cb7e7a873830708d6b02a78cd36a592c6fa14bf267b68725103b85c0d77f" -dependencies = [ - "link-section", - "linktime-proc-macro", -] - -[[package]] -name = "data-url" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" - -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.117", - "unicode-xid", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dispatch2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" -dependencies = [ - "bitflags 2.13.1", - "objc2", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "dlib" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" -dependencies = [ - "libloading", -] - -[[package]] -name = "document-features" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" -dependencies = [ - "litrs", -] - -[[package]] -name = "dwrote" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b35532432acc8b19ceed096e35dfa088d3ea037fe4f3c085f1f97f33b4d02" -dependencies = [ - "lazy_static", - "libc", - "winapi", - "wio", -] - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "enumn" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "equator" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" -dependencies = [ - "equator-macro", -] - -[[package]] -name = "equator-macro" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "erased-serde" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" -dependencies = [ - "serde", - "serde_core", - "typeid", -] - -[[package]] -name = "etagere" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" -dependencies = [ - "euclid", - "svg_fmt", -] - -[[package]] -name = "euclid" -version = "0.22.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" -dependencies = [ - "num-traits", -] - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - -[[package]] -name = "exr" -version = "1.74.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" -dependencies = [ - "bit_field", - "half", - "lebe", - "miniz_oxide", - "rayon-core", - "smallvec", - "zune-inflate", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "fax" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" -dependencies = [ - "fax_derive", -] - -[[package]] -name = "fax_derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" - -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - -[[package]] -name = "flate2" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" - -[[package]] -name = "float-ord" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" - -[[package]] -name = "float_next_after" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" - -[[package]] -name = "flume" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" -dependencies = [ - "fastrand", - "futures-core", - "futures-sink", - "spin 0.9.8", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - -[[package]] -name = "font-types" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "511e2c18a516c666d27867d2f9821f76e7d591f762e9fc41dd6cc5c90fe54b0b" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "font-types" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4d2d0cf79d38430cc9dc9aadec84774bff2e1ba30ae2bf6c16cfce9385a23" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "fontconfig-parser" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" -dependencies = [ - "roxmltree 0.20.0", -] - -[[package]] -name = "fontdb" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" -dependencies = [ - "fontconfig-parser", - "log", - "memmap2", - "slotmap", - "tinyvec", - "ttf-parser", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "freetype-sys" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-concurrency" -version = "7.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175cd8cca9e1d45b87f18ffa75088f2099e3c4fe5e2f83e42de112560bea8ea6" -dependencies = [ - "fixedbitset", - "futures-core", - "futures-lite", - "pin-project", - "smallvec", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "gif" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" -dependencies = [ - "color_quant", - "weezl", -] - -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - -[[package]] -name = "gl_generator" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" -dependencies = [ - "khronos_api", - "log", - "xml-rs", -] - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "glow" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29038e1c483364cc6bb3cf78feee1816002e127c331a1eec55a4d202b9e1adb5" -dependencies = [ - "js-sys", - "slotmap", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "glutin_wgl_sys" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" -dependencies = [ - "gl_generator", -] - -[[package]] -name = "gpu-allocator" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" -dependencies = [ - "ash", - "hashbrown 0.16.1", - "log", - "presser", - "thiserror 2.0.17", - "windows", -] - -[[package]] -name = "gpu-descriptor" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" -dependencies = [ - "bitflags 2.13.1", - "gpu-descriptor-types", - "hashbrown 0.15.5", -] - -[[package]] -name = "gpu-descriptor-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" -dependencies = [ - "bitflags 2.13.1", -] - -[[package]] -name = "gpui-pre" -version = "0.3.3" -dependencies = [ - "accesskit", - "anyhow", - "async-channel", - "async-task", - "bindgen", - "bitflags 2.13.1", - "chrono", - "core-video", - "ctor", - "derive_more", - "etagere", - "futures", - "futures-concurrency", - "getrandom 0.3.4", - "gpui-pre-collections", - "gpui-pre-http-client", - "gpui-pre-macros", - "gpui-pre-refineable", - "gpui-pre-scheduler", - "gpui-pre-shared-string", - "gpui-pre-sum-tree", - "gpui-pre-util", - "gpui-pre-util-macros", - "gpui-pre-ztracing", - "heapless", - "image", - "inventory", - "itertools 0.14.0", - "log", - "lyon", - "num_cpus", - "parking", - "parking_lot", - "pin-project", - "pollster 0.4.0", - "postage", - "profiling", - "rand", - "raw-window-handle", - "regex", - "resvg", - "schemars", - "seahash", - "serde", - "serde_json", - "slotmap", - "smallvec", - "spin 0.10.0", - "strum", - "taffy", - "thiserror 2.0.17", - "tracing", - "ttf-parser", - "url", - "usvg", - "uuid", - "waker-fn", - "web-time", - "windows", -] - -[[package]] -name = "gpui-pre-collections" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c8efa2e51e368c8538a7be1ea9a12127ca03abe6cc01d9d3474e9ac4f53016" -dependencies = [ - "gpui-pre-util", - "indexmap", - "rustc-hash 2.1.1", -] - -[[package]] -name = "gpui-pre-derive-refineable" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a098d319acc9f84bf159944f96c5ea43a4f4cd7ed759f984acbd15719495aa0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gpui-pre-http-client" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3495a45a28cb800c8626d2053406114bcaca26d390a4d4b183365b5bb8fdaf02" -dependencies = [ - "anyhow", - "async-compression", - "bytes", - "derive_more", - "futures", - "http", - "http-body", - "log", - "parking_lot", - "serde", - "serde_json", - "serde_urlencoded", - "url", -] - -[[package]] -name = "gpui-pre-macros" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be2db7b5097b4d523b2bce933604bcc5acdaf679bb9a150e8299e6c07efc29c" -dependencies = [ - "heck", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gpui-pre-perf" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1db0c046b93c2a29120f8ee4c04bc80a4d7d6117d1164ef349faface8943491" -dependencies = [ - "gpui-pre-collections", - "serde", - "serde_json", -] - -[[package]] -name = "gpui-pre-refineable" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864e2e54a3029481dae5b6aae3ba2905f2fb1dfe66ced913b68c9ff527f8e327" -dependencies = [ - "gpui-pre-derive-refineable", -] - -[[package]] -name = "gpui-pre-scheduler" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b58a78c4e0c032900704ea49922641c534cf8bbf1bf22eda6ba00a76e88c6c3" -dependencies = [ - "async-task", - "backtrace", - "chrono", - "flume", - "futures", - "parking_lot", - "rand", - "web-time", -] - -[[package]] -name = "gpui-pre-shared-string" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fc99fe88e44173758a500522d3f4059a6541da4b471af720e3f60cf34a2bc3" -dependencies = [ - "schemars", - "serde", - "smol_str", -] - -[[package]] -name = "gpui-pre-sum-tree" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002baed852f20cef1188d3d5e0f749025fc718e4d6cea026b9077a9a6c10d042" -dependencies = [ - "gpui-pre-ztracing", - "heapless", - "log", - "rayon", - "tracing", -] - -[[package]] -name = "gpui-pre-util" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe779c4cb00929aafcb2b307cd1240588aebb5d1eb328c59b80f77c05a41fad" -dependencies = [ - "anyhow", - "log", - "which", -] - -[[package]] -name = "gpui-pre-util-macros" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f0ccc4bccb6a31786095d15fc9f40d6a4c6a295522e3460331dd7e929ff2f79" -dependencies = [ - "gpui-pre-perf", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gpui-pre-wgpu" -version = "0.3.3" -dependencies = [ - "anyhow", - "bytemuck", - "cosmic-text", - "etagere", - "gpui-pre", - "gpui-pre-collections", - "gpui-pre-util", - "itertools 0.14.0", - "log", - "naga", - "parking_lot", - "profiling", - "raw-window-handle", - "smallvec", - "swash", - "unicode-bidi", - "unicode-segmentation", - "web-sys", - "wgpu", - "zed-font-kit", -] - -[[package]] -name = "gpui-pre-zlog" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1416ea5f018e3a8a1c8be266332c443583f28f8f87379a84ab1e77622173ac0" -dependencies = [ - "anyhow", - "chrono", - "gpui-pre-collections", - "log", -] - -[[package]] -name = "gpui-pre-ztracing" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cc64a25a3cc4e4f1d8acb00074d3925339dae657c020083735738ba8af96bf7" -dependencies = [ - "gpui-pre-zlog", - "gpui-pre-ztracing-macro", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "gpui-pre-ztracing-macro" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3ea75672348f37d94472e579f5979ee19b744d0fa5aaadc2fe129ccafa80b" - -[[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "num-traits", - "zerocopy", -] - -[[package]] -name = "harfrust" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9f40651a03bc0f7316bd75267ff5767e93017ef3cfffe76c6aa7252cc5a31c" -dependencies = [ - "bitflags 2.13.1", - "bytemuck", - "core_maths", - "read-fonts 0.37.0", - "smallvec", -] - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash 0.1.5", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash 0.2.0", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "heapless" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" -dependencies = [ - "hash32", - "stable_deref_trait", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hexf-parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" - -[[package]] -name = "http" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" - -[[package]] -name = "icu_properties" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" - -[[package]] -name = "icu_provider" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "image" -version = "0.25.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" -dependencies = [ - "bytemuck", - "byteorder-lite", - "color_quant", - "exr", - "gif", - "image-webp", - "moxcms", - "num-traits", - "png 0.18.0", - "qoi", - "ravif", - "rayon", - "tiff", - "zune-core", - "zune-jpeg", -] - -[[package]] -name = "image-webp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" -dependencies = [ - "byteorder-lite", - "quick-error", -] - -[[package]] -name = "imagesize" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" - -[[package]] -name = "imgref" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", -] - -[[package]] -name = "interpolate_name" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "inventory" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" -dependencies = [ - "rustversion", -] - -[[package]] -name = "io-surface" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "554b8c5d64ec09a3a520fe58e4d48a73e00ff32899cdcbe32a4877afd4968b8e" -dependencies = [ - "cgl", - "core-foundation", - "core-foundation-sys", - "leaky-cow", -] - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" -dependencies = [ - "cfg-if", - "futures-util", - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "khronos-egl" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" -dependencies = [ - "libc", - "libloading", - "pkg-config", -] - -[[package]] -name = "khronos_api" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" - -[[package]] -name = "kurbo" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b60dfc32f652b926df6192e55525b16d186c69d47876c3ead4da5cc9f8450e2" -dependencies = [ - "arrayvec", - "euclid", - "polycool", - "smallvec", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leak" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd100e01f1154f2908dfa7d02219aeab25d0b9c7fa955164192e3245255a0c73" - -[[package]] -name = "leaky-cow" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a8225d44241fd324a8af2806ba635fc7c8a7e9a7de4d5cf3ef54e71f5926fc" -dependencies = [ - "leak", -] - -[[package]] -name = "lebe" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" - -[[package]] -name = "libbz2-rs-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "libfuzzer-sys" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" -dependencies = [ - "arbitrary", - "cc", -] - -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link", -] - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "libredox" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" -dependencies = [ - "bitflags 2.13.1", - "libc", -] - -[[package]] -name = "linebender_resource_handle" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4" - -[[package]] -name = "link-section" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee1a0d6e252afe82e7bc2db42fba60e02ddf3b1accaf8cb21d96e34ba61f3d4" - -[[package]] -name = "linktime-proc-macro" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348d0075b1fc163b26d72a7f75fc5141daf2fd1bdf128d873cbaf6785d495bdf" - -[[package]] -name = "litemap" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" - -[[package]] -name = "litrs" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -dependencies = [ - "serde_core", - "value-bag", -] - -[[package]] -name = "loop9" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" -dependencies = [ - "imgref", -] - -[[package]] -name = "lyon" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcb7d54d54c8937364c9d41902d066656817dce1e03a44e5533afebd1ef4352" -dependencies = [ - "lyon_algorithms", - "lyon_tessellation", -] - -[[package]] -name = "lyon_algorithms" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c0829e28c4f336396f250d850c3987e16ce6db057ffe047ce0dd54aab6b647" -dependencies = [ - "lyon_path", - "num-traits", -] - -[[package]] -name = "lyon_geom" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e16770d760c7848b0c1c2d209101e408207a65168109509f8483837a36cf2e7" -dependencies = [ - "arrayvec", - "euclid", - "num-traits", -] - -[[package]] -name = "lyon_path" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aeca86bcfd632a15984ba029b539ffb811e0a70bf55e814ef8b0f54f506fdeb" -dependencies = [ - "lyon_geom", - "num-traits", -] - -[[package]] -name = "lyon_tessellation" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f586142e1280335b1bc89539f7c97dd80f08fc43e9ab1b74ef0a42b04aa353" -dependencies = [ - "float_next_after", - "lyon_path", - "num-traits", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "maybe-rayon" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" -dependencies = [ - "cfg-if", - "rayon", -] - -[[package]] -name = "memchr" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" - -[[package]] -name = "memmap2" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" -dependencies = [ - "libc", -] - -[[package]] -name = "metal" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" -dependencies = [ - "bitflags 2.13.1", - "block", - "core-graphics-types", - "foreign-types", - "log", - "objc", - "paste", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "moxcms" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" -dependencies = [ - "num-traits", - "pxfm", -] - -[[package]] -name = "naga" -version = "29.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2bf919621e7975acb27d881bae2fb993e0d45c8e0446e85e6272971e00dc8df" -dependencies = [ - "arrayvec", - "bit-set", - "bitflags 2.13.1", - "cfg-if", - "cfg_aliases", - "codespan-reporting", - "half", - "hashbrown 0.16.1", - "hexf-parse", - "indexmap", - "libm", - "log", - "num-traits", - "once_cell", - "rustc-hash 1.1.0", - "spirv", - "thiserror 2.0.17", - "unicode-ident", -] - -[[package]] -name = "ndk-sys" -version = "0.6.0+11769913" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" -dependencies = [ - "jni-sys", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-bigint" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", -] - -[[package]] -name = "objc2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" -dependencies = [ - "objc2-encode", -] - -[[package]] -name = "objc2-core-foundation" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" -dependencies = [ - "bitflags 2.13.1", - "dispatch2", - "objc2", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - -[[package]] -name = "objc2-foundation" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" -dependencies = [ - "bitflags 2.13.1", - "objc2", - "objc2-core-foundation", -] - -[[package]] -name = "objc2-metal" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" -dependencies = [ - "bitflags 2.13.1", - "block2", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-quartz-core" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" -dependencies = [ - "bitflags 2.13.1", - "objc2", - "objc2-core-foundation", - "objc2-foundation", - "objc2-metal", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "ordered-float" -version = "5.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c9e0d9b23589f26070720bac724174bfec1083e82f7854cdd0267518343c0" -dependencies = [ - "num-traits", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pastey" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" - -[[package]] -name = "pathfinder_geometry" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" -dependencies = [ - "log", - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_simd" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4500030c302e4af1d423f36f3b958d1aecb6c04184356ed5a833bf6b60435777" -dependencies = [ - "rustc_version", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - -[[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "png" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" -dependencies = [ - "bitflags 2.13.1", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "pollster" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" - -[[package]] -name = "pollster" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" - -[[package]] -name = "polycool" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50596ddc09eb5ad5f75cacd40209568e66df71baf86e1499a0e99c4cff12a5a6" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "portable-atomic" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" - -[[package]] -name = "portable-atomic-util" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "postage" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" -dependencies = [ - "atomic", - "crossbeam-queue", - "futures", - "log", - "parking_lot", - "pin-project", - "pollster 0.2.5", - "static_assertions", - "thiserror 1.0.69", -] - -[[package]] -name = "potential_utf" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" -dependencies = [ - "zerovec", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "presser" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - -[[package]] -name = "proc-macro-crate" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" -dependencies = [ - "toml_edit", -] - -[[package]] -name = "proc-macro2" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "profiling" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" -dependencies = [ - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pxfm" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cbdf373972bf78df4d3b518d07003938e2c7d1fb5891e55f9cb6df57009d84" -dependencies = [ - "num-traits", -] - -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quote" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "range-alloc" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" - -[[package]] -name = "rangemap" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" - -[[package]] -name = "rav1e" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" -dependencies = [ - "aligned-vec", - "arbitrary", - "arg_enum_proc_macro", - "arrayvec", - "av-scenechange", - "av1-grain", - "bitstream-io", - "built", - "cfg-if", - "interpolate_name", - "itertools 0.14.0", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive", - "num-traits", - "paste", - "profiling", - "rand", - "rand_chacha", - "simd_helpers", - "thiserror 2.0.17", - "v_frame", - "wasm-bindgen", -] - -[[package]] -name = "ravif" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" -dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error", - "rav1e", - "rayon", - "rgb", -] - -[[package]] -name = "raw-window-handle" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" - -[[package]] -name = "raw-window-metal" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" -dependencies = [ - "objc2", - "objc2-core-foundation", - "objc2-foundation", - "objc2-quartz-core", -] - -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "read-fonts" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717cf23b488adf64b9d711329542ba34de147df262370221940dfabc2c91358" -dependencies = [ - "bytemuck", - "font-types 0.10.0", -] - -[[package]] -name = "read-fonts" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5" -dependencies = [ - "bytemuck", - "core_maths", - "font-types 0.11.0", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.13.1", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror 1.0.69", -] - -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" - -[[package]] -name = "renderdoc-sys" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" - -[[package]] -name = "resvg" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b563218631706d614e23059436526d005b50ab5f2d506b55a17eb65c5eb83419" -dependencies = [ - "gif", - "image-webp", - "log", - "pico-args", - "rgb", - "svgtypes", - "tiny-skia", - "usvg", - "zune-jpeg", -] - -[[package]] -name = "rgb" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "roxmltree" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" - -[[package]] -name = "roxmltree" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb" -dependencies = [ - "memchr", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "rustybuzz" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" -dependencies = [ - "bitflags 2.13.1", - "bytemuck", - "core_maths", - "log", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schemars" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" -dependencies = [ - "dyn-clone", - "indexmap", - "ref-cast", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.117", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "self_cell" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_fmt" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_json" -version = "1.0.151" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" -dependencies = [ - "indexmap", - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "simd_helpers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" -dependencies = [ - "quote", -] - -[[package]] -name = "simplecss" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" -dependencies = [ - "log", -] - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "skrifa" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c31071dedf532758ecf3fed987cdb4bd9509f900e026ab684b4ecb81ea49841" -dependencies = [ - "bytemuck", - "read-fonts 0.35.0", -] - -[[package]] -name = "skrifa" -version = "0.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac" -dependencies = [ - "bytemuck", - "read-fonts 0.37.0", -] - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "slotmap" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" -dependencies = [ - "version_check", -] - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "smol_str" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aaa7368fcf4852a4c2dd92df0cace6a71f2091ca0a23391ce7f3a31833f1523" -dependencies = [ - "borsh", - "serde_core", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spin" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spirv" -version = "0.4.0+sdk-1.4.341.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f" -dependencies = [ - "bitflags 2.13.1", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -dependencies = [ - "float-cmp", -] - -[[package]] -name = "strum" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sval" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94c4464e595f0284970fd9c7e9013804d035d4a61ab74b113242c874c05814d" - -[[package]] -name = "sval_buffer" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0f46e34b20a39e6a2bf02b926983149b3af6609fd1ee8a6e63f6f340f3e2164" -dependencies = [ - "sval", - "sval_ref", -] - -[[package]] -name = "sval_dynamic" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d0970e53c92ab5381d3b2db1828da8af945954d4234225f6dd9c3afbcef3f5" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_fmt" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e5e6e1613e1e7fc2e1a9fdd709622e54c122ceb067a60d170d75efd491a839" -dependencies = [ - "itoa", - "ryu", - "sval", -] - -[[package]] -name = "sval_json" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aec382f7bfa6e367b23c9611f129b94eb7daaf3d8fae45a8d0a0211eb4d4c8e6" -dependencies = [ - "itoa", - "ryu", - "sval", -] - -[[package]] -name = "sval_nested" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3049d0f99ce6297f8f7d9953b35a0103b7584d8f638de40e64edb7105fa578ae" -dependencies = [ - "sval", - "sval_buffer", - "sval_ref", -] - -[[package]] -name = "sval_ref" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88913e77506085c0a8bf6912bb6558591a960faf5317df6c1d9b227224ca6e1" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_serde" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f579fd7254f4be6cd7b450034f856b78523404655848789c451bacc6aa8b387d" -dependencies = [ - "serde_core", - "sval", - "sval_nested", -] - -[[package]] -name = "svg_fmt" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" - -[[package]] -name = "svgtypes" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "695b5790b3131dafa99b3bbfd25a216edb3d216dad9ca208d4657bfb8f2abc3d" -dependencies = [ - "kurbo", - "siphasher", -] - -[[package]] -name = "swash" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47846491253e976bdd07d0f9cc24b7daf24720d11309302ccbbc6e6b6e53550a" -dependencies = [ - "skrifa 0.37.0", - "yazi", - "zeno", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sys-locale" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" -dependencies = [ - "libc", -] - -[[package]] -name = "taffy" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c034e05f6ee85a12daa63863c2245797715075c70649947aa0da54f3f2ab1d0f" -dependencies = [ - "arrayvec", - "serde", - "slotmap", - "smallvec", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl 2.0.17", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "tiff" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" -dependencies = [ - "fax", - "flate2", - "half", - "quick-error", - "weezl", - "zune-jpeg", -] - -[[package]] -name = "tiny-skia" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if", - "log", - "png 0.17.16", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - -[[package]] -name = "tinystr" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "toml_datetime" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" -dependencies = [ - "indexmap", - "toml_datetime", - "toml_parser", - "winnow", -] - -[[package]] -name = "toml_parser" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" -dependencies = [ - "winnow", -] - -[[package]] -name = "tracing" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tracing-core" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "ttf-parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -dependencies = [ - "core_maths", -] - -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - -[[package]] -name = "unicode-bidi-mirroring" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" - -[[package]] -name = "unicode-ccc" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - -[[package]] -name = "unicode-properties" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" - -[[package]] -name = "unicode-script" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" - -[[package]] -name = "unicode-segmentation" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" - -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - -[[package]] -name = "unicode-width" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "url" -version = "2.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "usvg" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e419dff010bb12512b0ae9e3d2f318dfbdf0167fde7eb05465134d4e8756076f" -dependencies = [ - "base64", - "data-url", - "flate2", - "fontdb", - "imagesize", - "kurbo", - "log", - "pico-args", - "roxmltree 0.21.1", - "rustybuzz", - "simplecss", - "siphasher", - "strict-num", - "svgtypes", - "tiny-skia-path", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" -dependencies = [ - "getrandom 0.3.4", - "js-sys", - "serde", - "sha1_smol", - "wasm-bindgen", -] - -[[package]] -name = "v_frame" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "value-bag" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" -dependencies = [ - "value-bag-serde1", - "value-bag-sval2", -] - -[[package]] -name = "value-bag-serde1" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16530907bfe2999a1773ca5900a65101e092c70f642f25cc23ca0c43573262c5" -dependencies = [ - "erased-serde", - "serde_core", - "serde_fmt", -] - -[[package]] -name = "value-bag-sval2" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00ae130edd690eaa877e4f40605d534790d1cf1d651e7685bd6a144521b251f" -dependencies = [ - "sval", - "sval_buffer", - "sval_dynamic", - "sval_fmt", - "sval_json", - "sval_ref", - "sval_serde", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.117", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wayland-sys" -version = "0.31.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" -dependencies = [ - "dlib", - "log", - "once_cell", - "pkg-config", -] - -[[package]] -name = "web-sys" -version = "0.3.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "weezl" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" - -[[package]] -name = "wgpu" -version = "29.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e8840e1ba2881d4cbb18d2147627a56af426ff064c0401eb0c8410c6325d07" -dependencies = [ - "arrayvec", - "bitflags 2.13.1", - "bytemuck", - "cfg-if", - "cfg_aliases", - "document-features", - "hashbrown 0.16.1", - "js-sys", - "log", - "naga", - "parking_lot", - "portable-atomic", - "profiling", - "raw-window-handle", - "smallvec", - "static_assertions", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "wgpu-core", - "wgpu-hal", - "wgpu-types", -] - -[[package]] -name = "wgpu-core" -version = "29.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f519832254e56965a9940c4af57dcb75f702b6f6fa4a0b172f685395843a4d7" -dependencies = [ - "arrayvec", - "bit-set", - "bit-vec", - "bitflags 2.13.1", - "bytemuck", - "cfg_aliases", - "document-features", - "hashbrown 0.16.1", - "indexmap", - "log", - "naga", - "once_cell", - "parking_lot", - "portable-atomic", - "profiling", - "raw-window-handle", - "rustc-hash 1.1.0", - "smallvec", - "thiserror 2.0.17", - "wgpu-core-deps-apple", - "wgpu-core-deps-emscripten", - "wgpu-core-deps-wasm", - "wgpu-core-deps-windows-linux-android", - "wgpu-hal", - "wgpu-naga-bridge", - "wgpu-types", -] - -[[package]] -name = "wgpu-core-deps-apple" -version = "29.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e39e26c4c0e07589e67d18546cf79ff45383659fc72fca4dd293358a0347f3" -dependencies = [ - "wgpu-hal", -] - -[[package]] -name = "wgpu-core-deps-emscripten" -version = "29.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e09be551dc939498bdd5f6b2c66e55ab275dad25825267a08605a80fc9f0af" -dependencies = [ - "wgpu-hal", -] - -[[package]] -name = "wgpu-core-deps-wasm" -version = "29.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1fb1798be2a912497d4c224f72d39bb0cb34af50e8bcc29865bc339c943059" -dependencies = [ - "wgpu-hal", -] - -[[package]] -name = "wgpu-core-deps-windows-linux-android" -version = "29.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e592c1bbef6ad047647ae6e666ebd8cee7a32bb4544d9700ec96cbf73230257" -dependencies = [ - "wgpu-hal", -] - -[[package]] -name = "wgpu-hal" -version = "29.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ace1c17727311c22a46e4e3faf56ea6de81af99dcc839bdfb54857b94d448d" -dependencies = [ - "android_system_properties", - "arrayvec", - "ash", - "bit-set", - "bitflags 2.13.1", - "block2", - "bytemuck", - "cfg-if", - "cfg_aliases", - "glow", - "glutin_wgl_sys", - "gpu-allocator", - "gpu-descriptor", - "hashbrown 0.16.1", - "js-sys", - "khronos-egl", - "libc", - "libloading", - "log", - "naga", - "ndk-sys", - "objc2", - "objc2-core-foundation", - "objc2-foundation", - "objc2-metal", - "objc2-quartz-core", - "once_cell", - "ordered-float", - "parking_lot", - "portable-atomic", - "portable-atomic-util", - "profiling", - "range-alloc", - "raw-window-handle", - "raw-window-metal", - "renderdoc-sys", - "smallvec", - "thiserror 2.0.17", - "wasm-bindgen", - "wayland-sys", - "web-sys", - "wgpu-naga-bridge", - "wgpu-types", - "windows", - "windows-core", - "windows-result", -] - -[[package]] -name = "wgpu-naga-bridge" -version = "29.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95226013f547544b223281cd16a4fb549aa9dcb562adbda0faae4c73ffbbc161" -dependencies = [ - "naga", - "wgpu-types", -] - -[[package]] -name = "wgpu-types" -version = "29.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84bf84cd9ca8ca45e2b223a3868f1adf9bfc0c66aeac212e76ee7e40fdadf8f5" -dependencies = [ - "bitflags 2.13.1", - "bytemuck", - "js-sys", - "log", - "raw-window-handle", - "web-sys", -] - -[[package]] -name = "which" -version = "8.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3ef584124b911bcc3875c2f1472e80f24361ceb789bd1c62b3e9a3df9ff43c" -dependencies = [ - "libc", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" -dependencies = [ - "windows-collections", - "windows-core", - "windows-future", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" -dependencies = [ - "windows-core", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-future" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" -dependencies = [ - "windows-core", - "windows-link", - "windows-threading", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-numerics" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" -dependencies = [ - "windows-core", - "windows-link", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows-threading" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "winnow" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] - -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi", -] - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "writeable" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" - -[[package]] -name = "xml-rs" -version = "0.8.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" - -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - -[[package]] -name = "y4m" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" - -[[package]] -name = "yazi" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" - -[[package]] -name = "yeslogic-fontconfig-sys" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503a066b4c037c440169d995b869046827dbc71263f6e8f3be6d77d4f3229dbd" -dependencies = [ - "dlib", - "once_cell", - "pkg-config", -] - -[[package]] -name = "yoke" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zed-font-kit" -version = "0.14.1-zed" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3898e450f36f852edda72e3f985c34426042c4951790b23b107f93394f9bff5" -dependencies = [ - "bitflags 2.13.1", - "byteorder", - "core-foundation", - "core-graphics", - "core-text", - "dirs", - "dwrote", - "float-ord", - "freetype-sys", - "lazy_static", - "libc", - "log", - "pathfinder_geometry", - "pathfinder_simd", - "walkdir", - "winapi", - "yeslogic-fontconfig-sys", -] - -[[package]] -name = "zeno" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" - -[[package]] -name = "zerocopy" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zerotrie" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f212a141d820099d57ffafb9569be9617a6f27d3dc881fbee8fb56642f917a9" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.3", -] - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" - -[[package]] -name = "zune-core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "zune-jpeg" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" -dependencies = [ - "zune-core", -] diff --git a/crates/gpui_pre_wgpu/Cargo.toml b/crates/gpui_pre_wgpu/Cargo.toml deleted file mode 100644 index 0bfc6d76..00000000 --- a/crates/gpui_pre_wgpu/Cargo.toml +++ /dev/null @@ -1,150 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies. -# -# If you are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2024" -name = "gpui-pre-wgpu" -version = "0.3.3" -build = false -publish = true -autolib = false -autobins = false -autoexamples = false -autotests = false -autobenches = false -description = "Zed's `gpui_wgpu` crate (gpui-pre snapshot of zed@5b055fa)" -readme = false -license = "Apache-2.0" -repository = "https://github.com/zed-industries/zed" -resolver = "2" - -[package.metadata.gpui-pre] -zed-crate = "gpui_wgpu" -zed-version = "0.1.0" -zed-rev = "5b055fa789a8b8d38ac951a6e0cde272f66b4495" - -[features] -default = [] -font-kit = ["dep:font-kit"] - -[lib] -name = "gpui_wgpu" -path = "src/gpui_wgpu.rs" - -[[bench]] -name = "layout_line" -path = "benches/layout_line.rs" -harness = false - -[dependencies.anyhow] -version = "1.0.86" - -[dependencies.bytemuck] -version = "1" - -[dependencies.collections] -version = "=0.3.3" -package = "gpui-pre-collections" - -[dependencies.cosmic-text] -version = "0.19.0" - -[dependencies.etagere] -version = "0.2" - -[dependencies.font-kit] -version = "0.14.1-zed" -optional = true -package = "zed-font-kit" - -[dependencies.gpui] -version = "=0.3.3" -default-features = false -package = "gpui-pre" - -[dependencies.gpui_util] -version = "=0.3.3" -package = "gpui-pre-util" - -[dependencies.itertools] -version = "0.14.0" - -[dependencies.log] -version = "0.4.16" -features = [ - "kv_unstable_serde", - "serde", -] - -[dependencies.parking_lot] -version = "0.12.1" - -[dependencies.profiling] -version = "1" - -[dependencies.raw-window-handle] -version = "0.6" - -[dependencies.smallvec] -version = "1.6" -features = [ - "union", - "const_new", -] - -[dependencies.swash] -version = "0.2.6" - -[dependencies.unicode-bidi] -version = "0.3.18" -features = ["hardcoded-data"] -default-features = false - -[dependencies.unicode-segmentation] -version = "1.10" - -[dependencies.wgpu] -version = "29.0.4" - -[dev-dependencies.naga] -version = "29.0.4" -features = ["wgsl-in"] - -[target.'cfg(target_family = "wasm")'.dependencies.web-sys] -version = "0.3" -features = ["HtmlCanvasElement"] - -[target.'cfg(target_family = "wasm")'.dependencies.wgpu] -version = "29.0.4" -features = ["webgl"] - -[lints.clippy] -dbg_macro = "deny" -declare_interior_mutable_const = "deny" -disallowed_methods = "deny" -large_enum_variant = "allow" -let_underscore_future = "allow" -nonminimal_bool = "allow" -redundant_clone = "deny" -single_range_in_vec_init = "allow" -todo = "deny" -too_many_arguments = "allow" -type_complexity = "allow" - -[lints.clippy.style] -level = "allow" -priority = -1 - -[lints.rust.unexpected_cfgs] -level = "allow" -priority = 0 - -[workspace] diff --git a/crates/gpui_pre_wgpu/LICENSE-APACHE b/crates/gpui_pre_wgpu/LICENSE-APACHE deleted file mode 100644 index 461a0fe5..00000000 --- a/crates/gpui_pre_wgpu/LICENSE-APACHE +++ /dev/null @@ -1,222 +0,0 @@ -Copyright 2022 - 2025 Zed Industries, Inc. - - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - - http://www.apache.org/licenses/LICENSE-2.0 - - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - - -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - - 1. Definitions. - - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - - END OF TERMS AND CONDITIONS diff --git a/crates/gpui_pre_wgpu/benches/layout_line.rs b/crates/gpui_pre_wgpu/benches/layout_line.rs deleted file mode 100644 index 6d66a7a2..00000000 --- a/crates/gpui_pre_wgpu/benches/layout_line.rs +++ /dev/null @@ -1,104 +0,0 @@ -use criterion::{Criterion, criterion_group, criterion_main}; -use gpui::{FontFallbacks, FontRun, PlatformTextSystem, font, px}; -use gpui_wgpu::CosmicTextSystem; -use std::borrow::Cow; - -const LILEX: &[u8] = include_bytes!("../../../assets/fonts/lilex/Lilex-Regular.ttf"); -const IBM_PLEX: &[u8] = - include_bytes!("../../../assets/fonts/ibm-plex-sans/IBMPlexSans-Regular.ttf"); - -// ~4 000 chars of typical ASCII code text, as a single display line. -// -// `layout_line` is handed one line at a time, already split on `\n` by its -// callers, so the newlines are replaced rather than kept: leaving them in would -// make this measure the multi-paragraph path instead of the common one, since -// `\n` is itself a bidi paragraph separator. -fn code_text() -> String { - concat!( - " fn compute_run_spans(\n", - " text: &str,\n", - " run_offset: usize,\n", - " run_len: usize,\n", - " primary: FontId,\n", - " fallback_chain: &[(FontId, SharedString)],\n", - " covers: &impl Fn(FontId, char) -> bool,\n", - " ) -> SmallVec<[RunSpan; 4]> {\n", - " let mut spans = SmallVec::new();\n", - " let run_end = run_offset + run_len;\n", - " if run_end <= run_offset { return spans; }\n", - " let run_text = &text[run_offset..run_end];\n", - " let mut span_start = run_offset;\n", - " let mut span_slot: Option = None;\n", - " for (ch_idx, ch) in run_text.char_indices() {\n", - " let abs = run_offset + ch_idx;\n", - " let next = pick_covering_slot(ch, span_slot, primary, fallback_chain, covers);\n", - " if next == span_slot { continue; }\n", - " if abs > span_start {\n", - " spans.push(RunSpan { start: span_start, end: abs, slot: span_slot });\n", - " }\n", - " span_start = abs;\n", - " span_slot = next;\n", - " }\n", - " spans\n", - " }\n", - ) - .repeat(8) // ~3 800 chars - .replace('\n', " ") -} - -fn bench_layout_line(c: &mut Criterion) { - let system = CosmicTextSystem::new_without_system_fonts("Lilex"); - system - .add_fonts(vec![Cow::Borrowed(LILEX), Cow::Borrowed(IBM_PLEX)]) - .unwrap(); - - let font_id_no_fallback = system.font_id(&font("Lilex")).unwrap(); - - let font_id_with_fallback = { - let mut f = font("Lilex"); - f.fallbacks = Some(FontFallbacks::from_fonts(vec!["IBM Plex Sans".to_string()])); - system.font_id(&f).unwrap() - }; - - let text = code_text(); - - // Same text, but with a bidi paragraph separator (U+001C) and RTL content - // forcing the per-paragraph shaping path. - let text_mixed_direction = text.clone() + "\u{001c}\u{05d0}\u{05d1}"; - assert!( - !text.contains('\n'), - "fast-path corpus must contain no separator" - ); - - let runs_no_fallback = vec![FontRun { - len: text.len(), - font_id: font_id_no_fallback, - }]; - let runs_with_fallback = vec![FontRun { - len: text.len(), - font_id: font_id_with_fallback, - }]; - let runs_mixed_direction = vec![FontRun { - len: text_mixed_direction.len(), - font_id: font_id_no_fallback, - }]; - - let mut group = c.benchmark_group("layout_line"); - - group.bench_function("no_fallback", |b| { - b.iter(|| system.layout_line(&text, px(14.0), &runs_no_fallback)) - }); - - group.bench_function("with_fallback_ascii", |b| { - b.iter(|| system.layout_line(&text, px(14.0), &runs_with_fallback)) - }); - - group.bench_function("mixed_direction_paragraphs", |b| { - b.iter(|| system.layout_line(&text_mixed_direction, px(14.0), &runs_mixed_direction)) - }); - - group.finish(); -} - -criterion_group!(benches, bench_layout_line); -criterion_main!(benches); diff --git a/crates/gpui_pre_wgpu/src/cosmic_text_system.rs b/crates/gpui_pre_wgpu/src/cosmic_text_system.rs deleted file mode 100644 index e84d9241..00000000 --- a/crates/gpui_pre_wgpu/src/cosmic_text_system.rs +++ /dev/null @@ -1,1454 +0,0 @@ -use anyhow::{Context as _, Ok, Result}; -use collections::HashMap; -use cosmic_text::{ - Attrs, AttrsList, Ellipsize, Family, Font as CosmicTextFont, - FontFeatures as CosmicFontFeatures, FontSystem, ShapeBuffer, ShapeLine, Stretch, Style, Weight, -}; -use gpui::{ - Bounds, DevicePixels, Font, FontFallbacks, FontFeatures, FontId, FontMetrics, FontRun, GlyphId, - IsZero as _, LineLayout, Pixels, PlatformTextSystem, RenderGlyphParams, SUBPIXEL_VARIANTS_X, - SUBPIXEL_VARIANTS_Y, ShapedGlyph, ShapedRun, SharedString, Size, TextRenderingMode, point, - size, -}; - -use itertools::Itertools; -use parking_lot::RwLock; -use smallvec::SmallVec; -use std::{borrow::Cow, ops::Range, sync::Arc}; -use swash::{ - scale::{Render, ScaleContext, Source, StrikeWith}, - zeno::{Format, Vector}, -}; -use unicode_segmentation::UnicodeSegmentation; - -pub struct CosmicTextSystem(RwLock); - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -struct FontKey { - family: SharedString, - features: FontFeatures, - fallbacks: Option, -} - -impl FontKey { - fn new(family: SharedString, features: FontFeatures, fallbacks: Option) -> Self { - Self { - family, - features, - fallbacks, - } - } -} - -struct CosmicTextSystemState { - font_system: FontSystem, - scratch: ShapeBuffer, - swash_scale_context: ScaleContext, - pending_glyph_images: HashMap, - /// Contains all already loaded fonts, including all faces. Indexed by `FontId`. - loaded_fonts: Vec, - /// Caches the `FontId`s associated with a specific family to avoid iterating the font database - /// for every font face in a family. - font_ids_by_family_cache: HashMap>, - system_font_fallback: String, -} - -struct LoadedFont { - font: Arc, - features: CosmicFontFeatures, - is_known_emoji_font: bool, - /// resolved at load time so `layout_line` shares one chain across faces. - /// `Arc` keeps clone cheap on the per-run hot path. - user_fallback_chain: Arc<[(FontId, SharedString)]>, -} - -struct FontMatchProperties { - primary_family_name: SharedString, - stretch: Stretch, - style: Style, - weight: Weight, - features: CosmicFontFeatures, - fallback_chain: Arc<[(FontId, SharedString)]>, -} - -impl FontMatchProperties { - fn attributes<'a>(&'a self, font_id: FontId, family_name: &'a str) -> Attrs<'a> { - Attrs::new() - .metadata(font_id.0) - .family(Family::Name(family_name)) - .stretch(self.stretch) - .style(self.style) - .weight(self.weight) - .font_features(self.features.clone()) - } -} - -impl CosmicTextSystem { - pub fn new(system_font_fallback: &str) -> Self { - let font_system = FontSystem::new(); - - Self(RwLock::new(CosmicTextSystemState { - font_system, - scratch: ShapeBuffer::default(), - swash_scale_context: ScaleContext::new(), - pending_glyph_images: HashMap::default(), - loaded_fonts: Vec::new(), - font_ids_by_family_cache: HashMap::default(), - system_font_fallback: system_font_fallback.to_string(), - })) - } - - pub fn new_without_system_fonts(system_font_fallback: &str) -> Self { - let font_system = FontSystem::new_with_locale_and_db( - "en-US".to_string(), - cosmic_text::fontdb::Database::new(), - ); - - Self(RwLock::new(CosmicTextSystemState { - font_system, - scratch: ShapeBuffer::default(), - swash_scale_context: ScaleContext::new(), - pending_glyph_images: HashMap::default(), - loaded_fonts: Vec::new(), - font_ids_by_family_cache: HashMap::default(), - system_font_fallback: system_font_fallback.to_string(), - })) - } -} - -impl PlatformTextSystem for CosmicTextSystem { - fn add_fonts(&self, fonts: Vec>) -> Result<()> { - self.0.write().add_fonts(fonts) - } - - fn all_font_names(&self) -> Vec { - let mut result = self - .0 - .read() - .font_system - .db() - .faces() - .filter_map(|face| face.families.first().map(|family| family.0.clone())) - .collect_vec(); - result.sort_unstable(); - result.dedup(); - result - } - - fn font_id(&self, font: &Font) -> Result { - let mut state = self.0.write(); - let key = FontKey::new( - font.family.clone(), - font.features.clone(), - font.fallbacks.clone(), - ); - let candidates = if let Some(font_ids) = state.font_ids_by_family_cache.get(&key) { - font_ids.as_slice() - } else { - let font_ids = - state.load_family(&font.family, &font.features, font.fallbacks.as_ref())?; - state.font_ids_by_family_cache.insert(key.clone(), font_ids); - state.font_ids_by_family_cache[&key].as_ref() - }; - - let ix = find_best_match(font, candidates, &state)?; - - Ok(candidates[ix]) - } - - fn prewarm_fonts(&self, font_ids: &[FontId]) { - self.0.write().prewarm_fonts(font_ids); - } - - fn font_metrics(&self, font_id: FontId) -> FontMetrics { - let metrics = self - .0 - .read() - .loaded_font(font_id) - .font - .as_swash() - .metrics(&[]); - - FontMetrics { - units_per_em: metrics.units_per_em as u32, - ascent: metrics.ascent, - descent: -metrics.descent, - line_gap: metrics.leading, - underline_position: metrics.underline_offset, - underline_thickness: metrics.stroke_size, - cap_height: metrics.cap_height, - x_height: metrics.x_height, - bounding_box: Bounds { - origin: point(0.0, 0.0), - size: size(metrics.max_width, metrics.ascent + metrics.descent), - }, - } - } - - fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { - let lock = self.0.read(); - let glyph_metrics = lock.loaded_font(font_id).font.as_swash().glyph_metrics(&[]); - let glyph_id = glyph_id.0 as u16; - Ok(Bounds { - origin: point(0.0, 0.0), - size: size( - glyph_metrics.advance_width(glyph_id), - glyph_metrics.advance_height(glyph_id), - ), - }) - } - - fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { - self.0.read().advance(font_id, glyph_id) - } - - fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { - self.0.read().glyph_for_char(font_id, ch) - } - - fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result> { - self.0.write().raster_bounds(params) - } - - fn rasterize_glyph( - &self, - params: &RenderGlyphParams, - raster_bounds: Bounds, - ) -> Result<(Size, Vec)> { - self.0.write().rasterize_glyph(params, raster_bounds) - } - - fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout { - self.0.write().layout_line(text, font_size, runs) - } - - fn recommended_rendering_mode( - &self, - _font_id: FontId, - _font_size: Pixels, - ) -> TextRenderingMode { - TextRenderingMode::Subpixel - } -} - -impl CosmicTextSystemState { - fn loaded_font(&self, font_id: FontId) -> &LoadedFont { - &self.loaded_fonts[font_id.0] - } - - fn font_match_properties(&self, font_id: FontId) -> Option { - let loaded_font = self.loaded_font(font_id); - let Some(face) = self.font_system.db().face(loaded_font.font.id()) else { - log::warn!("font face not found in database for font_id {:?}", font_id); - return None; - }; - let Some(first_family) = face.families.first() else { - log::warn!("font face has no family names for font_id {:?}", font_id); - return None; - }; - - Some(FontMatchProperties { - primary_family_name: first_family.0.clone().into(), - stretch: face.stretch, - style: face.style, - weight: face.weight, - features: loaded_font.features.clone(), - fallback_chain: Arc::clone(&loaded_font.user_fallback_chain), - }) - } - - fn prewarm_fonts(&mut self, font_ids: &[FontId]) { - for &font_id in font_ids { - let Some(properties) = self.font_match_properties(font_id) else { - continue; - }; - let primary_attributes = - properties.attributes(font_id, &properties.primary_family_name); - self.font_system.get_font_matches(&primary_attributes); - - for (fallback_id, fallback_name) in &*properties.fallback_chain { - let fallback_attributes = properties.attributes(*fallback_id, fallback_name); - self.font_system.get_font_matches(&fallback_attributes); - } - } - } - - #[profiling::function] - fn add_fonts(&mut self, fonts: Vec>) -> Result<()> { - let db = self.font_system.db_mut(); - for bytes in fonts { - db.load_font_source(cosmic_text::fontdb::Source::Binary(Arc::new(bytes))); - } - Ok(()) - } - - #[profiling::function] - fn load_family( - &mut self, - name: &str, - features: &FontFeatures, - fallbacks: Option<&FontFallbacks>, - ) -> Result> { - // recurse with `fallbacks = None` so a fallback family cannot pull in - // another chain. missing fallback families are dropped so a typo in - // settings still lets the primary family load. - let user_fallback_chain: Arc<[(FontId, SharedString)]> = match fallbacks { - Some(fallbacks) if !fallbacks.fallback_list().is_empty() => { - let mut chain: Vec<(FontId, SharedString)> = Vec::new(); - for fallback_name in fallbacks.fallback_list() { - let fb_key = FontKey::new( - SharedString::from(fallback_name.clone()), - features.clone(), - None, - ); - let fb_ids = if let Some(cached) = self.font_ids_by_family_cache.get(&fb_key) { - cached.clone() - } else { - let loaded = self.load_family(fallback_name, features, None)?; - self.font_ids_by_family_cache - .insert(fb_key.clone(), loaded.clone()); - loaded - }; - let Some(&fb_id) = fb_ids.first() else { - continue; - }; - let db_id = self.loaded_fonts[fb_id.0].font.id(); - if let Some(face) = self.font_system.db().face(db_id) - && let Some(family) = face.families.first() - { - chain.push((fb_id, SharedString::from(family.0.clone()))); - } - } - Arc::from(chain) - } - _ => Arc::from(Vec::new()), - }; - - let name = gpui::font_name_with_fallbacks(name, &self.system_font_fallback); - - let families = self - .font_system - .db() - .faces() - .filter(|face| face.families.iter().any(|family| *name == family.0)) - .map(|face| (face.id, face.post_script_name.clone())) - .collect::>(); - - let cosmic_features = cosmic_font_features(features)?; - - let mut loaded_font_ids = SmallVec::new(); - for (font_id, postscript_name) in families { - let font = self - .font_system - .get_font(font_id, cosmic_text::Weight::NORMAL) - .context("Could not load font")?; - - // HACK: To let the storybook run and render Windows caption icons. We should actually do better font fallback. - let allowed_bad_font_names = [ - "SegoeFluentIcons", // NOTE: Segoe fluent icons postscript name is inconsistent - "Segoe Fluent Icons", - ]; - - if font.as_swash().charmap().map('m') == 0 - && !allowed_bad_font_names.contains(&postscript_name.as_str()) - { - self.font_system.db_mut().remove_face(font.id()); - continue; - }; - - let font_id = FontId(self.loaded_fonts.len()); - loaded_font_ids.push(font_id); - self.loaded_fonts.push(LoadedFont { - font, - features: cosmic_features.clone(), - is_known_emoji_font: check_is_known_emoji_font(&postscript_name), - user_fallback_chain: Arc::clone(&user_fallback_chain), - }); - } - - Ok(loaded_font_ids) - } - - fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { - let glyph_metrics = self.loaded_font(font_id).font.as_swash().glyph_metrics(&[]); - Ok(Size { - width: glyph_metrics.advance_width(glyph_id.0 as u16), - height: glyph_metrics.advance_height(glyph_id.0 as u16), - }) - } - - fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { - let glyph_id = self.loaded_font(font_id).font.as_swash().charmap().map(ch); - if glyph_id == 0 { - None - } else { - Some(GlyphId(glyph_id.into())) - } - } - - fn raster_bounds(&mut self, params: &RenderGlyphParams) -> Result> { - let image = self.render_glyph_image(params)?; - let bounds = Bounds { - origin: point(image.placement.left.into(), (-image.placement.top).into()), - size: size(image.placement.width.into(), image.placement.height.into()), - }; - if !bounds.is_zero() { - self.pending_glyph_images.insert(params.clone(), image); - } - Ok(bounds) - } - - #[profiling::function] - fn rasterize_glyph( - &mut self, - params: &RenderGlyphParams, - glyph_bounds: Bounds, - ) -> Result<(Size, Vec)> { - if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 { - anyhow::bail!("glyph bounds are empty"); - } - - let mut image = match self.pending_glyph_images.remove(params) { - Some(image) => image, - None => self.render_glyph_image(params)?, - }; - let bitmap_size = glyph_bounds.size; - match image.content { - swash::scale::image::Content::Color | swash::scale::image::Content::SubpixelMask => { - // Convert from RGBA to BGRA. - for pixel in image.data.chunks_exact_mut(4) { - pixel.swap(0, 2); - } - Ok((bitmap_size, image.data)) - } - swash::scale::image::Content::Mask => { - if params.subpixel_rendering { - // We must always return RGBA data when subpixel rendering is requested. - let expanded = image.data.iter().flat_map(|&a| [a, a, a, a]).collect(); - Ok((bitmap_size, expanded)) - } else { - Ok((bitmap_size, image.data)) - } - } - } - } - - fn render_glyph_image( - &mut self, - params: &RenderGlyphParams, - ) -> Result { - let loaded_font = &self.loaded_fonts[params.font_id.0]; - let font_ref = loaded_font.font.as_swash(); - let pixel_size = f32::from(params.font_size); - - let subpixel_offset = Vector::new( - params.subpixel_variant.x as f32 / SUBPIXEL_VARIANTS_X as f32 / params.scale_factor, - params.subpixel_variant.y as f32 / SUBPIXEL_VARIANTS_Y as f32 / params.scale_factor, - ); - - let mut scaler = self - .swash_scale_context - .builder(font_ref) - .size(pixel_size * params.scale_factor) - .hint(true) - .build(); - - let sources: &[Source] = if params.is_emoji { - &[ - Source::ColorOutline(0), - Source::ColorBitmap(StrikeWith::BestFit), - Source::Outline, - ] - } else { - &[Source::Bitmap(StrikeWith::ExactSize), Source::Outline] - }; - - let mut renderer = Render::new(sources); - if params.subpixel_rendering { - // There seems to be a bug in Swash where the B and R values are swapped. - renderer - .format(Format::subpixel_bgra()) - .offset(subpixel_offset); - } else { - renderer.format(Format::Alpha).offset(subpixel_offset); - } - - let glyph_id: u16 = params.glyph_id.0.try_into()?; - renderer - .render(&mut scaler, glyph_id) - .with_context(|| format!("unable to render glyph via swash for {params:?}")) - } - - /// This is used when cosmic_text has chosen a fallback font instead of using the requested - /// font, typically to handle some unicode characters. When this happens, `loaded_fonts` may not - /// yet have an entry for this fallback font, and so one is added. - /// - /// Note that callers shouldn't use this `FontId` somewhere that will retrieve the corresponding - /// `LoadedFont.features`, as it will have an arbitrarily chosen or empty value. The only - /// current use of this field is for the *input* of `layout_line`, and so it's fine to use - /// `font_id_for_cosmic_id` when computing the *output* of `layout_line`. - fn font_id_for_cosmic_id(&mut self, id: cosmic_text::fontdb::ID) -> Result { - if let Some(ix) = self - .loaded_fonts - .iter() - .position(|loaded_font| loaded_font.font.id() == id) - { - Ok(FontId(ix)) - } else { - let font = self - .font_system - .get_font(id, cosmic_text::Weight::NORMAL) - .context("failed to get fallback font from cosmic-text font system")?; - let face = self - .font_system - .db() - .face(id) - .context("fallback font face not found in cosmic-text database")?; - - let font_id = FontId(self.loaded_fonts.len()); - self.loaded_fonts.push(LoadedFont { - font, - features: CosmicFontFeatures::new(), - is_known_emoji_font: check_is_known_emoji_font(&face.post_script_name), - user_fallback_chain: Arc::from(Vec::new()), - }); - - Ok(font_id) - } - } - - #[profiling::function] - fn layout_line(&mut self, text: &str, font_size: Pixels, font_runs: &[FontRun]) -> LineLayout { - if contains_paragraph_separator(text) { - self.layout_line_with_separators(text, font_size, font_runs) - } else { - self.layout_line_no_separators(text, font_size, font_runs) - } - } - - fn layout_line_with_separators( - &mut self, - text: &str, - font_size: Pixels, - font_runs: &[FontRun], - ) -> LineLayout { - let mut layout = LineLayout { - font_size, - len: text.len(), - ..Default::default() - }; - let mut paragraph_start = 0; - - for (separator_start, separator) in text - .char_indices() - .filter(|(_, character)| is_paragraph_separator(*character)) - { - let separator_end = separator_start + separator.len_utf8(); - self.shape_segment( - text, - paragraph_start..separator_start, - font_size, - font_runs, - &mut layout, - ); - self.shape_segment( - text, - separator_start..separator_end, - font_size, - font_runs, - &mut layout, - ); - paragraph_start = separator_end; - } - - self.shape_segment( - text, - paragraph_start..text.len(), - font_size, - font_runs, - &mut layout, - ); - - layout - } - - fn shape_segment( - &mut self, - text: &str, - range: Range, - font_size: Pixels, - font_runs: &[FontRun], - layout: &mut LineLayout, - ) { - if range.is_empty() { - return; - } - - let segment_font_runs = clip_font_runs(font_runs, range.clone()); - let segment = - self.layout_line_no_separators(&text[range.clone()], font_size, &segment_font_runs); - - let mut segment_runs = segment.runs; - for run in &mut segment_runs { - for glyph in &mut run.glyphs { - glyph.index += range.start; - glyph.position.x += layout.width; - } - } - - for mut run in segment_runs { - if let Some(same_run) = layout - .runs - .last_mut() - .filter(|last| last.font_id == run.font_id) - { - same_run.glyphs.append(&mut run.glyphs); - } else { - layout.runs.push(run); - } - } - - layout.width += segment.width; - layout.ascent = layout.ascent.max(segment.ascent); - layout.descent = layout.descent.max(segment.descent); - } - - fn layout_line_no_separators( - &mut self, - text: &str, - font_size: Pixels, - font_runs: &[FontRun], - ) -> LineLayout { - let mut attrs_list = AttrsList::new(&Attrs::new()); - let mut offs = 0; - for run in font_runs { - let run_end = offs + run.len; - - let Some(properties) = self.font_match_properties(run.font_id) else { - offs = run_end; - continue; - }; - - let primary_attrs = properties.attributes(run.font_id, &properties.primary_family_name); - let fallback_attrs: SmallVec<[Attrs<'_>; 4]> = properties - .fallback_chain - .iter() - .map(|(font_id, family_name)| properties.attributes(*font_id, family_name)) - .collect(); - - let spans = if properties.fallback_chain.is_empty() { - let mut spans = SmallVec::<[RunSpan; 4]>::new(); - spans.push(RunSpan { - start: offs, - end: run_end, - slot: None, - font_id: run.font_id, - }); - spans - } else { - let loaded_fonts = &self.loaded_fonts; - let covers = |id: FontId, ch: char| charmap_covers(loaded_fonts, id, ch); - compute_run_spans( - text, - offs, - run.len, - run.font_id, - &properties.fallback_chain, - &covers, - ) - }; - - for span in spans { - let attrs = match span.slot { - None => &primary_attrs, - Some(ix) => &fallback_attrs[ix], - }; - attrs_list.add_span(span.start..span.end, attrs); - } - offs = run_end; - } - - let line = ShapeLine::new( - &mut self.font_system, - text, - &attrs_list, - cosmic_text::Shaping::Advanced, - 4, - ); - let mut layout_lines = Vec::with_capacity(1); - line.layout_to_buffer( - &mut self.scratch, - f32::from(font_size), - None, // We do our own wrapping - cosmic_text::Wrap::None, - Ellipsize::None, - None, - &mut layout_lines, - None, - cosmic_text::Hinting::Disabled, - ); - - let Some(layout) = layout_lines.first() else { - return LineLayout { - font_size, - width: Pixels::ZERO, - ascent: Pixels::ZERO, - descent: Pixels::ZERO, - runs: Vec::new(), - len: text.len(), - }; - }; - - let mut runs: Vec = Vec::new(); - for glyph in &layout.glyphs { - let mut font_id = FontId(glyph.metadata); - let mut loaded_font = self.loaded_font(font_id); - if loaded_font.font.id() != glyph.font_id { - match self.font_id_for_cosmic_id(glyph.font_id) { - std::result::Result::Ok(resolved_id) => { - font_id = resolved_id; - loaded_font = self.loaded_font(font_id); - } - Err(error) => { - log::warn!( - "failed to resolve cosmic font id {:?}: {error:#}", - glyph.font_id - ); - continue; - } - } - } - let is_emoji = loaded_font.is_known_emoji_font; - - // HACK: Prevent crash caused by variation selectors. - if glyph.glyph_id == 3 && is_emoji { - continue; - } - - let shaped_glyph = ShapedGlyph { - id: GlyphId(glyph.glyph_id as u32), - position: point(glyph.x.into(), glyph.y.into()), - index: glyph.start, - is_emoji, - }; - - if let Some(last_run) = runs - .last_mut() - .filter(|last_run| last_run.font_id == font_id) - { - last_run.glyphs.push(shaped_glyph); - } else { - runs.push(ShapedRun { - font_id, - glyphs: vec![shaped_glyph], - }); - } - } - - LineLayout { - font_size, - width: layout.w.into(), - ascent: layout.max_ascent.into(), - descent: layout.max_descent.into(), - runs, - len: text.len(), - } - } -} - -#[inline(always)] -fn is_paragraph_separator(character: char) -> bool { - unicode_bidi::bidi_class(character) == unicode_bidi::BidiClass::B -} - -fn contains_paragraph_separator(text: &str) -> bool { - if text - .bytes() - .any(|byte| matches!(byte, b'\n' | b'\r' | 0x1c | 0x1d | 0x1e)) - { - return true; - } - - !text.is_ascii() && text.chars().any(is_paragraph_separator) -} - -fn clip_font_runs(font_runs: &[FontRun], range: Range) -> SmallVec<[FontRun; 4]> { - let mut clipped = SmallVec::new(); - let mut offs = 0; - for run in font_runs { - let run_start = offs; - offs += run.len; - if offs <= range.start { - continue; - } - if run_start >= range.end { - break; - } - let start = run_start.max(range.start); - let end = offs.min(range.end); - if start < end { - clipped.push(FontRun { - len: end - start, - font_id: run.font_id, - }); - } - } - clipped -} - -#[cfg(feature = "font-kit")] -fn find_best_match( - font: &Font, - candidates: &[FontId], - state: &CosmicTextSystemState, -) -> Result { - let candidate_properties = candidates - .iter() - .map(|font_id| { - let database_id = state.loaded_font(*font_id).font.id(); - let face_info = state - .font_system - .db() - .face(database_id) - .context("font face not found in database")?; - Ok(face_info_into_properties(face_info)) - }) - .collect::>>()?; - - let ix = - font_kit::matching::find_best_match(&candidate_properties, &font_into_properties(font)) - .context("requested font family contains no font matching the other parameters")?; - - Ok(ix) -} - -#[cfg(not(feature = "font-kit"))] -fn find_best_match( - font: &Font, - candidates: &[FontId], - state: &CosmicTextSystemState, -) -> Result { - if candidates.is_empty() { - anyhow::bail!("requested font family contains no font matching the other parameters"); - } - if candidates.len() == 1 { - return Ok(0); - } - - let target_weight = font.weight.0; - let target_italic = matches!( - font.style, - gpui::FontStyle::Italic | gpui::FontStyle::Oblique - ); - - let mut best_index = 0; - let mut best_score = u32::MAX; - - for (index, font_id) in candidates.iter().enumerate() { - let database_id = state.loaded_font(*font_id).font.id(); - let face_info = state - .font_system - .db() - .face(database_id) - .context("font face not found in database")?; - - let is_italic = matches!( - face_info.style, - cosmic_text::Style::Italic | cosmic_text::Style::Oblique - ); - let style_penalty: u32 = if is_italic == target_italic { 0 } else { 1000 }; - let weight_diff = (face_info.weight.0 as i32 - target_weight as i32).unsigned_abs(); - let score = style_penalty + weight_diff; - - if score < best_score { - best_score = score; - best_index = index; - } - } - - Ok(best_index) -} - -/// one contiguous slice of a `FontRun` that maps to a single slot. `slot` is -/// `None` for the primary font and `Some(ix)` for `fallback_chain[ix]`. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -struct RunSpan { - start: usize, - end: usize, - slot: Option, - font_id: FontId, -} - -/// walks `text[run_offset..run_offset + run_len]` and groups codepoints into -/// spans. inheriting codepoints stay in the current span so shaping clusters -/// like emoji zwj sequences and combining marks are not torn apart. -fn compute_run_spans( - text: &str, - run_offset: usize, - run_len: usize, - primary: FontId, - fallback_chain: &[(FontId, SharedString)], - covers: &impl Fn(FontId, char) -> bool, -) -> SmallVec<[RunSpan; 4]> { - let mut spans = SmallVec::new(); - let run_end = run_offset + run_len; - if run_end <= run_offset { - return spans; - } - if fallback_chain.is_empty() { - spans.push(RunSpan { - start: run_offset, - end: run_end, - slot: None, - font_id: primary, - }); - return spans; - } - let run_text = &text[run_offset..run_end]; - let mut span_start = run_offset; - let mut span_slot: Option = None; - let mut span_font_id = primary; - for (grapheme_idx, grapheme) in run_text.grapheme_indices(true) { - let abs = run_offset + grapheme_idx; - let ch = grapheme.chars().next().unwrap_or('\0'); - let next_slot = pick_covering_slot(ch, span_slot, primary, fallback_chain, covers); - if next_slot == span_slot { - continue; - } - if abs > span_start { - spans.push(RunSpan { - start: span_start, - end: abs, - slot: span_slot, - font_id: span_font_id, - }); - } - span_start = abs; - span_slot = next_slot; - span_font_id = slot_font_id(next_slot, primary, fallback_chain); - } - if span_start < run_end { - spans.push(RunSpan { - start: span_start, - end: run_end, - slot: span_slot, - font_id: span_font_id, - }); - } - spans -} - -fn slot_font_id( - slot: Option, - primary: FontId, - fallback_chain: &[(FontId, SharedString)], -) -> FontId { - match slot { - None => primary, - Some(ix) => fallback_chain[ix].0, - } -} - -fn pick_covering_slot( - ch: char, - current: Option, - primary: FontId, - fallback_chain: &[(FontId, SharedString)], - covers: &impl Fn(FontId, char) -> bool, -) -> Option { - if (ch as u32) <= 0x7F { - return None; - } - if covers(primary, ch) { - return None; - } - let current_id = slot_font_id(current, primary, fallback_chain); - if covers(current_id, ch) { - return current; - } - - fallback_chain - .iter() - .position(|(fb_id, _)| covers(*fb_id, ch)) -} - -fn charmap_covers(loaded_fonts: &[LoadedFont], id: FontId, ch: char) -> bool { - loaded_fonts - .get(id.0) - .is_some_and(|loaded| loaded.font.as_swash().charmap().map(ch) != 0) -} - -fn cosmic_font_features(features: &FontFeatures) -> Result { - let mut result = CosmicFontFeatures::new(); - for feature in features.0.iter() { - let name_bytes: [u8; 4] = feature - .0 - .as_bytes() - .try_into() - .context("Incorrect feature flag format")?; - - let tag = cosmic_text::FeatureTag::new(&name_bytes); - - result.set(tag, feature.1); - } - Ok(result) -} - -#[cfg(feature = "font-kit")] -fn font_into_properties(font: &gpui::Font) -> font_kit::properties::Properties { - font_kit::properties::Properties { - style: match font.style { - gpui::FontStyle::Normal => font_kit::properties::Style::Normal, - gpui::FontStyle::Italic => font_kit::properties::Style::Italic, - gpui::FontStyle::Oblique => font_kit::properties::Style::Oblique, - }, - weight: font_kit::properties::Weight(font.weight.0), - stretch: Default::default(), - } -} - -#[cfg(feature = "font-kit")] -fn face_info_into_properties( - face_info: &cosmic_text::fontdb::FaceInfo, -) -> font_kit::properties::Properties { - font_kit::properties::Properties { - style: match face_info.style { - cosmic_text::Style::Normal => font_kit::properties::Style::Normal, - cosmic_text::Style::Italic => font_kit::properties::Style::Italic, - cosmic_text::Style::Oblique => font_kit::properties::Style::Oblique, - }, - weight: font_kit::properties::Weight(face_info.weight.0.into()), - stretch: match face_info.stretch { - cosmic_text::Stretch::Condensed => font_kit::properties::Stretch::CONDENSED, - cosmic_text::Stretch::Expanded => font_kit::properties::Stretch::EXPANDED, - cosmic_text::Stretch::ExtraCondensed => font_kit::properties::Stretch::EXTRA_CONDENSED, - cosmic_text::Stretch::ExtraExpanded => font_kit::properties::Stretch::EXTRA_EXPANDED, - cosmic_text::Stretch::Normal => font_kit::properties::Stretch::NORMAL, - cosmic_text::Stretch::SemiCondensed => font_kit::properties::Stretch::SEMI_CONDENSED, - cosmic_text::Stretch::SemiExpanded => font_kit::properties::Stretch::SEMI_EXPANDED, - cosmic_text::Stretch::UltraCondensed => font_kit::properties::Stretch::ULTRA_CONDENSED, - cosmic_text::Stretch::UltraExpanded => font_kit::properties::Stretch::ULTRA_EXPANDED, - }, - } -} - -fn check_is_known_emoji_font(postscript_name: &str) -> bool { - // TODO: Include other common emoji fonts - postscript_name == "NotoColorEmoji" -} - -#[cfg(test)] -mod tests { - use super::*; - - fn fid(i: usize) -> FontId { - FontId(i) - } - - fn chain(ids: &[usize]) -> SmallVec<[(FontId, SharedString); 4]> { - ids.iter() - .map(|&i| (fid(i), SharedString::from(format!("fb{i}")))) - .collect() - } - - fn span(start: usize, end: usize, slot: Option, font_id: FontId) -> RunSpan { - RunSpan { - start, - end, - slot, - font_id, - } - } - - const IBM_PLEX: &[u8] = - include_bytes!("../../../assets/fonts/ibm-plex-sans/IBMPlexSans-Regular.ttf"); - - /// Every code point of `Bidi_Class=B`, each of which starts a new bidi - /// paragraph and so can split one line into mixed-direction paragraphs. - const SEPARATORS: &[char] = &[ - '\u{000a}', '\u{000d}', '\u{001c}', '\u{001d}', '\u{001e}', '\u{0085}', '\u{2029}', - ]; - - fn text_system() -> Result { - let text_system = CosmicTextSystem::new_without_system_fonts("IBM Plex Sans"); - text_system.add_fonts(vec![Cow::Borrowed(IBM_PLEX)])?; - Ok(text_system) - } - - fn layout_text(text_system: &CosmicTextSystem, text: &str) -> Result { - let font_id = text_system.font_id(&gpui::font("IBM Plex Sans"))?; - let runs = [FontRun { - len: text.len(), - font_id, - }]; - Ok(text_system.layout_line(text, gpui::px(14.0), &runs)) - } - - /// Mirrors the original crash: mixed-direction text reaching the shaper - /// through `shape_text`, which only splits lines on `\n`. - #[test] - fn shape_text_with_mixed_direction_paragraphs() -> Result<()> { - let platform_text_system = Arc::new(text_system()?); - let text_system = Arc::new(gpui::TextSystem::new(platform_text_system)); - let window_text_system = gpui::WindowTextSystem::new(text_system); - - let text: SharedString = "first line\n\u{05d0}\u{001c}A".into(); - let runs = [gpui::TextRun { - len: text.len(), - font: gpui::font("IBM Plex Sans"), - ..Default::default() - }]; - - let lines = window_text_system.shape_text(text, gpui::px(14.0), &runs, None, None)?; - - assert_eq!(lines.len(), 2); - assert_eq!(lines[1].len(), "\u{05d0}\u{001c}A".len()); - assert!(lines[1].width() > Pixels::ZERO); - Ok(()) - } - - #[test] - fn layout_line_with_mixed_direction_paragraphs() -> Result<()> { - let text_system = text_system()?; - - for separator in SEPARATORS { - for text in [ - format!("\u{05d0}{separator}A"), - format!("A{separator}\u{05d0}"), - ] { - let layout = layout_text(&text_system, &text)?; - - assert_eq!(layout.len, text.len(), "{text:?}"); - assert!(layout.width > Pixels::ZERO, "{text:?}"); - assert!( - layout.runs.iter().any(|run| !run.glyphs.is_empty()), - "{text:?}" - ); - } - } - - Ok(()) - } - - #[test] - fn layout_line_with_separators_at_line_edges() -> Result<()> { - let text_system = text_system()?; - - for text in [ - "\u{001c}", - "\u{001c}\u{001c}", - "\u{001c}\u{05d0}", - "\u{05d0}\u{001c}", - "\u{05d0}\u{001c}\u{001c}A", - "\u{001c}\u{05d0}\u{001c}A\u{001c}", - ] { - let layout = layout_text(&text_system, text)?; - assert_eq!(layout.len, text.len(), "{text:?}"); - } - - Ok(()) - } - - /// Glyph indices must stay absolute and positions ordered across segment - /// boundaries, otherwise cursor placement and hit testing desync. Uses - /// single-direction text so visual order matches logical order. - #[test] - fn layout_line_keeps_indices_and_positions_ordered_across_paragraphs() -> Result<()> { - let text_system = text_system()?; - let text = "ab\u{001c}cd\u{2029}ef"; - let layout = layout_text(&text_system, text)?; - - let glyphs: Vec<_> = layout.runs.iter().flat_map(|run| &run.glyphs).collect(); - assert!(!glyphs.is_empty()); - - for glyph in &glyphs { - assert!(glyph.index < text.len(), "{:?}", glyph.index); - assert!(text.is_char_boundary(glyph.index), "{:?}", glyph.index); - } - for pair in glyphs.windows(2) { - assert!(pair[0].index < pair[1].index); - assert!(pair[0].position.x <= pair[1].position.x); - } - - // Every segment contributes width, so the whole line is wider than its - // leading paragraph alone. - assert!(layout.width > layout_text(&text_system, "ab")?.width); - Ok(()) - } - - /// A font run boundary that does not line up with a paragraph boundary must - /// still be clipped to the right segments. - #[test] - fn layout_line_with_font_run_straddling_a_separator() -> Result<()> { - let text_system = text_system()?; - let font_id = text_system.font_id(&gpui::font("IBM Plex Sans"))?; - let text = "ab\u{001c}\u{05d0}\u{05d1}"; - - // The run boundary falls inside the trailing RTL paragraph. - let runs = [ - FontRun { - len: "ab\u{001c}\u{05d0}".len(), - font_id, - }, - FontRun { - len: "\u{05d1}".len(), - font_id, - }, - ]; - let layout = text_system.layout_line(text, gpui::px(14.0), &runs); - - assert_eq!(layout.len, text.len()); - assert!(layout.width > Pixels::ZERO); - Ok(()) - } - - /// Lines with no separator take the fast path and must be shaped exactly as - /// they were before paragraph splitting existed. - #[test] - fn layout_line_without_separators_takes_fast_path() -> Result<()> { - let text_system = text_system()?; - - for text in [ - "hello world", - "\u{05d0}\u{05d1}\u{05d2}", - "mixed \u{05d0}\u{05d1}", - ] { - assert!(!contains_paragraph_separator(text), "{text:?}"); - let layout = layout_text(&text_system, text)?; - assert_eq!(layout.len, text.len(), "{text:?}"); - assert!(layout.width > Pixels::ZERO, "{text:?}"); - } - - Ok(()) - } - - #[test] - fn paragraph_separator_detection() { - for separator in SEPARATORS { - assert!(is_paragraph_separator(*separator), "{separator:?}"); - assert!(contains_paragraph_separator(&format!("a{separator}b"))); - } - - for text in [ - "", - "plain ascii", - "\u{05d0}", - "tab\there", - "emoji \u{1f600}", - ] { - assert!(!contains_paragraph_separator(text), "{text:?}"); - } - } - - #[test] - fn font_runs_are_clipped_to_segment() { - let runs = [ - FontRun { - len: 3, - font_id: fid(1), - }, - FontRun { - len: 4, - font_id: fid(2), - }, - ]; - - assert_eq!(clip_font_runs(&runs, 0..7).as_slice(), &runs); - assert_eq!( - clip_font_runs(&runs, 2..5).as_slice(), - &[ - FontRun { - len: 1, - font_id: fid(1) - }, - FontRun { - len: 2, - font_id: fid(2) - }, - ] - ); - assert_eq!( - clip_font_runs(&runs, 3..7).as_slice(), - &[FontRun { - len: 4, - font_id: fid(2) - }] - ); - assert!(clip_font_runs(&runs, 5..5).is_empty()); - } - - #[test] - fn primary_wins_over_current_fallback_when_primary_covers() { - let primary = fid(0); - let fb = chain(&[1, 2]); - let covers = |id: FontId, _: char| id == fid(0) || id == fid(1); - assert_eq!( - pick_covering_slot('a', Some(0), primary, &fb, &covers), - None - ); - } - - #[test] - fn primary_preferred_over_fallback_when_both_cover() { - let primary = fid(0); - let fb = chain(&[1]); - let covers = |_: FontId, _: char| true; - assert_eq!(pick_covering_slot('a', None, primary, &fb, &covers), None); - } - - #[test] - fn falls_through_chain_in_order() { - let primary = fid(0); - let fb = chain(&[1, 2, 3]); - // only fallback 2 at index 1 covers. - let covers = |id: FontId, _: char| id == fid(2); - assert_eq!( - pick_covering_slot('字', None, primary, &fb, &covers), - Some(1) - ); - } - - #[test] - fn no_coverage_returns_primary() { - let primary = fid(0); - let fb = chain(&[1, 2]); - let covers = |_: FontId, _: char| false; - // nothing covers. return `None` so the `cosmic-text` built in script - // fallback can take over during shaping. - assert_eq!( - pick_covering_slot('\u{1F600}', Some(1), primary, &fb, &covers), - None - ); - } - - #[test] - fn empty_chain_always_returns_primary() { - let primary = fid(0); - let fb: SmallVec<[(FontId, SharedString); 4]> = SmallVec::new(); - let covers = |_: FontId, _: char| false; - assert_eq!(pick_covering_slot('a', None, primary, &fb, &covers), None); - } - - #[test] - fn slot_font_id_resolution() { - let primary = fid(7); - let fb = chain(&[10, 20]); - assert_eq!(slot_font_id(None, primary, &fb), fid(7)); - assert_eq!(slot_font_id(Some(0), primary, &fb), fid(10)); - assert_eq!(slot_font_id(Some(1), primary, &fb), fid(20)); - } - - #[test] - fn run_spans_with_no_chain_emit_one_primary_span() { - let primary = fid(0); - let fb: SmallVec<[(FontId, SharedString); 4]> = SmallVec::new(); - let covers = |_: FontId, _: char| false; - let text = "hello"; - let spans = compute_run_spans(text, 0, text.len(), primary, &fb, &covers); - assert_eq!(spans.as_slice(), &[span(0, text.len(), None, primary)]); - } - - #[test] - fn run_spans_use_byte_offsets_for_multibyte_chars() { - let primary = fid(0); - let fb = chain(&[1]); - // primary covers ascii. fallback covers cjk. - let covers = |id: FontId, ch: char| { - if id == primary { - ch.is_ascii() - } else { - !ch.is_ascii() - } - }; - let text = "a字b"; - let spans = compute_run_spans(text, 0, text.len(), primary, &fb, &covers); - // '字' is 3 bytes so split is at 1 then 4. - assert_eq!( - spans.as_slice(), - &[ - span(0, 1, None, primary), - span(1, 4, Some(0), fid(1)), - span(4, 5, None, primary), - ] - ); - } - - #[test] - fn run_spans_respect_run_offset() { - let primary = fid(0); - let fb = chain(&[1]); - let covers = |id: FontId, ch: char| { - if id == primary { - ch.is_ascii() - } else { - !ch.is_ascii() - } - }; - // outer text has a prefix that is not part of this run. - let text = "xx字y"; - let run_offset = 2; - let run_len = text.len() - run_offset; - let spans = compute_run_spans(text, run_offset, run_len, primary, &fb, &covers); - assert_eq!( - spans.as_slice(), - &[span(2, 5, Some(0), fid(1)), span(5, 6, None, primary)] - ); - } - - #[test] - fn run_spans_keep_combining_marks_with_base_in_fallback() { - let primary = fid(0); - let fb = chain(&[1]); - // primary covers ascii only. fallback covers the base char. - // combining mark must stay in the fallback span even when fallback - // does not advertise coverage of it. - let covers = |id: FontId, ch: char| { - if id == primary { - ch.is_ascii() - } else { - ch == '\u{0905}' - } - }; - // \u{0905} devanagari short a + \u{0902} candrabindu mark. - let text = "\u{0905}\u{0902}"; - let spans = compute_run_spans(text, 0, text.len(), primary, &fb, &covers); - assert_eq!(spans.as_slice(), &[span(0, text.len(), Some(0), fid(1))]); - } - - #[test] - fn run_spans_keep_zwj_inside_emoji_cluster() { - let primary = fid(0); - let fb = chain(&[1]); - // only fallback covers the emoji codepoints. zwj must not split. - let covers = |id: FontId, ch: char| id == fid(1) && ch != '\u{200D}'; - // family zwj sequence woman zwj girl. - let text = "\u{1F469}\u{200D}\u{1F467}"; - let spans = compute_run_spans(text, 0, text.len(), primary, &fb, &covers); - assert_eq!(spans.as_slice(), &[span(0, text.len(), Some(0), fid(1))]); - } - - #[test] - fn run_spans_collapse_adjacent_same_slot() { - let primary = fid(0); - let fb = chain(&[1]); - let covers = |id: FontId, ch: char| { - if id == primary { - ch.is_ascii() - } else { - !ch.is_ascii() - } - }; - let text = "字字字"; - let spans = compute_run_spans(text, 0, text.len(), primary, &fb, &covers); - assert_eq!(spans.as_slice(), &[span(0, text.len(), Some(0), fid(1))]); - } - - #[test] - fn run_spans_empty_run_returns_no_spans() { - let primary = fid(0); - let fb = chain(&[1]); - let covers = |_: FontId, _: char| true; - let spans = compute_run_spans("anything", 3, 0, primary, &fb, &covers); - assert!(spans.is_empty()); - } -} diff --git a/crates/gpui_pre_wgpu/src/gpui_wgpu.rs b/crates/gpui_pre_wgpu/src/gpui_wgpu.rs deleted file mode 100644 index 3557d6b5..00000000 --- a/crates/gpui_pre_wgpu/src/gpui_wgpu.rs +++ /dev/null @@ -1,11 +0,0 @@ -mod cosmic_text_system; -mod shaders; -mod wgpu_atlas; -mod wgpu_context; -mod wgpu_renderer; - -pub use cosmic_text_system::*; -pub use wgpu; -pub use wgpu_atlas::*; -pub use wgpu_context::*; -pub use wgpu_renderer::{GpuContext, WgpuRenderer, WgpuSurfaceConfig}; diff --git a/crates/gpui_pre_wgpu/src/shaders.rs b/crates/gpui_pre_wgpu/src/shaders.rs deleted file mode 100644 index 3d4a12a4..00000000 --- a/crates/gpui_pre_wgpu/src/shaders.rs +++ /dev/null @@ -1,54 +0,0 @@ -/// Shader variant for backends with storage buffer support: the shared shader -/// logic plus the storage-buffer instance transport. -pub(crate) const STORAGE_BUFFER_SHADERS: &str = concat!( - include_str!("shaders.wgsl"), - include_str!("shaders_storage.wgsl"), -); - -/// Shader variant for WebGL2, which has no storage buffers: the shared shader -/// logic plus the texture-based instance transport. -pub(crate) const WEBGL_SHADERS: &str = concat!( - include_str!("shaders.wgsl"), - include_str!("shaders_webgl.wgsl"), -); - -/// Subpixel text rendering requires dual-source blending, which WebGL2 lacks, so -/// this variant only ever runs with the storage-buffer transport. The `enable` -/// directive must precede all declarations. -pub(crate) const SUBPIXEL_SHADERS: &str = concat!( - "enable dual_source_blending;\n", - include_str!("shaders.wgsl"), - include_str!("shaders_storage.wgsl"), - include_str!("shaders_subpixel.wgsl"), -); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn webgl_shader_is_valid_wgsl_without_storage_buffers() { - assert!(!WEBGL_SHADERS.contains("var cyan -> green -> yellow -> red). -fn heat_map_color(value: f32, minValue: f32, maxValue: f32, position: vec2) -> vec4 { - // Normalize value to 0-1 range - let t = clamp((value - minValue) / (maxValue - minValue), 0.0, 1.0); - - // Heat map color calculation - let r = t * t; - let g = 4.0 * t * (1.0 - t); - let b = (1.0 - t) * (1.0 - t); - let heat_color = vec3(r, g, b); - - // Create a checkerboard pattern (black and white) - let sum = floor(position.x / 3) + floor(position.y / 3); - let is_odd = fract(sum * 0.5); // 0.0 for even, 0.5 for odd - let checker_value = is_odd * 2.0; // 0.0 for even, 1.0 for odd - let checker_color = vec3(checker_value); - - // Determine if value is in range (1.0 if in range, 0.0 if out of range) - let in_range = step(minValue, value) * step(value, maxValue); - - // Mix checkerboard and heat map based on whether value is in range - let final_color = mix(checker_color, heat_color, in_range); - - return vec4(final_color, 1.0); -} - -*/ - -// Contrast and gamma correction adapted from https://github.com/microsoft/terminal/blob/1283c0f5b99a2961673249fa77c6b986efb5086c/src/renderer/atlas/dwrite.hlsl -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -fn color_brightness(color: vec3) -> f32 { - // REC. 601 luminance coefficients for perceived brightness - return dot(color, vec3(0.30, 0.59, 0.11)); -} - -fn light_on_dark_contrast(enhancedContrast: f32, color: vec3) -> f32 { - let brightness = color_brightness(color); - let multiplier = saturate(4.0 * (0.75 - brightness)); - return enhancedContrast * multiplier; -} - -fn enhance_contrast(alpha: f32, k: f32) -> f32 { - return alpha * (k + 1.0) / (alpha * k + 1.0); -} - -fn enhance_contrast3(alpha: vec3, k: f32) -> vec3 { - return alpha * (k + 1.0) / (alpha * k + 1.0); -} - -fn apply_alpha_correction(a: f32, b: f32, g: vec4) -> f32 { - let brightness_adjustment = g.x * b + g.y; - let correction = brightness_adjustment * a + (g.z * b + g.w); - return a + a * (1.0 - a) * correction; -} - -fn apply_alpha_correction3(a: vec3, b: vec3, g: vec4) -> vec3 { - let brightness_adjustment = g.x * b + g.y; - let correction = brightness_adjustment * a + (g.z * b + g.w); - return a + a * (1.0 - a) * correction; -} - -fn apply_contrast_and_gamma_correction(sample: f32, color: vec3, enhanced_contrast_factor: f32, gamma_ratios: vec4) -> f32 { - let enhanced_contrast = light_on_dark_contrast(enhanced_contrast_factor, color); - let brightness = color_brightness(color); - - let contrasted = enhance_contrast(sample, enhanced_contrast); - return apply_alpha_correction(contrasted, brightness, gamma_ratios); -} - -fn apply_contrast_and_gamma_correction3(sample: vec3, color: vec3, enhanced_contrast_factor: f32, gamma_ratios: vec4) -> vec3 { - let enhanced_contrast = light_on_dark_contrast(enhanced_contrast_factor, color); - - let contrasted = enhance_contrast3(sample, enhanced_contrast); - return apply_alpha_correction3(contrasted, color, gamma_ratios); -} - -struct GlobalParams { - viewport_size: vec2, - premultiplied_alpha: u32, - pad: u32, -} - -struct GammaParams { - gamma_ratios: vec4, - grayscale_enhanced_contrast: f32, - subpixel_enhanced_contrast: f32, - is_bgr: u32, - pad: u32, -} - -@group(0) @binding(0) var globals: GlobalParams; -@group(0) @binding(1) var gamma_params: GammaParams; -@group(2) @binding(0) var t_sprite: texture_2d; -@group(2) @binding(1) var s_sprite: sampler; - -const M_PI_F: f32 = 3.1415926; -const GRAYSCALE_FACTORS: vec3 = vec3(0.2126, 0.7152, 0.0722); - -struct Bounds { - origin: vec2, - size: vec2, -} - -struct Corners { - top_left: f32, - top_right: f32, - bottom_right: f32, - bottom_left: f32, -} - -struct ContentMask { - bounds: Bounds, - corner_radii: Corners, - clip_index: u32, - clip_padding: u32, -} - -struct RoundedClip { - bounds: Bounds, - radii_x: Corners, - radii_y: Corners, - parent: u32, - padding: u32, -} - -struct Edges { - top: f32, - right: f32, - bottom: f32, - left: f32, -} - -struct Hsla { - h: f32, - s: f32, - l: f32, - a: f32, -} - -struct LinearColorStop { - color: Hsla, - percentage: f32, -} - -struct Background { - // 0u is Solid - // 1u is LinearGradient - // 2u is PatternSlash - // 3u is Checkerboard - tag: u32, - // 0u is sRGB linear color - // 1u is Oklab color - color_space: u32, - solid: Hsla, - gradient_angle_or_pattern_height: f32, - colors: array, - pad: u32, -} - -struct AtlasTextureId { - index: u32, - kind: u32, -} - -struct AtlasBounds { - origin: vec2, - size: vec2, -} - -struct AtlasTile { - texture_id: AtlasTextureId, - tile_id: u32, - padding: u32, - bounds: AtlasBounds, -} - -struct TransformationMatrix { - rotation_scale: mat2x2, - translation: vec2, -} - -fn to_device_position_impl(position: vec2) -> vec4 { - let device_position = position / globals.viewport_size * vec2(2.0, -2.0) + vec2(-1.0, 1.0); - return vec4(device_position, 0.0, 1.0); -} - -fn to_device_position(unit_vertex: vec2, bounds: Bounds) -> vec4 { - let position = unit_vertex * vec2(bounds.size) + bounds.origin; - return to_device_position_impl(position); -} - -fn to_device_position_transformed(unit_vertex: vec2, bounds: Bounds, transform: TransformationMatrix) -> vec4 { - let position = unit_vertex * vec2(bounds.size) + bounds.origin; - //Note: Rust side stores it as row-major, so transposing here - let transformed = transpose(transform.rotation_scale) * position + transform.translation; - return to_device_position_impl(transformed); -} - -fn to_tile_position(unit_vertex: vec2, tile: AtlasTile) -> vec2 { - let atlas_size = vec2(textureDimensions(t_sprite, 0)); - return (vec2(tile.bounds.origin) + unit_vertex * vec2(tile.bounds.size)) / atlas_size; -} - -fn distance_from_clip_rect_impl(position: vec2, mask: ContentMask) -> vec4 { - let tl = position - mask.bounds.origin; - let br = mask.bounds.origin + mask.bounds.size - position; - return vec4(tl.x, br.x, tl.y, br.y); -} - -fn distance_from_clip_bounds_impl(position: vec2, clip_bounds: Bounds) -> vec4 { - let tl = position - clip_bounds.origin; - let br = clip_bounds.origin + clip_bounds.size - position; - return vec4(tl.x, br.x, tl.y, br.y); -} - -fn distance_from_clip_rect(unit_vertex: vec2, bounds: Bounds, mask: ContentMask) -> vec4 { - let position = unit_vertex * vec2(bounds.size) + bounds.origin; - return distance_from_clip_rect_impl(position, mask); -} - -fn distance_from_clip_rect_transformed(unit_vertex: vec2, bounds: Bounds, mask: ContentMask, transform: TransformationMatrix) -> vec4 { - let position = unit_vertex * vec2(bounds.size) + bounds.origin; - let transformed = transpose(transform.rotation_scale) * position + transform.translation; - return distance_from_clip_rect_impl(transformed, mask); -} - -// Signed distance near an ellipse boundary; exact for circles and on the axes. -fn clip_corner_distance(point: vec2, radii: vec2) -> f32 { - if (any(radii <= vec2(0.0)) || any(point >= radii)) { - return -1e20; - } - let p = point - radii; - let k0 = length(p / radii); - let k1 = length(p / (radii * radii)); - if (k1 == 0.0) { return -min(radii.x, radii.y); } - return k0 * (k0 - 1.0) / k1; -} - -fn rounded_clip_distance(position: vec2, clip: RoundedClip) -> f32 { - let tl = position - clip.bounds.origin; - let br = clip.bounds.size - tl; - var distance = max(max(-tl.x, -tl.y), max(-br.x, -br.y)); - distance = max(distance, clip_corner_distance(tl, vec2(clip.radii_x.top_left, clip.radii_y.top_left))); - distance = max(distance, clip_corner_distance(vec2(br.x, tl.y), vec2(clip.radii_x.top_right, clip.radii_y.top_right))); - distance = max(distance, clip_corner_distance(br, vec2(clip.radii_x.bottom_right, clip.radii_y.bottom_right))); - distance = max(distance, clip_corner_distance(vec2(tl.x, br.y), vec2(clip.radii_x.bottom_left, clip.radii_y.bottom_left))); - return distance; -} - -fn content_mask_coverage(position: vec2, mask_bounds: vec4, mask_radii: vec4, clip_index: u32) -> f32 { - var distance = -1e20; - if (clip_index == 0u) { - let radii = Corners(mask_radii.x, mask_radii.y, mask_radii.z, mask_radii.w); - distance = rounded_clip_distance(position, RoundedClip(Bounds(mask_bounds.xy, mask_bounds.zw), radii, radii, 0u, 0u)); - } else { - var index = clip_index; - loop { - let clip = load_clip(index - 1u); - distance = max(distance, rounded_clip_distance(position, clip)); - index = clip.parent; - if (index == 0u) { break; } - } - } - return saturate(0.5 - distance); -} - -// https://gamedev.stackexchange.com/questions/92015/optimized-linear-to-srgb-glsl -fn srgb_to_linear(srgb: vec3) -> vec3 { - let cutoff = srgb < vec3(0.04045); - let higher = pow((srgb + vec3(0.055)) / vec3(1.055), vec3(2.4)); - let lower = srgb / vec3(12.92); - return select(higher, lower, cutoff); -} - -fn srgb_to_linear_component(a: f32) -> f32 { - let cutoff = a < 0.04045; - let higher = pow((a + 0.055) / 1.055, 2.4); - let lower = a / 12.92; - return select(higher, lower, cutoff); -} - -fn linear_to_srgb(linear: vec3) -> vec3 { - let cutoff = linear < vec3(0.0031308); - let higher = vec3(1.055) * pow(linear, vec3(1.0 / 2.4)) - vec3(0.055); - let lower = linear * vec3(12.92); - return select(higher, lower, cutoff); -} - -/// Convert a linear color to sRGBA space. -fn linear_to_srgba(color: vec4) -> vec4 { - return vec4(linear_to_srgb(color.rgb), color.a); -} - -/// Convert a sRGBA color to linear space. -fn srgba_to_linear(color: vec4) -> vec4 { - return vec4(srgb_to_linear(color.rgb), color.a); -} - -/// Hsla to linear RGBA conversion. -fn hsla_to_rgba(hsla: Hsla) -> vec4 { - let h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range - let s = hsla.s; - let l = hsla.l; - let a = hsla.a; - - let c = (1.0 - abs(2.0 * l - 1.0)) * s; - let x = c * (1.0 - abs(h % 2.0 - 1.0)); - let m = l - c / 2.0; - var color = vec3(m); - - if (h >= 0.0 && h < 1.0) { - color.r += c; - color.g += x; - } else if (h >= 1.0 && h < 2.0) { - color.r += x; - color.g += c; - } else if (h >= 2.0 && h < 3.0) { - color.g += c; - color.b += x; - } else if (h >= 3.0 && h < 4.0) { - color.g += x; - color.b += c; - } else if (h >= 4.0 && h < 5.0) { - color.r += x; - color.b += c; - } else { - color.r += c; - color.b += x; - } - - return vec4(color, a); -} - -/// Convert a linear sRGB to Oklab space. -/// Reference: https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab -fn linear_srgb_to_oklab(color: vec4) -> vec4 { - let l = 0.4122214708 * color.r + 0.5363325363 * color.g + 0.0514459929 * color.b; - let m = 0.2119034982 * color.r + 0.6806995451 * color.g + 0.1073969566 * color.b; - let s = 0.0883024619 * color.r + 0.2817188376 * color.g + 0.6299787005 * color.b; - - let l_ = pow(l, 1.0 / 3.0); - let m_ = pow(m, 1.0 / 3.0); - let s_ = pow(s, 1.0 / 3.0); - - return vec4( - 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_, - 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_, - 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_, - color.a - ); -} - -/// Convert an Oklab color to linear sRGB space. -fn oklab_to_linear_srgb(color: vec4) -> vec4 { - let l_ = color.r + 0.3963377774 * color.g + 0.2158037573 * color.b; - let m_ = color.r - 0.1055613458 * color.g - 0.0638541728 * color.b; - let s_ = color.r - 0.0894841775 * color.g - 1.2914855480 * color.b; - - let l = l_ * l_ * l_; - let m = m_ * m_ * m_; - let s = s_ * s_ * s_; - - return vec4( - 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s, - -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s, - -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s, - color.a - ); -} - -fn over(below: vec4, above: vec4) -> vec4 { - let alpha = above.a + below.a * (1.0 - above.a); - let color = (above.rgb * above.a + below.rgb * below.a * (1.0 - above.a)) / alpha; - return vec4(color, alpha); -} - -// A standard gaussian function, used for weighting samples -fn gaussian(x: f32, sigma: f32) -> f32{ - return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * M_PI_F) * sigma); -} - -// This approximates the error function, needed for the gaussian integral -fn erf(v: vec2) -> vec2 { - let s = sign(v); - let a = abs(v); - let r1 = 1.0 + (0.278393 + (0.230389 + (0.000972 + 0.078108 * a) * a) * a) * a; - let r2 = r1 * r1; - return s - s / (r2 * r2); -} - -fn blur_along_x(x: f32, y: f32, sigma: f32, corner: f32, half_size: vec2) -> f32 { - let delta = min(half_size.y - corner - abs(y), 0.0); - let curved = half_size.x - corner + sqrt(max(0.0, corner * corner - delta * delta)); - let integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma)); - return integral.y - integral.x; -} - -// Selects corner radius based on quadrant. -fn pick_corner_radius(center_to_point: vec2, radii: Corners) -> f32 { - if (center_to_point.x < 0.0) { - if (center_to_point.y < 0.0) { - return radii.top_left; - } else { - return radii.bottom_left; - } - } else { - if (center_to_point.y < 0.0) { - return radii.top_right; - } else { - return radii.bottom_right; - } - } -} - -// Signed distance of the point to the quad's border - positive outside the -// border, and negative inside. -// -// See comments on similar code using `quad_sdf_impl` in `fs_quad` for -// explanation. -fn quad_sdf(point: vec2, bounds: Bounds, corner_radii: Corners) -> f32 { - let half_size = bounds.size / 2.0; - let center = bounds.origin + half_size; - let center_to_point = point - center; - let corner_radius = pick_corner_radius(center_to_point, corner_radii); - let corner_to_point = abs(center_to_point) - half_size; - let corner_center_to_point = corner_to_point + corner_radius; - return quad_sdf_impl(corner_center_to_point, corner_radius); -} - -fn quad_sdf_impl(corner_center_to_point: vec2, corner_radius: f32) -> f32 { - if (corner_radius == 0.0) { - // Fast path for unrounded corners. - return max(corner_center_to_point.x, corner_center_to_point.y); - } else { - // Signed distance of the point from a quad that is inset by corner_radius. - // It is negative inside this quad, and positive outside. - let signed_distance_to_inset_quad = - // 0 inside the inset quad, and positive outside. - length(max(vec2(0.0), corner_center_to_point)) + - // 0 outside the inset quad, and negative inside. - min(0.0, max(corner_center_to_point.x, corner_center_to_point.y)); - - return signed_distance_to_inset_quad - corner_radius; - } -} - -// Abstract away the final color transformation based on the -// target alpha compositing mode. -fn blend_color(color: vec4, alpha_factor: f32) -> vec4 { - let alpha = color.a * alpha_factor; - let multiplier = select(1.0, alpha, globals.premultiplied_alpha != 0u); - return vec4(color.rgb * multiplier, alpha); -} - - -struct GradientColor { - solid: vec4, - color0: vec4, - color1: vec4, -} - -fn prepare_gradient_color(tag: u32, color_space: u32, - solid: Hsla, colors: array) -> GradientColor { - var result = GradientColor(); - - if (tag == 0u || tag == 2u || tag == 3u) { - result.solid = hsla_to_rgba(solid); - } else if (tag == 1u) { - // The hsla_to_rgba is returns a linear sRGB color - result.color0 = hsla_to_rgba(colors[0].color); - result.color1 = hsla_to_rgba(colors[1].color); - - // Prepare color space in vertex for avoid conversion - // in fragment shader for performance reasons - if (color_space == 0u) { - // sRGB - result.color0 = linear_to_srgba(result.color0); - result.color1 = linear_to_srgba(result.color1); - } else if (color_space == 1u) { - // Oklab - result.color0 = linear_srgb_to_oklab(result.color0); - result.color1 = linear_srgb_to_oklab(result.color1); - } - } - - return result; -} - -fn gradient_color(background: Background, position: vec2, bounds: Bounds, - solid_color: vec4, color0: vec4, color1: vec4) -> vec4 { - var background_color = vec4(0.0); - - switch (background.tag) { - default: { - return solid_color; - } - case 1u: { - // Linear gradient background. - // -90 degrees to match the CSS gradient angle. - let angle = background.gradient_angle_or_pattern_height; - let radians = (angle % 360.0 - 90.0) * M_PI_F / 180.0; - var direction = vec2(cos(radians), sin(radians)); - let stop0_percentage = background.colors[0].percentage; - let stop1_percentage = background.colors[1].percentage; - - // Expand the short side to be the same as the long side - if (bounds.size.x > bounds.size.y) { - direction.y *= bounds.size.y / bounds.size.x; - } else { - direction.x *= bounds.size.x / bounds.size.y; - } - - // Get the t value for the linear gradient with the color stop percentages. - let half_size = bounds.size / 2.0; - let center = bounds.origin + half_size; - let center_to_point = position - center; - var t = dot(center_to_point, direction) / length(direction); - // Check the direct to determine the use x or y - if (abs(direction.x) > abs(direction.y)) { - t = (t + half_size.x) / bounds.size.x; - } else { - t = (t + half_size.y) / bounds.size.y; - } - - // Adjust t based on the stop percentages - t = (t - stop0_percentage) / (stop1_percentage - stop0_percentage); - t = clamp(t, 0.0, 1.0); - - switch (background.color_space) { - default: { - background_color = srgba_to_linear(mix(color0, color1, t)); - } - case 1u: { - let oklab_color = mix(color0, color1, t); - background_color = oklab_to_linear_srgb(oklab_color); - } - } - } - case 2u: { - // pattern slash - let gradient_angle_or_pattern_height = background.gradient_angle_or_pattern_height; - let pattern_width = (gradient_angle_or_pattern_height / 65535.0f) / 255.0f; - let pattern_interval = (gradient_angle_or_pattern_height % 65535.0f) / 255.0f; - let pattern_height = pattern_width + pattern_interval; - let stripe_angle = M_PI_F / 4.0; - let pattern_period = pattern_height * sin(stripe_angle); - let rotation = mat2x2( - cos(stripe_angle), -sin(stripe_angle), - sin(stripe_angle), cos(stripe_angle) - ); - let relative_position = position - bounds.origin; - let rotated_point = rotation * relative_position; - let pattern = rotated_point.x % pattern_period; - let distance = min(pattern, pattern_period - pattern) - pattern_period * (pattern_width / pattern_height) / 2.0f; - background_color = solid_color; - background_color.a *= saturate(0.5 - distance); - } - case 3u: { - // checkerboard - let size = background.gradient_angle_or_pattern_height; - let relative_position = position - bounds.origin; - - let x_index = floor(relative_position.x / size); - let y_index = floor(relative_position.y / size); - let should_be_colored = (x_index + y_index) % 2.0; - - background_color = solid_color; - background_color.a *= saturate(should_be_colored); - } - } - - return background_color; -} - -// --- quads --- // - -struct Quad { - order: u32, - border_style: u32, - bounds: Bounds, - content_mask: ContentMask, - background: Background, - border_color: Hsla, - corner_radii: Corners, - border_widths: Edges, -} - -struct QuadVarying { - @builtin(position) position: vec4, - @location(0) @interpolate(flat) border_color: vec4, - @location(1) @interpolate(flat) quad_id: u32, - // TODO: use `clip_distance` once Naga supports it - @location(2) clip_distances: vec4, - @location(3) @interpolate(flat) background_solid: vec4, - @location(4) @interpolate(flat) background_color0: vec4, - @location(5) @interpolate(flat) background_color1: vec4, - @location(6) @interpolate(flat) clip_mask_bounds: vec4, - @location(7) @interpolate(flat) clip_mask_radii: vec4, - @location(8) @interpolate(flat) clip_index: u32, -} - -@vertex -fn vs_quad(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> QuadVarying { - let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); - let quad = load_quad(instance_id); - - var out = QuadVarying(); - out.position = to_device_position(unit_vertex, quad.bounds); - - let gradient = prepare_gradient_color( - quad.background.tag, - quad.background.color_space, - quad.background.solid, - quad.background.colors - ); - out.background_solid = gradient.solid; - out.background_color0 = gradient.color0; - out.background_color1 = gradient.color1; - out.border_color = hsla_to_rgba(quad.border_color); - out.quad_id = instance_id; - out.clip_mask_bounds = vec4(quad.content_mask.bounds.origin, quad.content_mask.bounds.size); - out.clip_index = quad.content_mask.clip_index; - out.clip_mask_radii = vec4( - quad.content_mask.corner_radii.top_left, - quad.content_mask.corner_radii.top_right, - quad.content_mask.corner_radii.bottom_right, - quad.content_mask.corner_radii.bottom_left, - ); - out.clip_distances = distance_from_clip_rect(unit_vertex, quad.bounds, quad.content_mask); - return out; -} - -@fragment -fn fs_quad(input: QuadVarying) -> @location(0) vec4 { - let coverage = content_mask_coverage(input.position.xy, input.clip_mask_bounds, input.clip_mask_radii, input.clip_index); - // Alpha clip first, since we don't have `clip_distance`. - if (any(input.clip_distances < vec4(0.0)) - || coverage <= 0.0) { - return vec4(0.0); - } - - let quad = load_quad(input.quad_id); - - let background_color = gradient_color(quad.background, input.position.xy, quad.bounds, - input.background_solid, input.background_color0, input.background_color1); - - let unrounded = quad.corner_radii.top_left == 0.0 && - quad.corner_radii.bottom_left == 0.0 && - quad.corner_radii.top_right == 0.0 && - quad.corner_radii.bottom_right == 0.0; - - // Fast path when the quad is not rounded and doesn't have any border - if (quad.border_widths.top == 0.0 && - quad.border_widths.left == 0.0 && - quad.border_widths.right == 0.0 && - quad.border_widths.bottom == 0.0 && - unrounded) { - return blend_color(background_color, coverage); - } - - let size = quad.bounds.size; - let half_size = size / 2.0; - let point = input.position.xy - quad.bounds.origin; - let center_to_point = point - half_size; - - // Signed distance field threshold for inclusion of pixels. 0.5 is the - // minimum distance between the center of the pixel and the edge. - let antialias_threshold = 0.5; - - // Radius of the nearest corner - let corner_radius = pick_corner_radius(center_to_point, quad.corner_radii); - - // Width of the nearest borders - let border = vec2( - select( - quad.border_widths.right, - quad.border_widths.left, - center_to_point.x < 0.0), - select( - quad.border_widths.bottom, - quad.border_widths.top, - center_to_point.y < 0.0)); - - // 0-width borders are reduced so that `inner_sdf >= antialias_threshold`. - // The purpose of this is to not draw antialiasing pixels in this case. - let reduced_border = - vec2(select(border.x, -antialias_threshold, border.x == 0.0), - select(border.y, -antialias_threshold, border.y == 0.0)); - - // Vector from the corner of the quad bounds to the point, after mirroring - // the point into the bottom right quadrant. Both components are <= 0. - let corner_to_point = abs(center_to_point) - half_size; - - // Vector from the point to the center of the rounded corner's circle, also - // mirrored into bottom right quadrant. - let corner_center_to_point = corner_to_point + corner_radius; - - // Whether the nearest point on the border is rounded - let is_near_rounded_corner = - corner_center_to_point.x >= 0 && - corner_center_to_point.y >= 0; - - // Vector from straight border inner corner to point. - let straight_border_inner_corner_to_point = corner_to_point + reduced_border; - - // Whether the point is beyond the inner edge of the straight border. - let is_beyond_inner_straight_border = - straight_border_inner_corner_to_point.x > 0 || - straight_border_inner_corner_to_point.y > 0; - - // Whether the point is far enough inside the quad, such that the pixels are - // not affected by the straight border. - let is_within_inner_straight_border = - straight_border_inner_corner_to_point.x < -antialias_threshold && - straight_border_inner_corner_to_point.y < -antialias_threshold; - - // Fast path for points that must be part of the background. - // - // This could be optimized further for large rounded corners by including - // points in an inscribed rectangle, or some other quick linear check. - // However, that might negatively impact performance in the case of - // reasonable sizes for rounded corners. - if (is_within_inner_straight_border && !is_near_rounded_corner) { - return blend_color(background_color, coverage); - } - - // Signed distance of the point to the outside edge of the quad's border. It - // is positive outside this edge, and negative inside. - let outer_sdf = quad_sdf_impl(corner_center_to_point, corner_radius); - - // Approximate signed distance of the point to the inside edge of the quad's - // border. It is negative outside this edge (within the border), and - // positive inside. - // - // This is not always an accurate signed distance: - // * The rounded portions with varying border width use an approximation of - // nearest-point-on-ellipse. - // * When it is quickly known to be outside the edge, -1.0 is used. - var inner_sdf = 0.0; - if (corner_center_to_point.x <= 0 || corner_center_to_point.y <= 0) { - // Fast paths for straight borders. - inner_sdf = -max(straight_border_inner_corner_to_point.x, - straight_border_inner_corner_to_point.y); - } else if (is_beyond_inner_straight_border) { - // Fast path for points that must be outside the inner edge. - inner_sdf = -1.0; - } else if (reduced_border.x == reduced_border.y) { - // Fast path for circular inner edge. - inner_sdf = -(outer_sdf + reduced_border.x); - } else { - let ellipse_radii = max(vec2(0.0), corner_radius - reduced_border); - inner_sdf = quarter_ellipse_sdf(corner_center_to_point, ellipse_radii); - } - - // Negative when inside the border - let border_sdf = max(inner_sdf, outer_sdf); - - var color = background_color; - if (border_sdf < antialias_threshold) { - var border_color = input.border_color; - - // Dashed border logic when border_style == 1 - if (quad.border_style == 1) { - // Position along the perimeter in "dash space", where each dash - // period has length 1 - var t = 0.0; - - // Total number of dash periods, so that the dash spacing can be - // adjusted to evenly divide it - var max_t = 0.0; - - // Border width is proportional to dash size. This is the behavior - // used by browsers, but also avoids dashes from different segments - // overlapping when dash size is smaller than the border width. - // - // Dash pattern: (2 * border width) dash, (1 * border width) gap - let dash_length_per_width = 2.0; - let dash_gap_per_width = 1.0; - let dash_period_per_width = dash_length_per_width + dash_gap_per_width; - - // Since the dash size is determined by border width, the density of - // dashes varies. Multiplying a pixel distance by this returns a - // position in dash space - it has units (dash period / pixels). So - // a dash velocity of (1 / 10) is 1 dash every 10 pixels. - var dash_velocity = 0.0; - - // Dividing this by the border width gives the dash velocity - let dv_numerator = 1.0 / dash_period_per_width; - - if (unrounded) { - // When corners aren't rounded, the dashes are separately laid - // out on each straight line, rather than around the whole - // perimeter. This way each line starts and ends with a dash. - let is_horizontal = - corner_center_to_point.x < - corner_center_to_point.y; - - // When applying dashed borders to just some, not all, the sides. - // The way we chose border widths above sometimes comes with a 0 width value. - // So we choose again to avoid division by zero. - // TODO: A better solution exists taking a look at the whole file. - // this does not fix single dashed borders at the corners - let dashed_border = vec2( - max( - quad.border_widths.bottom, - quad.border_widths.top, - ), - max( - quad.border_widths.right, - quad.border_widths.left, - ) - ); - - let border_width = select(dashed_border.y, dashed_border.x, is_horizontal); - dash_velocity = dv_numerator / border_width; - t = select(point.y, point.x, is_horizontal) * dash_velocity; - max_t = select(size.y, size.x, is_horizontal) * dash_velocity; - } else { - // When corners are rounded, the dashes are laid out clockwise - // around the whole perimeter. - - let r_tr = quad.corner_radii.top_right; - let r_br = quad.corner_radii.bottom_right; - let r_bl = quad.corner_radii.bottom_left; - let r_tl = quad.corner_radii.top_left; - - let w_t = quad.border_widths.top; - let w_r = quad.border_widths.right; - let w_b = quad.border_widths.bottom; - let w_l = quad.border_widths.left; - - // Straight side dash velocities - let dv_t = select(dv_numerator / w_t, 0.0, w_t <= 0.0); - let dv_r = select(dv_numerator / w_r, 0.0, w_r <= 0.0); - let dv_b = select(dv_numerator / w_b, 0.0, w_b <= 0.0); - let dv_l = select(dv_numerator / w_l, 0.0, w_l <= 0.0); - - // Straight side lengths in dash space - let s_t = (size.x - r_tl - r_tr) * dv_t; - let s_r = (size.y - r_tr - r_br) * dv_r; - let s_b = (size.x - r_br - r_bl) * dv_b; - let s_l = (size.y - r_bl - r_tl) * dv_l; - - let corner_dash_velocity_tr = corner_dash_velocity(dv_t, dv_r); - let corner_dash_velocity_br = corner_dash_velocity(dv_b, dv_r); - let corner_dash_velocity_bl = corner_dash_velocity(dv_b, dv_l); - let corner_dash_velocity_tl = corner_dash_velocity(dv_t, dv_l); - - // Corner lengths in dash space - let c_tr = r_tr * (M_PI_F / 2.0) * corner_dash_velocity_tr; - let c_br = r_br * (M_PI_F / 2.0) * corner_dash_velocity_br; - let c_bl = r_bl * (M_PI_F / 2.0) * corner_dash_velocity_bl; - let c_tl = r_tl * (M_PI_F / 2.0) * corner_dash_velocity_tl; - - // Cumulative dash space upto each segment - let upto_tr = s_t; - let upto_r = upto_tr + c_tr; - let upto_br = upto_r + s_r; - let upto_b = upto_br + c_br; - let upto_bl = upto_b + s_b; - let upto_l = upto_bl + c_bl; - let upto_tl = upto_l + s_l; - max_t = upto_tl + c_tl; - - if (is_near_rounded_corner) { - let radians = atan2(corner_center_to_point.y, - corner_center_to_point.x); - let corner_t = radians * corner_radius; - - if (center_to_point.x >= 0.0) { - if (center_to_point.y < 0.0) { - dash_velocity = corner_dash_velocity_tr; - // Subtracted because radians is pi/2 to 0 when - // going clockwise around the top right corner, - // since the y axis has been flipped - t = upto_r - corner_t * dash_velocity; - } else { - dash_velocity = corner_dash_velocity_br; - // Added because radians is 0 to pi/2 when going - // clockwise around the bottom-right corner - t = upto_br + corner_t * dash_velocity; - } - } else { - if (center_to_point.y >= 0.0) { - dash_velocity = corner_dash_velocity_bl; - // Subtracted because radians is pi/2 to 0 when - // going clockwise around the bottom-left corner, - // since the x axis has been flipped - t = upto_l - corner_t * dash_velocity; - } else { - dash_velocity = corner_dash_velocity_tl; - // Added because radians is 0 to pi/2 when going - // clockwise around the top-left corner, since both - // axis were flipped - t = upto_tl + corner_t * dash_velocity; - } - } - } else { - // Straight borders - let is_horizontal = - corner_center_to_point.x < - corner_center_to_point.y; - if (is_horizontal) { - if (center_to_point.y < 0.0) { - dash_velocity = dv_t; - t = (point.x - r_tl) * dash_velocity; - } else { - dash_velocity = dv_b; - t = upto_bl - (point.x - r_bl) * dash_velocity; - } - } else { - if (center_to_point.x < 0.0) { - dash_velocity = dv_l; - t = upto_tl - (point.y - r_tl) * dash_velocity; - } else { - dash_velocity = dv_r; - t = upto_r + (point.y - r_tr) * dash_velocity; - } - } - } - } - - let dash_length = dash_length_per_width / dash_period_per_width; - let desired_dash_gap = dash_gap_per_width / dash_period_per_width; - - // Straight borders should start and end with a dash, so max_t is - // reduced to cause this. - max_t -= select(0.0, dash_length, unrounded); - if (max_t >= 1.0) { - // Adjust dash gap to evenly divide max_t. - let dash_count = floor(max_t); - let dash_period = max_t / dash_count; - border_color.a *= dash_alpha( - t, - dash_period, - dash_length, - dash_velocity, - antialias_threshold); - } else if (unrounded) { - // When there isn't enough space for the full gap between the - // two start / end dashes of a straight border, reduce gap to - // make them fit. - let dash_gap = max_t - dash_length; - if (dash_gap > 0.0) { - let dash_period = dash_length + dash_gap; - border_color.a *= dash_alpha( - t, - dash_period, - dash_length, - dash_velocity, - antialias_threshold); - } - } - } - - // Blend the border on top of the background and then linearly interpolate - // between the two as we slide inside the background. - let blended_border = over(background_color, border_color); - color = mix(background_color, blended_border, - saturate(antialias_threshold - inner_sdf)); - } - - return blend_color(color, min(coverage, saturate(antialias_threshold - outer_sdf))); -} - -// Returns the dash velocity of a corner given the dash velocity of the two -// sides, by returning the slower velocity (larger dashes). -// -// Since 0 is used for dash velocity when the border width is 0 (instead of -// +inf), this returns the other dash velocity in that case. -// -// An alternative to this might be to appropriately interpolate the dash -// velocity around the corner, but that seems overcomplicated. -fn corner_dash_velocity(dv1: f32, dv2: f32) -> f32 { - if (dv1 == 0.0) { - return dv2; - } else if (dv2 == 0.0) { - return dv1; - } else { - return min(dv1, dv2); - } -} - -// Returns alpha used to render antialiased dashes. -// `t` is within the dash when `fmod(t, period) < length`. -fn dash_alpha(t: f32, period: f32, length: f32, dash_velocity: f32, antialias_threshold: f32) -> f32 { - let half_period = period / 2; - let half_length = length / 2; - // Value in [-half_period, half_period]. - // The dash is in [-half_length, half_length]. - let centered = fmod(t + half_period - half_length, period) - half_period; - // Signed distance for the dash, negative values are inside the dash. - let signed_distance = abs(centered) - half_length; - // Antialiased alpha based on the signed distance. - return saturate(antialias_threshold - signed_distance / dash_velocity); -} - -// This approximates distance to the nearest point to a quarter ellipse in a way -// that is sufficient for anti-aliasing when the ellipse is not very eccentric. -// The components of `point` are expected to be positive. -// -// Negative on the outside and positive on the inside. -fn quarter_ellipse_sdf(point: vec2, radii: vec2) -> f32 { - // Scale the space to treat the ellipse like a unit circle. - let circle_vec = point / radii; - let unit_circle_sdf = length(circle_vec) - 1.0; - // Approximate up-scaling of the length by using the average of the radii. - // - // TODO: A better solution would be to use the gradient of the implicit - // function for an ellipse to approximate a scaling factor. - return unit_circle_sdf * (radii.x + radii.y) * -0.5; -} - -// Modulus that has the same sign as `a`. -fn fmod(a: f32, b: f32) -> f32 { - return a - b * trunc(a / b); -} - -// --- shadows --- // - -struct Shadow { - order: u32, - blur_radius: f32, - // The shadow rect for drop shadows; the "hole" rect for inset shadows. - bounds: Bounds, - corner_radii: Corners, - content_mask: ContentMask, - color: Hsla, - // Only consulted when `inset == 1u`: the element's own bounds, used as a rounded-rect - // clip so the shadow never escapes the element. - element_bounds: Bounds, - element_corner_radii: Corners, - // 0 = drop shadow, 1 = inset shadow. - inset: u32, - pad: u32, // align to 8 bytes -} - -struct ShadowVarying { - @builtin(position) position: vec4, - @location(0) @interpolate(flat) color: vec4, - @location(1) @interpolate(flat) shadow_id: u32, - //TODO: use `clip_distance` once Naga supports it - @location(3) clip_distances: vec4, - @location(4) @interpolate(flat) clip_mask_bounds: vec4, - @location(5) @interpolate(flat) clip_mask_radii: vec4, - @location(6) @interpolate(flat) clip_index: u32, -} - -@vertex -fn vs_shadow(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> ShadowVarying { - let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); - var shadow = load_shadow(instance_id); - - var geometry: Bounds; - if (shadow.inset != 0u) { - geometry = shadow.element_bounds; - } else { - // Leave room for the gaussian tail outside the shadow rect. - let margin = 3.0 * shadow.blur_radius; - geometry = shadow.bounds; - geometry.origin -= vec2(margin); - geometry.size += 2.0 * vec2(margin); - } - - var out = ShadowVarying(); - out.position = to_device_position(unit_vertex, geometry); - out.color = hsla_to_rgba(shadow.color); - out.shadow_id = instance_id; - out.clip_mask_bounds = vec4(shadow.content_mask.bounds.origin, shadow.content_mask.bounds.size); - out.clip_index = shadow.content_mask.clip_index; - out.clip_mask_radii = vec4( - shadow.content_mask.corner_radii.top_left, - shadow.content_mask.corner_radii.top_right, - shadow.content_mask.corner_radii.bottom_right, - shadow.content_mask.corner_radii.bottom_left, - ); - out.clip_distances = distance_from_clip_rect(unit_vertex, geometry, shadow.content_mask); - return out; -} - -@fragment -fn fs_shadow(input: ShadowVarying) -> @location(0) vec4 { - let coverage = content_mask_coverage(input.position.xy, input.clip_mask_bounds, input.clip_mask_radii, input.clip_index); - // Alpha clip first, since we don't have `clip_distance`. - if (any(input.clip_distances < vec4(0.0)) - || coverage <= 0.0) { - return vec4(0.0); - } - - let shadow = load_shadow(input.shadow_id); - let half_size = shadow.bounds.size / 2.0; - let center = shadow.bounds.origin + half_size; - let center_to_point = input.position.xy - center; - - let corner_radius = pick_corner_radius(center_to_point, shadow.corner_radii); - - var alpha: f32; - if (shadow.blur_radius == 0.0) { - let distance = quad_sdf(input.position.xy, shadow.bounds, shadow.corner_radii); - alpha = saturate(0.5 - distance); - } else { - // The signal is only non-zero in a limited range, so don't waste samples - let low = center_to_point.y - half_size.y; - let high = center_to_point.y + half_size.y; - let start = clamp(-3.0 * shadow.blur_radius, low, high); - let end = clamp(3.0 * shadow.blur_radius, low, high); - - // Accumulate samples (we can get away with surprisingly few samples) - let step = (end - start) / 4.0; - var y = start + step * 0.5; - alpha = 0.0; - for (var i = 0; i < 4; i += 1) { - let blur = blur_along_x(center_to_point.x, center_to_point.y - y, - shadow.blur_radius, corner_radius, half_size); - alpha += blur * gaussian(y, shadow.blur_radius) * step; - y += step; - } - } - - if (shadow.inset != 0u) { - // The inset shadow is the complement of the (blurred) hole rect, clipped to the element. - // `saturate(0.5 - d)` gives a 1-pixel antialiased edge: d <= -0.5 -> 1, d >= 0.5 -> 0. - alpha = 1.0 - alpha; - let element_distance = quad_sdf(input.position.xy, shadow.element_bounds, - shadow.element_corner_radii); - alpha *= saturate(0.5 - element_distance); - } - - return blend_color(input.color, alpha * coverage); -} - -// --- path rasterization --- // - -struct PathRasterizationVertex { - xy_position: vec2, - st_position: vec2, - color: Background, - bounds: Bounds, - content_mask: ContentMask, -} - - - -struct PathRasterizationVarying { - @builtin(position) position: vec4, - @location(0) st_position: vec2, - @location(1) @interpolate(flat) vertex_id: u32, - //TODO: use `clip_distance` once Naga supports it - @location(3) clip_distances: vec4, - @location(4) @interpolate(flat) clip_mask_bounds: vec4, - @location(5) @interpolate(flat) clip_mask_radii: vec4, - @location(6) @interpolate(flat) clip_index: u32, -} - -@vertex -fn vs_path_rasterization(@builtin(vertex_index) vertex_id: u32) -> PathRasterizationVarying { - let v = load_path_vertex(vertex_id); - - var out = PathRasterizationVarying(); - out.position = to_device_position_impl(v.xy_position); - out.st_position = v.st_position; - out.vertex_id = vertex_id; - out.clip_distances = distance_from_clip_bounds_impl(v.xy_position, v.bounds); - out.clip_mask_bounds = vec4(v.content_mask.bounds.origin, v.content_mask.bounds.size); - out.clip_index = v.content_mask.clip_index; - out.clip_mask_radii = vec4( - v.content_mask.corner_radii.top_left, - v.content_mask.corner_radii.top_right, - v.content_mask.corner_radii.bottom_right, - v.content_mask.corner_radii.bottom_left, - ); - return out; -} - -@fragment -fn fs_path_rasterization(input: PathRasterizationVarying) -> @location(0) vec4 { - let coverage = content_mask_coverage(input.position.xy, input.clip_mask_bounds, input.clip_mask_radii, input.clip_index); - let dx = dpdx(input.st_position); - let dy = dpdy(input.st_position); - if (any(input.clip_distances < vec4(0.0)) - || coverage <= 0.0) { - return vec4(0.0); - } - - let v = load_path_vertex(input.vertex_id); - let background = v.color; - let bounds = v.bounds; - - var alpha: f32; - if (length(vec2(dx.x, dy.x)) < 0.001) { - // If the gradient is too small, return a solid color. - alpha = 1.0; - } else { - let gradient = 2.0 * input.st_position.xx * vec2(dx.x, dy.x) - vec2(dx.y, dy.y); - let f = input.st_position.x * input.st_position.x - input.st_position.y; - let distance = f / length(gradient); - alpha = saturate(0.5 - distance); - } - let prepared_gradient = prepare_gradient_color( - background.tag, - background.color_space, - background.solid, - background.colors, - ); - let color = gradient_color(background, input.position.xy, bounds, - prepared_gradient.solid, prepared_gradient.color0, prepared_gradient.color1); - return vec4(color.rgb * color.a * alpha, color.a * alpha) * coverage; -} - -// --- paths --- // - -struct PathSprite { - bounds: Bounds, -} - - -struct PathVarying { - @builtin(position) position: vec4, - @location(0) texture_coords: vec2, -} - -@vertex -fn vs_path(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> PathVarying { - let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); - let sprite = load_path_sprite(instance_id); - // Don't apply content mask because it was already accounted for when rasterizing the path. - let device_position = to_device_position(unit_vertex, sprite.bounds); - // For screen-space intermediate texture, convert screen position to texture coordinates - let screen_position = sprite.bounds.origin + unit_vertex * sprite.bounds.size; - let texture_coords = screen_position / globals.viewport_size; - - var out = PathVarying(); - out.position = device_position; - out.texture_coords = texture_coords; - - return out; -} - -@fragment -fn fs_path(input: PathVarying) -> @location(0) vec4 { - let sample = textureSample(t_sprite, s_sprite, input.texture_coords); - return sample; -} - -// --- underlines --- // - -struct Underline { - order: u32, - pad: u32, - bounds: Bounds, - content_mask: ContentMask, - color: Hsla, - thickness: f32, - wavy: u32, -} - - -struct UnderlineVarying { - @builtin(position) position: vec4, - @location(0) @interpolate(flat) color: vec4, - @location(1) @interpolate(flat) underline_id: u32, - //TODO: use `clip_distance` once Naga supports it - @location(3) clip_distances: vec4, - @location(4) @interpolate(flat) clip_mask_bounds: vec4, - @location(5) @interpolate(flat) clip_mask_radii: vec4, - @location(6) @interpolate(flat) clip_index: u32, -} - -@vertex -fn vs_underline(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> UnderlineVarying { - let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); - let underline = load_underline(instance_id); - - var out = UnderlineVarying(); - out.position = to_device_position(unit_vertex, underline.bounds); - out.color = hsla_to_rgba(underline.color); - out.underline_id = instance_id; - out.clip_mask_bounds = vec4(underline.content_mask.bounds.origin, underline.content_mask.bounds.size); - out.clip_index = underline.content_mask.clip_index; - out.clip_mask_radii = vec4( - underline.content_mask.corner_radii.top_left, - underline.content_mask.corner_radii.top_right, - underline.content_mask.corner_radii.bottom_right, - underline.content_mask.corner_radii.bottom_left, - ); - out.clip_distances = distance_from_clip_rect(unit_vertex, underline.bounds, underline.content_mask); - return out; -} - -@fragment -fn fs_underline(input: UnderlineVarying) -> @location(0) vec4 { - let coverage = content_mask_coverage(input.position.xy, input.clip_mask_bounds, input.clip_mask_radii, input.clip_index); - const WAVE_FREQUENCY: f32 = 2.0; - const WAVE_HEIGHT_RATIO: f32 = 0.8; - - // Alpha clip first, since we don't have `clip_distance`. - if (any(input.clip_distances < vec4(0.0)) - || coverage <= 0.0) { - return vec4(0.0); - } - - let underline = load_underline(input.underline_id); - if (underline.wavy == 0u) - { - return blend_color(input.color, input.color.a * coverage); - } - - let half_thickness = underline.thickness * 0.5; - - let st = (input.position.xy - underline.bounds.origin) / underline.bounds.size.y - vec2(0.0, 0.5); - let frequency = M_PI_F * WAVE_FREQUENCY * underline.thickness / underline.bounds.size.y; - let amplitude = (underline.thickness * WAVE_HEIGHT_RATIO) / underline.bounds.size.y; - - let sine = sin(st.x * frequency) * amplitude; - let dSine = cos(st.x * frequency) * amplitude * frequency; - let distance = (st.y - sine) / sqrt(1.0 + dSine * dSine); - let distance_in_pixels = distance * underline.bounds.size.y; - let distance_from_top_border = distance_in_pixels - half_thickness; - let distance_from_bottom_border = distance_in_pixels + half_thickness; - let alpha = saturate(0.5 - max(-distance_from_bottom_border, distance_from_top_border)); - return blend_color(input.color, alpha * input.color.a * coverage); -} - -// --- monochrome sprites --- // - -struct MonochromeSprite { - order: u32, - pad: u32, - bounds: Bounds, - content_mask: ContentMask, - color: Hsla, - tile: AtlasTile, - transformation: TransformationMatrix, -} - - -struct MonoSpriteVarying { - @builtin(position) position: vec4, - @location(0) tile_position: vec2, - @location(1) @interpolate(flat) color: vec4, - @location(3) clip_distances: vec4, - @location(4) @interpolate(flat) clip_mask_bounds: vec4, - @location(5) @interpolate(flat) clip_mask_radii: vec4, - @location(6) @interpolate(flat) clip_index: u32, -} - -@vertex -fn vs_mono_sprite(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> MonoSpriteVarying { - let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); - let sprite = load_mono_sprite(instance_id); - - var out = MonoSpriteVarying(); - out.position = to_device_position_transformed(unit_vertex, sprite.bounds, sprite.transformation); - - out.tile_position = to_tile_position(unit_vertex, sprite.tile); - out.color = hsla_to_rgba(sprite.color); - out.clip_mask_bounds = vec4(sprite.content_mask.bounds.origin, sprite.content_mask.bounds.size); - out.clip_index = sprite.content_mask.clip_index; - out.clip_mask_radii = vec4( - sprite.content_mask.corner_radii.top_left, - sprite.content_mask.corner_radii.top_right, - sprite.content_mask.corner_radii.bottom_right, - sprite.content_mask.corner_radii.bottom_left, - ); - out.clip_distances = distance_from_clip_rect_transformed(unit_vertex, sprite.bounds, sprite.content_mask, sprite.transformation); - return out; -} - -@fragment -fn fs_mono_sprite(input: MonoSpriteVarying) -> @location(0) vec4 { - let coverage = content_mask_coverage(input.position.xy, input.clip_mask_bounds, input.clip_mask_radii, input.clip_index); - let sample = textureSample(t_sprite, s_sprite, input.tile_position).r; - let alpha_corrected = apply_contrast_and_gamma_correction(sample, input.color.rgb, gamma_params.grayscale_enhanced_contrast, gamma_params.gamma_ratios); - - // Alpha clip after using the derivatives. - if (any(input.clip_distances < vec4(0.0)) - || coverage <= 0.0) { - return vec4(0.0); - } - - return blend_color(input.color, alpha_corrected * coverage); -} - -// --- polychrome sprites --- // - -struct PolychromeSprite { - order: u32, - pad: u32, - grayscale: u32, - opacity: f32, - bounds: Bounds, - content_mask: ContentMask, - corner_radii: Corners, - tile: AtlasTile, -} - - -struct PolySpriteVarying { - @builtin(position) position: vec4, - @location(0) tile_position: vec2, - @location(1) @interpolate(flat) sprite_id: u32, - @location(3) clip_distances: vec4, - @location(4) @interpolate(flat) clip_mask_bounds: vec4, - @location(5) @interpolate(flat) clip_mask_radii: vec4, - @location(6) @interpolate(flat) clip_index: u32, -} - -@vertex -fn vs_poly_sprite(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> PolySpriteVarying { - let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); - let sprite = load_poly_sprite(instance_id); - - var out = PolySpriteVarying(); - out.position = to_device_position(unit_vertex, sprite.bounds); - out.tile_position = to_tile_position(unit_vertex, sprite.tile); - out.sprite_id = instance_id; - out.clip_mask_bounds = vec4(sprite.content_mask.bounds.origin, sprite.content_mask.bounds.size); - out.clip_index = sprite.content_mask.clip_index; - out.clip_mask_radii = vec4( - sprite.content_mask.corner_radii.top_left, - sprite.content_mask.corner_radii.top_right, - sprite.content_mask.corner_radii.bottom_right, - sprite.content_mask.corner_radii.bottom_left, - ); - out.clip_distances = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask); - return out; -} - -@fragment -fn fs_poly_sprite(input: PolySpriteVarying) -> @location(0) vec4 { - let coverage = content_mask_coverage(input.position.xy, input.clip_mask_bounds, input.clip_mask_radii, input.clip_index); - let sample = textureSample(t_sprite, s_sprite, input.tile_position); - // Alpha clip after using the derivatives. - if (any(input.clip_distances < vec4(0.0)) - || coverage <= 0.0) { - return vec4(0.0); - } - - let sprite = load_poly_sprite(input.sprite_id); - let distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii); - - var color = sample; - if (sprite.grayscale != 0u) { - let grayscale = dot(color.rgb, GRAYSCALE_FACTORS); - color = vec4(vec3(grayscale), sample.a); - } - return blend_color(color, sprite.opacity * min(coverage, saturate(0.5 - distance))); -} - -// --- surfaces --- // - -struct SurfaceParams { - bounds: Bounds, - @size(48) content_mask: ContentMask, -} - -@group(1) @binding(0) var surface_locals: SurfaceParams; -@group(1) @binding(1) var t_y: texture_2d; -@group(1) @binding(2) var t_cb_cr: texture_2d; -@group(1) @binding(3) var s_surface: sampler; - -const ycbcr_to_RGB = mat4x4( - vec4( 1.0000f, 1.0000f, 1.0000f, 0.0), - vec4( 0.0000f, -0.3441f, 1.7720f, 0.0), - vec4( 1.4020f, -0.7141f, 0.0000f, 0.0), - vec4(-0.7010f, 0.5291f, -0.8860f, 1.0), -); - -struct SurfaceVarying { - @builtin(position) position: vec4, - @location(0) texture_position: vec2, - @location(3) clip_distances: vec4, - @location(4) @interpolate(flat) clip_mask_bounds: vec4, - @location(5) @interpolate(flat) clip_mask_radii: vec4, - @location(6) @interpolate(flat) clip_index: u32, -} - -@vertex -fn vs_surface(@builtin(vertex_index) vertex_id: u32) -> SurfaceVarying { - let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); - - var out = SurfaceVarying(); - out.position = to_device_position(unit_vertex, surface_locals.bounds); - out.texture_position = unit_vertex; - out.clip_mask_bounds = vec4(surface_locals.content_mask.bounds.origin, surface_locals.content_mask.bounds.size); - out.clip_index = surface_locals.content_mask.clip_index; - out.clip_mask_radii = vec4( - surface_locals.content_mask.corner_radii.top_left, - surface_locals.content_mask.corner_radii.top_right, - surface_locals.content_mask.corner_radii.bottom_right, - surface_locals.content_mask.corner_radii.bottom_left, - ); - out.clip_distances = distance_from_clip_rect(unit_vertex, surface_locals.bounds, surface_locals.content_mask); - return out; -} - -@fragment -fn fs_surface(input: SurfaceVarying) -> @location(0) vec4 { - let coverage = content_mask_coverage(input.position.xy, input.clip_mask_bounds, input.clip_mask_radii, input.clip_index); - // Alpha clip after using the derivatives. - if (any(input.clip_distances < vec4(0.0)) - || coverage <= 0.0) { - return vec4(0.0); - } - - let y_cb_cr = vec4( - textureSampleLevel(t_y, s_surface, input.texture_position, 0.0).r, - textureSampleLevel(t_cb_cr, s_surface, input.texture_position, 0.0).rg, - 1.0); - - return blend_color(ycbcr_to_RGB * y_cb_cr, coverage); -} diff --git a/crates/gpui_pre_wgpu/src/shaders_storage.wgsl b/crates/gpui_pre_wgpu/src/shaders_storage.wgsl deleted file mode 100644 index 6f74e0e1..00000000 --- a/crates/gpui_pre_wgpu/src/shaders_storage.wgsl +++ /dev/null @@ -1,48 +0,0 @@ -// Storage-buffer instance transport. Concatenated after `shaders.wgsl` on backends -// with storage buffer support; `shaders_webgl.wgsl` is the WebGL2 counterpart that -// decodes the same records from a texture instead. -// -// All buffers share `@group(1) @binding(0)` because each pipeline binds only the -// buffer its own entry points read. - -@group(1) @binding(0) var b_quads: array; -@group(1) @binding(0) var b_shadows: array; -@group(1) @binding(0) var b_path_vertices: array; -@group(1) @binding(0) var b_path_sprites: array; -@group(1) @binding(0) var b_underlines: array; -@group(1) @binding(0) var b_mono_sprites: array; -@group(1) @binding(0) var b_poly_sprites: array; - -fn load_quad(instance_id: u32) -> Quad { - return b_quads[instance_id]; -} - -fn load_shadow(instance_id: u32) -> Shadow { - return b_shadows[instance_id]; -} - -fn load_path_vertex(vertex_id: u32) -> PathRasterizationVertex { - return b_path_vertices[vertex_id]; -} - -fn load_path_sprite(instance_id: u32) -> PathSprite { - return b_path_sprites[instance_id]; -} - -fn load_underline(instance_id: u32) -> Underline { - return b_underlines[instance_id]; -} - -fn load_mono_sprite(instance_id: u32) -> MonochromeSprite { - return b_mono_sprites[instance_id]; -} - -fn load_poly_sprite(instance_id: u32) -> PolychromeSprite { - return b_poly_sprites[instance_id]; -} - -@group(3) @binding(0) var b_clips: array; - -fn load_clip(index: u32) -> RoundedClip { - return b_clips[index]; -} diff --git a/crates/gpui_pre_wgpu/src/shaders_subpixel.wgsl b/crates/gpui_pre_wgpu/src/shaders_subpixel.wgsl deleted file mode 100644 index 91213844..00000000 --- a/crates/gpui_pre_wgpu/src/shaders_subpixel.wgsl +++ /dev/null @@ -1,69 +0,0 @@ -// --- subpixel sprites --- // - -struct SubpixelSprite { - order: u32, - pad: u32, - bounds: Bounds, - content_mask: ContentMask, - color: Hsla, - tile: AtlasTile, - transformation: TransformationMatrix, -} -@group(1) @binding(0) var b_subpixel_sprites: array; - -struct SubpixelSpriteOutput { - @builtin(position) position: vec4, - @location(0) tile_position: vec2, - @location(1) @interpolate(flat) color: vec4, - @location(3) clip_distances: vec4, - @location(4) @interpolate(flat) clip_mask_bounds: vec4, - @location(5) @interpolate(flat) clip_mask_radii: vec4, - @location(6) @interpolate(flat) clip_index: u32, -} - -struct SubpixelSpriteFragmentOutput { - @location(0) @blend_src(0) foreground: vec4, - @location(0) @blend_src(1) alpha: vec4, -} - -@vertex -fn vs_subpixel_sprite(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> SubpixelSpriteOutput { - let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); - let sprite = b_subpixel_sprites[instance_id]; - - var out = SubpixelSpriteOutput(); - out.position = to_device_position_transformed(unit_vertex, sprite.bounds, sprite.transformation); - out.tile_position = to_tile_position(unit_vertex, sprite.tile); - out.color = hsla_to_rgba(sprite.color); - out.clip_mask_bounds = vec4(sprite.content_mask.bounds.origin, sprite.content_mask.bounds.size); - out.clip_index = sprite.content_mask.clip_index; - out.clip_mask_radii = vec4( - sprite.content_mask.corner_radii.top_left, - sprite.content_mask.corner_radii.top_right, - sprite.content_mask.corner_radii.bottom_right, - sprite.content_mask.corner_radii.bottom_left, - ); - out.clip_distances = distance_from_clip_rect_transformed(unit_vertex, sprite.bounds, sprite.content_mask, sprite.transformation); - return out; -} - -@fragment -fn fs_subpixel_sprite(input: SubpixelSpriteOutput) -> SubpixelSpriteFragmentOutput { - let coverage = content_mask_coverage(input.position.xy, input.clip_mask_bounds, input.clip_mask_radii, input.clip_index); - var sample = textureSample(t_sprite, s_sprite, input.tile_position).rgb; - if (gamma_params.is_bgr != 0u) { - sample = sample.bgr; - } - let alpha_corrected = apply_contrast_and_gamma_correction3(sample, input.color.rgb, gamma_params.subpixel_enhanced_contrast, gamma_params.gamma_ratios); - - // Alpha clip after using the derivatives. - if (any(input.clip_distances < vec4(0.0)) - || coverage <= 0.0) { - return SubpixelSpriteFragmentOutput(vec4(0.0), vec4(0.0)); - } - - var out = SubpixelSpriteFragmentOutput(); - out.foreground = vec4(input.color.rgb, 1.0); - out.alpha = vec4(input.color.a * alpha_corrected * coverage, coverage); - return out; -} diff --git a/crates/gpui_pre_wgpu/src/shaders_webgl.wgsl b/crates/gpui_pre_wgpu/src/shaders_webgl.wgsl deleted file mode 100644 index d36d0787..00000000 --- a/crates/gpui_pre_wgpu/src/shaders_webgl.wgsl +++ /dev/null @@ -1,265 +0,0 @@ -@group(1) @binding(0) var t_instances: texture_2d; -@group(3) @binding(0) var t_clips: texture_2d; - -// Each texel of `t_instances` packs four 32-bit words of instance data. Records -// are read strictly front to back, so all readers share a cursor that keeps the -// most recently fetched texel and only touches the texture again when the next -// word crosses a texel boundary. This fetches each texel exactly once per -// record load instead of once per word. The `read_*` functions must therefore -// consume their words in struct declaration order; skipping or re-reading words -// would still return correct data (the cursor re-fetches whenever the texel -// index changes) but would forfeit the single-fetch-per-texel guarantee. -struct InstanceCursor { - word_index: u32, - texel_index: u32, - texel: vec4, - width: u32, -} - -fn fetch_instance_texel(texel_index: u32, width: u32) -> vec4 { - let coordinate = vec2( - i32(texel_index % width), - i32(texel_index / width), - ); - return textureLoad(t_instances, coordinate, 0); -} - -fn instance_cursor(word_index: u32) -> InstanceCursor { - let width = textureDimensions(t_instances).x; - let texel_index = word_index / 4u; - return InstanceCursor( - word_index, - texel_index, - fetch_instance_texel(texel_index, width), - width, - ); -} - -fn read_word(cursor: ptr) -> u32 { - let word_index = (*cursor).word_index; - let texel_index = word_index / 4u; - if texel_index != (*cursor).texel_index { - (*cursor).texel = fetch_instance_texel(texel_index, (*cursor).width); - (*cursor).texel_index = texel_index; - } - (*cursor).word_index = word_index + 1u; - return (*cursor).texel[word_index % 4u]; -} - -fn read_f32(cursor: ptr) -> f32 { - return bitcast(read_word(cursor)); -} - -fn read_i32(cursor: ptr) -> i32 { - return bitcast(read_word(cursor)); -} - -fn read_vec2_f32(cursor: ptr) -> vec2 { - return vec2(read_f32(cursor), read_f32(cursor)); -} - -fn read_vec2_i32(cursor: ptr) -> vec2 { - return vec2(read_i32(cursor), read_i32(cursor)); -} - -fn read_hsla(cursor: ptr) -> Hsla { - return Hsla( - read_f32(cursor), - read_f32(cursor), - read_f32(cursor), - read_f32(cursor), - ); -} - -fn read_bounds(cursor: ptr) -> Bounds { - return Bounds(read_vec2_f32(cursor), read_vec2_f32(cursor)); -} - -fn read_content_mask(cursor: ptr) -> ContentMask { - return ContentMask(read_bounds(cursor), read_corners(cursor), read_word(cursor), read_word(cursor)); -} - -fn read_corners(cursor: ptr) -> Corners { - return Corners( - read_f32(cursor), - read_f32(cursor), - read_f32(cursor), - read_f32(cursor), - ); -} - -fn read_edges(cursor: ptr) -> Edges { - return Edges( - read_f32(cursor), - read_f32(cursor), - read_f32(cursor), - read_f32(cursor), - ); -} - -fn read_color_stop(cursor: ptr) -> LinearColorStop { - return LinearColorStop(read_hsla(cursor), read_f32(cursor)); -} - -fn read_background(cursor: ptr) -> Background { - return Background( - read_word(cursor), - read_word(cursor), - read_hsla(cursor), - read_f32(cursor), - array( - read_color_stop(cursor), - read_color_stop(cursor), - ), - read_word(cursor), - ); -} - -fn read_atlas_tile(cursor: ptr) -> AtlasTile { - return AtlasTile( - AtlasTextureId( - read_word(cursor), - read_word(cursor), - ), - read_word(cursor), - read_word(cursor), - AtlasBounds( - read_vec2_i32(cursor), - read_vec2_i32(cursor), - ), - ); -} - -fn read_transformation(cursor: ptr) -> TransformationMatrix { - return TransformationMatrix( - mat2x2( - read_vec2_f32(cursor), - read_vec2_f32(cursor), - ), - read_vec2_f32(cursor), - ); -} - -fn load_quad(instance_id: u32) -> Quad { - var cursor = instance_cursor(instance_id * 46u); - return Quad( - read_word(&cursor), - read_word(&cursor), - read_bounds(&cursor), - read_content_mask(&cursor), - read_background(&cursor), - read_hsla(&cursor), - read_corners(&cursor), - read_edges(&cursor), - ); -} - -fn load_shadow(instance_id: u32) -> Shadow { - var cursor = instance_cursor(instance_id * 34u); - return Shadow( - read_word(&cursor), - read_f32(&cursor), - read_bounds(&cursor), - read_corners(&cursor), - read_content_mask(&cursor), - read_hsla(&cursor), - read_bounds(&cursor), - read_corners(&cursor), - read_word(&cursor), - read_word(&cursor), - ); -} - -fn load_path_vertex(vertex_id: u32) -> PathRasterizationVertex { - var cursor = instance_cursor(vertex_id * 36u); - return PathRasterizationVertex( - read_vec2_f32(&cursor), - read_vec2_f32(&cursor), - read_background(&cursor), - read_bounds(&cursor), - read_content_mask(&cursor), - ); -} - -fn load_path_sprite(instance_id: u32) -> PathSprite { - var cursor = instance_cursor(instance_id * 4u); - return PathSprite(read_bounds(&cursor)); -} - -fn load_underline(instance_id: u32) -> Underline { - var cursor = instance_cursor(instance_id * 22u); - return Underline( - read_word(&cursor), - read_word(&cursor), - read_bounds(&cursor), - read_content_mask(&cursor), - read_hsla(&cursor), - read_f32(&cursor), - read_word(&cursor), - ); -} - -fn load_mono_sprite(instance_id: u32) -> MonochromeSprite { - var cursor = instance_cursor(instance_id * 34u); - return MonochromeSprite( - read_word(&cursor), - read_word(&cursor), - read_bounds(&cursor), - read_content_mask(&cursor), - read_hsla(&cursor), - read_atlas_tile(&cursor), - read_transformation(&cursor), - ); -} - -fn load_poly_sprite(instance_id: u32) -> PolychromeSprite { - var cursor = instance_cursor(instance_id * 30u); - return PolychromeSprite( - read_word(&cursor), - read_word(&cursor), - read_word(&cursor), - read_f32(&cursor), - read_bounds(&cursor), - read_content_mask(&cursor), - read_corners(&cursor), - read_atlas_tile(&cursor), - ); -} - -// Clip readers reference only group 3. Reusing the instance cursor would also -// make surface fragments require t_instances at their uniform-buffer binding. -fn fetch_clip_texel(index: u32, width: u32) -> vec4 { - return textureLoad(t_clips, vec2(i32(index % width), i32(index / width)), 0); -} - -fn load_clip(index: u32) -> RoundedClip { - let word_index = index * 14u; - let width = textureDimensions(t_clips).x; - let texel_index = word_index / 4u; - // A 14-word record starts at word 0 or 2 and always spans four texels. - let a = fetch_clip_texel(texel_index, width); - let b = fetch_clip_texel(texel_index + 1u, width); - let c = fetch_clip_texel(texel_index + 2u, width); - let d = fetch_clip_texel(texel_index + 3u, width); - var bounds_words = a; - var rx_words = b; - var ry_words = c; - var parent = d.x; - var padding = d.y; - if word_index % 4u == 2u { - bounds_words = vec4(a.zw, b.xy); - rx_words = vec4(b.zw, c.xy); - ry_words = vec4(c.zw, d.xy); - parent = d.z; - padding = d.w; - } - let bounds = bitcast>(bounds_words); - let rx = bitcast>(rx_words); - let ry = bitcast>(ry_words); - return RoundedClip( - Bounds(bounds.xy, bounds.zw), - Corners(rx.x, rx.y, rx.z, rx.w), - Corners(ry.x, ry.y, ry.z, ry.w), - parent, padding, - ); -} diff --git a/crates/gpui_pre_wgpu/src/wgpu_atlas.rs b/crates/gpui_pre_wgpu/src/wgpu_atlas.rs deleted file mode 100644 index 3cf71ac9..00000000 --- a/crates/gpui_pre_wgpu/src/wgpu_atlas.rs +++ /dev/null @@ -1,527 +0,0 @@ -use anyhow::{Context as _, Result}; -use collections::FxHashMap; -use etagere::{BucketedAtlasAllocator, size2}; -use gpui::{ - AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTextureList, AtlasTile, Bounds, DevicePixels, - PlatformAtlas, Point, Size, -}; -use parking_lot::Mutex; -use std::{borrow::Cow, ops, sync::Arc}; - -use crate::WgpuContext; - -fn device_size_to_etagere(size: Size) -> etagere::Size { - size2(size.width.0, size.height.0) -} - -fn etagere_point_to_device(point: etagere::Point) -> Point { - Point { - x: DevicePixels(point.x), - y: DevicePixels(point.y), - } -} - -pub struct WgpuAtlas(Mutex); - -struct PendingUpload { - id: AtlasTextureId, - bounds: Bounds, - data: Vec, -} - -struct WgpuAtlasState { - device: Arc, - queue: Arc, - max_texture_size: u32, - color_texture_format: wgpu::TextureFormat, - storage: WgpuAtlasStorage, - tiles_by_key: FxHashMap, - pending_uploads: Vec, -} - -pub struct WgpuTextureInfo { - pub view: wgpu::TextureView, -} - -impl WgpuAtlas { - pub fn new( - device: Arc, - queue: Arc, - color_texture_format: wgpu::TextureFormat, - ) -> Self { - let max_texture_size = device.limits().max_texture_dimension_2d; - WgpuAtlas(Mutex::new(WgpuAtlasState { - device, - queue, - max_texture_size, - color_texture_format, - storage: WgpuAtlasStorage::default(), - tiles_by_key: Default::default(), - pending_uploads: Vec::new(), - })) - } - - pub fn from_context(context: &WgpuContext) -> Self { - Self::new( - context.device.clone(), - context.queue.clone(), - context.color_texture_format(), - ) - } - - pub fn before_frame(&self) { - let mut lock = self.0.lock(); - lock.flush_uploads(); - } - - pub fn get_texture_info(&self, id: AtlasTextureId) -> WgpuTextureInfo { - let lock = self.0.lock(); - let texture = &lock.storage[id]; - WgpuTextureInfo { - view: texture.view.clone(), - } - } - - /// Clears all cached textures and tiles, forcing them to be recreated. - /// Use this for incremental recovery when the device is still valid. - pub fn clear(&self) { - let mut lock = self.0.lock(); - lock.storage = WgpuAtlasStorage::default(); - lock.tiles_by_key.clear(); - lock.pending_uploads.clear(); - } - - /// Handles device lost by clearing all textures and cached tiles. - /// The atlas will lazily recreate textures as needed on subsequent frames. - pub fn handle_device_lost(&self, context: &WgpuContext) { - let mut lock = self.0.lock(); - lock.device = context.device.clone(); - lock.queue = context.queue.clone(); - lock.color_texture_format = context.color_texture_format(); - lock.storage = WgpuAtlasStorage::default(); - lock.tiles_by_key.clear(); - lock.pending_uploads.clear(); - } -} - -impl PlatformAtlas for WgpuAtlas { - fn get_or_insert_with<'a>( - &self, - key: &AtlasKey, - build: &mut dyn FnMut() -> Result, Cow<'a, [u8]>)>>, - ) -> Result> { - let mut lock = self.0.lock(); - if let Some(tile) = lock.tiles_by_key.get(key) { - Ok(Some(*tile)) - } else { - profiling::scope!("new tile"); - let Some((size, bytes)) = build()? else { - return Ok(None); - }; - let tile = lock - .allocate(size, key.texture_kind()) - .context("failed to allocate")?; - lock.upload_texture(tile.texture_id, tile.bounds, &bytes); - lock.tiles_by_key.insert(key.clone(), tile); - Ok(Some(tile)) - } - } - - fn remove(&self, key: &AtlasKey) { - let mut lock = self.0.lock(); - - let Some(tile) = lock.tiles_by_key.remove(key) else { - return; - }; - let id = tile.texture_id; - - let Some(texture_slot) = lock.storage[id.kind].textures.get_mut(id.index as usize) else { - return; - }; - - if let Some(mut texture) = texture_slot.take() { - texture.allocator.deallocate(tile.tile_id.into()); - texture.decrement_ref_count(); - if texture.is_unreferenced() { - lock.pending_uploads - .retain(|upload| upload.id != texture.id); - lock.storage[id.kind] - .free_list - .push(texture.id.index as usize); - } else { - *texture_slot = Some(texture); - } - } - } -} - -impl WgpuAtlasState { - fn allocate( - &mut self, - size: Size, - texture_kind: AtlasTextureKind, - ) -> Option { - { - let textures = &mut self.storage[texture_kind]; - - if let Some(tile) = textures - .iter_mut() - .rev() - .find_map(|texture| texture.allocate(size)) - { - return Some(tile); - } - } - - let texture = self.push_texture(size, texture_kind); - texture.allocate(size) - } - - fn push_texture( - &mut self, - min_size: Size, - kind: AtlasTextureKind, - ) -> &mut WgpuAtlasTexture { - const DEFAULT_ATLAS_SIZE: Size = Size { - width: DevicePixels(1024), - height: DevicePixels(1024), - }; - let max_texture_size = self.max_texture_size as i32; - let max_atlas_size = Size { - width: DevicePixels(max_texture_size), - height: DevicePixels(max_texture_size), - }; - - let size = min_size.min(&max_atlas_size).max(&DEFAULT_ATLAS_SIZE); - let format = match kind { - AtlasTextureKind::Monochrome => wgpu::TextureFormat::R8Unorm, - AtlasTextureKind::Subpixel | AtlasTextureKind::Polychrome => self.color_texture_format, - }; - - let texture = self.device.create_texture(&wgpu::TextureDescriptor { - label: Some("atlas"), - size: wgpu::Extent3d { - width: size.width.0 as u32, - height: size.height.0 as u32, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format, - usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, - view_formats: &[], - }); - - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - - let texture_list = &mut self.storage[kind]; - let index = texture_list.free_list.pop(); - - let atlas_texture = WgpuAtlasTexture { - id: AtlasTextureId { - index: index.unwrap_or(texture_list.textures.len()) as u32, - kind, - }, - allocator: BucketedAtlasAllocator::new(device_size_to_etagere(size)), - format, - texture, - view, - live_atlas_keys: 0, - }; - - if let Some(ix) = index { - texture_list.textures[ix] = Some(atlas_texture); - texture_list - .textures - .get_mut(ix) - .and_then(|t| t.as_mut()) - .expect("texture must exist") - } else { - texture_list.textures.push(Some(atlas_texture)); - texture_list - .textures - .last_mut() - .and_then(|t| t.as_mut()) - .expect("texture must exist") - } - } - - fn upload_texture(&mut self, id: AtlasTextureId, bounds: Bounds, bytes: &[u8]) { - let data = self - .storage - .get(id) - .map(|texture| swizzle_upload_data(bytes, texture.format)) - .unwrap_or_else(|| bytes.to_vec()); - - self.pending_uploads - .push(PendingUpload { id, bounds, data }); - } - - fn flush_uploads(&mut self) { - for upload in self.pending_uploads.drain(..) { - let Some(texture) = self.storage.get(upload.id) else { - continue; - }; - let bytes_per_pixel = texture.bytes_per_pixel(); - - self.queue.write_texture( - wgpu::TexelCopyTextureInfo { - texture: &texture.texture, - mip_level: 0, - origin: wgpu::Origin3d { - x: upload.bounds.origin.x.0 as u32, - y: upload.bounds.origin.y.0 as u32, - z: 0, - }, - aspect: wgpu::TextureAspect::All, - }, - &upload.data, - wgpu::TexelCopyBufferLayout { - offset: 0, - bytes_per_row: Some(upload.bounds.size.width.0 as u32 * bytes_per_pixel as u32), - rows_per_image: None, - }, - wgpu::Extent3d { - width: upload.bounds.size.width.0 as u32, - height: upload.bounds.size.height.0 as u32, - depth_or_array_layers: 1, - }, - ); - } - } -} - -#[derive(Default)] -struct WgpuAtlasStorage { - monochrome_textures: AtlasTextureList, - subpixel_textures: AtlasTextureList, - polychrome_textures: AtlasTextureList, -} - -impl ops::Index for WgpuAtlasStorage { - type Output = AtlasTextureList; - fn index(&self, kind: AtlasTextureKind) -> &Self::Output { - match kind { - AtlasTextureKind::Monochrome => &self.monochrome_textures, - AtlasTextureKind::Subpixel => &self.subpixel_textures, - AtlasTextureKind::Polychrome => &self.polychrome_textures, - } - } -} - -impl ops::IndexMut for WgpuAtlasStorage { - fn index_mut(&mut self, kind: AtlasTextureKind) -> &mut Self::Output { - match kind { - AtlasTextureKind::Monochrome => &mut self.monochrome_textures, - AtlasTextureKind::Subpixel => &mut self.subpixel_textures, - AtlasTextureKind::Polychrome => &mut self.polychrome_textures, - } - } -} - -impl WgpuAtlasStorage { - fn get(&self, id: AtlasTextureId) -> Option<&WgpuAtlasTexture> { - self[id.kind] - .textures - .get(id.index as usize) - .and_then(|t| t.as_ref()) - } -} - -impl ops::Index for WgpuAtlasStorage { - type Output = WgpuAtlasTexture; - fn index(&self, id: AtlasTextureId) -> &Self::Output { - let textures = match id.kind { - AtlasTextureKind::Monochrome => &self.monochrome_textures, - AtlasTextureKind::Subpixel => &self.subpixel_textures, - AtlasTextureKind::Polychrome => &self.polychrome_textures, - }; - textures[id.index as usize] - .as_ref() - .expect("texture must exist") - } -} - -struct WgpuAtlasTexture { - id: AtlasTextureId, - allocator: BucketedAtlasAllocator, - texture: wgpu::Texture, - view: wgpu::TextureView, - format: wgpu::TextureFormat, - live_atlas_keys: u32, -} - -impl WgpuAtlasTexture { - fn allocate(&mut self, size: Size) -> Option { - let allocation = self.allocator.allocate(device_size_to_etagere(size))?; - let tile = AtlasTile { - texture_id: self.id, - tile_id: allocation.id.into(), - padding: 0, - bounds: Bounds { - origin: etagere_point_to_device(allocation.rectangle.min), - size, - }, - }; - self.live_atlas_keys += 1; - Some(tile) - } - - fn bytes_per_pixel(&self) -> u8 { - match self.format { - wgpu::TextureFormat::R8Unorm => 1, - wgpu::TextureFormat::Bgra8Unorm | wgpu::TextureFormat::Rgba8Unorm => 4, - _ => 4, - } - } - - fn decrement_ref_count(&mut self) { - self.live_atlas_keys -= 1; - } - - fn is_unreferenced(&self) -> bool { - self.live_atlas_keys == 0 - } -} - -fn swizzle_upload_data(bytes: &[u8], format: wgpu::TextureFormat) -> Vec { - match format { - wgpu::TextureFormat::Rgba8Unorm => { - let mut data = bytes.to_vec(); - for pixel in data.chunks_exact_mut(4) { - pixel.swap(0, 2); - } - data - } - _ => bytes.to_vec(), - } -} - -#[cfg(all(test, not(target_family = "wasm")))] -mod tests { - use super::*; - use gpui::block_on; - use gpui::{ImageId, RenderImageParams}; - use std::sync::Arc; - - fn test_device_and_queue() -> anyhow::Result<(Arc, Arc)> { - block_on(async { - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: wgpu::Backends::all(), - flags: wgpu::InstanceFlags::default(), - backend_options: wgpu::BackendOptions::default(), - memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(), - display: None, - }); - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::LowPower, - compatible_surface: None, - force_fallback_adapter: false, - }) - .await - .map_err(|error| anyhow::anyhow!("failed to request adapter: {error}"))?; - let (device, queue) = adapter - .request_device(&wgpu::DeviceDescriptor { - label: Some("wgpu_atlas_test_device"), - required_features: wgpu::Features::empty(), - required_limits: wgpu::Limits::downlevel_defaults() - .using_resolution(adapter.limits()) - .using_alignment(adapter.limits()), - memory_hints: wgpu::MemoryHints::MemoryUsage, - trace: wgpu::Trace::Off, - experimental_features: wgpu::ExperimentalFeatures::disabled(), - }) - .await - .map_err(|error| anyhow::anyhow!("failed to request device: {error}"))?; - Ok((Arc::new(device), Arc::new(queue))) - }) - } - - #[test] - fn before_frame_skips_uploads_for_removed_texture() -> anyhow::Result<()> { - let (device, queue) = test_device_and_queue()?; - - let atlas = WgpuAtlas::new(device, queue, wgpu::TextureFormat::Bgra8Unorm); - let key = AtlasKey::Image(RenderImageParams { - image_id: ImageId(1), - frame_index: 0, - }); - let size = Size { - width: DevicePixels(1), - height: DevicePixels(1), - }; - let mut build = || Ok(Some((size, Cow::Owned(vec![0, 0, 0, 255])))); - - // Regression test: before the fix, this panicked in flush_uploads - atlas - .get_or_insert_with(&key, &mut build)? - .expect("tile should be created"); - atlas.remove(&key); - atlas.before_frame(); - Ok(()) - } - - #[test] - fn remove_deallocates_tile_space_for_reuse() -> anyhow::Result<()> { - let (device, queue) = test_device_and_queue()?; - let atlas = WgpuAtlas::new(device, queue, wgpu::TextureFormat::Bgra8Unorm); - - let small = Size { - width: DevicePixels(64), - height: DevicePixels(64), - }; - let big = Size { - width: DevicePixels(700), - height: DevicePixels(700), - }; - - let make_key = |image_id: usize| { - AtlasKey::Image(RenderImageParams { - image_id: ImageId(image_id), - frame_index: 0, - }) - }; - let insert = |key: &AtlasKey, size: Size| { - let byte_count = (size.width.0 as usize) * (size.height.0 as usize) * 4; - atlas - .get_or_insert_with(key, &mut || { - Ok(Some((size, Cow::Owned(vec![0u8; byte_count])))) - }) - .expect("allocation should succeed") - .expect("callback returns Some") - }; - - let keeper_key = make_key(1); - let big_key_a = make_key(2); - let big_key_b = make_key(3); - - let keeper_tile = insert(&keeper_key, small); - let tile_a = insert(&big_key_a, big); - assert_eq!(keeper_tile.texture_id, tile_a.texture_id); - - atlas.remove(&big_key_a); - let tile_b = insert(&big_key_b, big); - assert_eq!(tile_b.texture_id, keeper_tile.texture_id); - Ok(()) - } - - #[test] - fn swizzle_upload_data_preserves_bgra_uploads() { - let input = vec![0x10, 0x20, 0x30, 0x40]; - assert_eq!( - swizzle_upload_data(&input, wgpu::TextureFormat::Bgra8Unorm), - input - ); - } - - #[test] - fn swizzle_upload_data_converts_bgra_to_rgba() { - let input = vec![0x10, 0x20, 0x30, 0x40, 0xAA, 0xBB, 0xCC, 0xDD]; - assert_eq!( - swizzle_upload_data(&input, wgpu::TextureFormat::Rgba8Unorm), - vec![0x30, 0x20, 0x10, 0x40, 0xCC, 0xBB, 0xAA, 0xDD] - ); - } -} diff --git a/crates/gpui_pre_wgpu/src/wgpu_context.rs b/crates/gpui_pre_wgpu/src/wgpu_context.rs deleted file mode 100644 index 7e744e80..00000000 --- a/crates/gpui_pre_wgpu/src/wgpu_context.rs +++ /dev/null @@ -1,606 +0,0 @@ -#[cfg(not(target_family = "wasm"))] -use anyhow::Context as _; -#[cfg(not(target_family = "wasm"))] -use gpui_util::ResultExt; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; -use wgpu::TextureFormat; - -pub struct WgpuContext { - pub instance: wgpu::Instance, - pub adapter: wgpu::Adapter, - pub device: Arc, - pub queue: Arc, - backend: WgpuBackend, - dual_source_blending: bool, - color_texture_format: wgpu::TextureFormat, - device_lost: Arc, -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum WgpuBackend { - BrowserWebGpu, - Gl, - Native(wgpu::Backend), -} - -#[cfg(target_family = "wasm")] -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub enum WebBackendPreference { - #[default] - Auto, - WebGpu, - WebGl, -} - -#[cfg(target_family = "wasm")] -pub struct PreparedWebGraphics { - pub context: WgpuContext, - pub surface: wgpu::Surface<'static>, -} - -/// wgpu-core refuses to create a surface when neither the instance nor the surface -/// target carries a display handle, and `SurfaceTarget::Canvas` always passes `None`. -/// The WebGL2 backend never reads the handle (WebGPU bypasses wgpu-core entirely), so -/// a unit web display handle on the instance satisfies the check. -#[cfg(target_family = "wasm")] -#[derive(Debug)] -struct WebDisplaySource; - -#[cfg(target_family = "wasm")] -impl raw_window_handle::HasDisplayHandle for WebDisplaySource { - fn display_handle( - &self, - ) -> Result, raw_window_handle::HandleError> { - Ok(raw_window_handle::DisplayHandle::web()) - } -} - -#[derive(Clone, Copy)] -pub struct CompositorGpuHint { - pub vendor_id: u32, - pub device_id: u32, -} - -impl WgpuContext { - #[cfg(not(target_family = "wasm"))] - pub fn new( - instance: wgpu::Instance, - surface: &wgpu::Surface<'_>, - compositor_gpu: Option, - ) -> anyhow::Result { - Self::new_with_options(instance, surface, compositor_gpu, false) - } - - #[cfg(not(target_family = "wasm"))] - pub fn new_rejecting_software( - instance: wgpu::Instance, - surface: &wgpu::Surface<'_>, - compositor_gpu: Option, - ) -> anyhow::Result { - Self::new_with_options(instance, surface, compositor_gpu, true) - } - - #[cfg(not(target_family = "wasm"))] - fn new_with_options( - instance: wgpu::Instance, - surface: &wgpu::Surface<'_>, - compositor_gpu: Option, - reject_software: bool, - ) -> anyhow::Result { - let device_id_filter = match std::env::var("ZED_DEVICE_ID") { - Ok(val) => parse_pci_id(&val) - .context("Failed to parse device ID from `ZED_DEVICE_ID` environment variable") - .log_err(), - Err(std::env::VarError::NotPresent) => None, - err => { - err.context("Failed to read value of `ZED_DEVICE_ID` environment variable") - .log_err(); - None - } - }; - - // Select an adapter by actually testing surface configuration with the real device. - // This is the only reliable way to determine compatibility on hybrid GPU systems. - let (adapter, device, queue, dual_source_blending, color_texture_format) = - gpui::block_on(Self::select_adapter_and_device( - &instance, - device_id_filter, - surface, - compositor_gpu.as_ref(), - reject_software, - ))?; - - let device_lost = Arc::new(AtomicBool::new(false)); - device.set_device_lost_callback({ - let device_lost = Arc::clone(&device_lost); - move |reason, message| { - log::error!("wgpu device lost: reason={reason:?}, message={message}"); - if reason != wgpu::DeviceLostReason::Destroyed { - device_lost.store(true, Ordering::Relaxed); - } - } - }); - - log::info!( - "Selected GPU adapter: {:?} ({:?})", - adapter.get_info().name, - adapter.get_info().backend - ); - - let backend = WgpuBackend::Native(adapter.get_info().backend); - Ok(Self { - instance, - adapter, - device: Arc::new(device), - queue: Arc::new(queue), - backend, - dual_source_blending, - color_texture_format, - device_lost, - }) - } - - #[cfg(target_family = "wasm")] - pub async fn new_web( - canvas: &web_sys::HtmlCanvasElement, - preference: WebBackendPreference, - ) -> anyhow::Result { - Self::new_web_with_backend(canvas, preference).await - } - - #[cfg(target_family = "wasm")] - #[allow(clippy::arc_with_non_send_sync)] - async fn new_web_with_backend( - canvas: &web_sys::HtmlCanvasElement, - preference: WebBackendPreference, - ) -> anyhow::Result { - let backends = match preference { - WebBackendPreference::Auto => wgpu::Backends::BROWSER_WEBGPU | wgpu::Backends::GL, - WebBackendPreference::WebGpu => wgpu::Backends::BROWSER_WEBGPU, - WebBackendPreference::WebGl => wgpu::Backends::GL, - }; - let descriptor = wgpu::InstanceDescriptor { - backends, - flags: wgpu::InstanceFlags::default(), - backend_options: wgpu::BackendOptions::default(), - memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(), - display: Some(Box::new(WebDisplaySource)), - }; - let instance = if preference == WebBackendPreference::Auto { - wgpu::util::new_instance_with_webgpu_detection(descriptor).await - } else { - wgpu::Instance::new(descriptor) - }; - let surface = instance - .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone())) - .map_err(|error| { - anyhow::anyhow!("Failed to create browser graphics surface: {error}") - })?; - - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, - compatible_surface: Some(&surface), - force_fallback_adapter: false, - }) - .await - .map_err(|error| { - anyhow::anyhow!( - "Failed to request a {preference:?} adapter compatible with the canvas: {error}" - ) - })?; - let adapter_info = adapter.get_info(); - let backend = match adapter_info.backend { - wgpu::Backend::BrowserWebGpu => WgpuBackend::BrowserWebGpu, - wgpu::Backend::Gl => WgpuBackend::Gl, - backend => { - anyhow::bail!( - "Browser graphics initialization selected unexpected backend {backend:?}" - ) - } - }; - - let device_lost = Arc::new(AtomicBool::new(false)); - let (device, queue, dual_source_blending, color_texture_format) = - Self::create_device(&adapter).await?; - device.set_device_lost_callback({ - let device_lost = Arc::clone(&device_lost); - move |reason, message| { - log::error!("wgpu device lost: reason={reason:?}, message={message}"); - if reason != wgpu::DeviceLostReason::Destroyed { - device_lost.store(true, Ordering::Relaxed); - } - } - }); - log::info!( - "Browser graphics initialized: requested={preference:?}, selected={backend:?}, \ - adapter={:?}, limits={:?}, dual_source_blending={dual_source_blending}", - adapter_info.name, - device.limits(), - ); - - let context = Self { - instance, - adapter, - device: Arc::new(device), - queue: Arc::new(queue), - backend, - dual_source_blending, - color_texture_format, - device_lost, - }; - Ok(PreparedWebGraphics { context, surface }) - } - - async fn create_device( - adapter: &wgpu::Adapter, - ) -> anyhow::Result<(wgpu::Device, wgpu::Queue, bool, TextureFormat)> { - let dual_source_blending = adapter - .features() - .contains(wgpu::Features::DUAL_SOURCE_BLENDING); - - let mut required_features = wgpu::Features::empty(); - if dual_source_blending { - required_features |= wgpu::Features::DUAL_SOURCE_BLENDING; - } else { - log::warn!( - "Dual-source blending not available on this GPU. \ - Subpixel text antialiasing will be disabled." - ); - } - - let color_atlas_texture_format = Self::select_color_texture_format(adapter)?; - #[cfg(target_family = "wasm")] - let required_limits = if adapter.get_info().backend == wgpu::Backend::Gl { - wgpu::Limits::downlevel_webgl2_defaults() - .using_resolution(adapter.limits()) - .using_alignment(adapter.limits()) - } else { - wgpu::Limits::downlevel_defaults() - .using_resolution(adapter.limits()) - .using_alignment(adapter.limits()) - }; - #[cfg(not(target_family = "wasm"))] - let required_limits = wgpu::Limits::downlevel_defaults() - .using_resolution(adapter.limits()) - .using_alignment(adapter.limits()); - - let (device, queue) = adapter - .request_device(&wgpu::DeviceDescriptor { - label: Some("gpui_device"), - required_features, - required_limits, - memory_hints: wgpu::MemoryHints::MemoryUsage, - trace: wgpu::Trace::Off, - experimental_features: wgpu::ExperimentalFeatures::disabled(), - }) - .await - .map_err(|e| anyhow::anyhow!("Failed to create wgpu device: {e}"))?; - - Ok(( - device, - queue, - dual_source_blending, - color_atlas_texture_format, - )) - } - - #[cfg(not(target_family = "wasm"))] - pub fn instance(display: Box) -> wgpu::Instance { - wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: wgpu::Backends::VULKAN | wgpu::Backends::GL, - flags: wgpu::InstanceFlags::default(), - backend_options: wgpu::BackendOptions::default(), - memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(), - display: Some(display), - }) - } - - pub fn check_compatible_with_surface(&self, surface: &wgpu::Surface<'_>) -> anyhow::Result<()> { - let caps = surface.get_capabilities(&self.adapter); - if caps.formats.is_empty() { - let info = self.adapter.get_info(); - anyhow::bail!( - "Adapter {:?} (backend={:?}, device={:#06x}) is not compatible with the \ - display surface for this window.", - info.name, - info.backend, - info.device, - ); - } - Ok(()) - } - - /// Select an adapter and create a device, testing that the surface can actually be configured. - /// This is the only reliable way to determine compatibility on hybrid GPU systems, where - /// adapters may report surface compatibility via get_capabilities() but fail when actually - /// configuring (e.g., NVIDIA reporting Vulkan Wayland support but failing because the - /// Wayland compositor runs on the Intel GPU). - #[cfg(not(target_family = "wasm"))] - async fn select_adapter_and_device( - instance: &wgpu::Instance, - device_id_filter: Option, - surface: &wgpu::Surface<'_>, - compositor_gpu: Option<&CompositorGpuHint>, - reject_software: bool, - ) -> anyhow::Result<( - wgpu::Adapter, - wgpu::Device, - wgpu::Queue, - bool, - TextureFormat, - )> { - let mut adapters: Vec<_> = instance.enumerate_adapters(wgpu::Backends::all()).await; - - if adapters.is_empty() { - anyhow::bail!("No GPU adapters found"); - } - - if let Some(device_id) = device_id_filter { - log::info!("ZED_DEVICE_ID filter: {:#06x}", device_id); - } - - // Sort adapters into a single priority order. Tiers (from highest to lowest): - // - // 1. ZED_DEVICE_ID match — explicit user override - // 2. Compositor GPU match — the GPU the display server is rendering on - // 3. Device type (Discrete > Integrated > Other > Virtual > Cpu). - // "Other" ranks above "Virtual" because OpenGL seems to count as "Other". - // 4. Backend — prefer Vulkan/Metal/Dx12 over GL/etc. - adapters.sort_by_key(|adapter| { - let info = adapter.get_info(); - - // Backends like OpenGL report device=0 for all adapters, so - // device-based matching is only meaningful when non-zero. - let device_known = info.device != 0; - - let user_override: u8 = match device_id_filter { - Some(id) if device_known && info.device == id => 0, - _ => 1, - }; - - let compositor_match: u8 = match compositor_gpu { - Some(hint) - if device_known - && info.vendor == hint.vendor_id - && info.device == hint.device_id => - { - 0 - } - _ => 1, - }; - - let type_priority: u8 = if info.device_type == wgpu::DeviceType::Cpu { - 4 - } else { - match info.device_type { - wgpu::DeviceType::DiscreteGpu => 0, - wgpu::DeviceType::IntegratedGpu => 1, - wgpu::DeviceType::Other => 2, - wgpu::DeviceType::VirtualGpu => 3, - wgpu::DeviceType::Cpu => 4, - } - }; - - let backend_priority: u8 = match info.backend { - wgpu::Backend::Vulkan | wgpu::Backend::Metal | wgpu::Backend::Dx12 => 0, - _ => 1, - }; - - ( - user_override, - compositor_match, - type_priority, - backend_priority, - ) - }); - - // Log all available adapters (in sorted order) - log::info!("Found {} GPU adapter(s):", adapters.len()); - for adapter in &adapters { - let info = adapter.get_info(); - log::info!( - " - {} (vendor={:#06x}, device={:#06x}, backend={:?}, type={:?})", - info.name, - info.vendor, - info.device, - info.backend, - info.device_type, - ); - } - - // Test each adapter by creating a device and configuring the surface - for adapter in adapters { - let info = adapter.get_info(); - - if reject_software && info.device_type == wgpu::DeviceType::Cpu { - log::info!( - "Skipping software renderer: {} ({:?})", - info.name, - info.backend - ); - continue; - } - - log::info!("Testing adapter: {} ({:?})...", info.name, info.backend); - - match Self::try_adapter_with_surface(&adapter, surface).await { - Ok((device, queue, dual_source_blending, color_atlas_texture_format)) => { - log::info!( - "Selected GPU (passed configuration test): {} ({:?})", - info.name, - info.backend - ); - return Ok(( - adapter, - device, - queue, - dual_source_blending, - color_atlas_texture_format, - )); - } - Err(e) => { - log::info!( - " Adapter {} ({:?}) failed: {}, trying next...", - info.name, - info.backend, - e - ); - } - } - } - - anyhow::bail!("No GPU adapter found that can configure the display surface") - } - - /// Try to use an adapter with a surface by creating a device and testing configuration. - /// Returns the device and queue if successful, allowing them to be reused. - #[cfg(not(target_family = "wasm"))] - async fn try_adapter_with_surface( - adapter: &wgpu::Adapter, - surface: &wgpu::Surface<'_>, - ) -> anyhow::Result<(wgpu::Device, wgpu::Queue, bool, TextureFormat)> { - let caps = surface.get_capabilities(adapter); - if caps.formats.is_empty() { - anyhow::bail!("no compatible surface formats"); - } - if caps.alpha_modes.is_empty() { - anyhow::bail!("no compatible alpha modes"); - } - - let (device, queue, dual_source_blending, color_atlas_texture_format) = - Self::create_device(adapter).await?; - let error_scope = device.push_error_scope(wgpu::ErrorFilter::Validation); - - let test_config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: caps.formats[0], - width: 64, - height: 64, - present_mode: wgpu::PresentMode::Fifo, - desired_maximum_frame_latency: 2, - alpha_mode: caps.alpha_modes[0], - view_formats: vec![], - }; - - surface.configure(&device, &test_config); - - let error = error_scope.pop().await; - if let Some(e) = error { - anyhow::bail!("surface configuration failed: {e}"); - } - - Ok(( - device, - queue, - dual_source_blending, - color_atlas_texture_format, - )) - } - - fn select_color_texture_format(adapter: &wgpu::Adapter) -> anyhow::Result { - let required_usages = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST; - let bgra_features = adapter.get_texture_format_features(wgpu::TextureFormat::Bgra8Unorm); - let rgba_features = adapter.get_texture_format_features(wgpu::TextureFormat::Rgba8Unorm); - #[cfg(target_family = "wasm")] - if adapter.get_info().backend == wgpu::Backend::Gl - && rgba_features.allowed_usages.contains(required_usages) - { - return Ok(wgpu::TextureFormat::Rgba8Unorm); - } - if bgra_features.allowed_usages.contains(required_usages) { - return Ok(wgpu::TextureFormat::Bgra8Unorm); - } - if rgba_features.allowed_usages.contains(required_usages) { - let info = adapter.get_info(); - log::warn!( - "Adapter {} ({:?}) does not support Bgra8Unorm atlas textures with usages {:?}; \ - falling back to Rgba8Unorm atlas textures.", - info.name, - info.backend, - required_usages, - ); - return Ok(wgpu::TextureFormat::Rgba8Unorm); - } - - let info = adapter.get_info(); - Err(anyhow::anyhow!( - "Adapter {} ({:?}, device={:#06x}) does not support a usable color atlas texture \ - format with usages {:?}. Bgra8Unorm allowed usages: {:?}; \ - Rgba8Unorm allowed usages: {:?}.", - info.name, - info.backend, - info.device, - required_usages, - bgra_features.allowed_usages, - rgba_features.allowed_usages, - )) - } - pub fn backend(&self) -> WgpuBackend { - self.backend - } - - pub fn uses_webgl_instance_data(&self) -> bool { - matches!(self.backend, WgpuBackend::Gl) && cfg!(target_family = "wasm") - } - - pub fn supports_dual_source_blending(&self) -> bool { - self.dual_source_blending - } - - pub fn color_texture_format(&self) -> wgpu::TextureFormat { - self.color_texture_format - } - - /// Returns true if the GPU device was lost (e.g., due to driver crash, suspend/resume). - /// When this returns true, the context should be recreated. - pub fn device_lost(&self) -> bool { - self.device_lost.load(Ordering::Relaxed) - } - - /// Returns a clone of the device_lost flag for sharing with renderers. - pub(crate) fn device_lost_flag(&self) -> Arc { - Arc::clone(&self.device_lost) - } -} - -#[cfg(not(target_family = "wasm"))] -fn parse_pci_id(id: &str) -> anyhow::Result { - let mut id = id.trim(); - - if id.starts_with("0x") || id.starts_with("0X") { - id = &id[2..]; - } - let is_hex_string = id.chars().all(|c| c.is_ascii_hexdigit()); - let is_4_chars = id.len() == 4; - anyhow::ensure!( - is_4_chars && is_hex_string, - "Expected a 4 digit PCI ID in hexadecimal format" - ); - - u32::from_str_radix(id, 16).context("parsing PCI ID as hex") -} - -#[cfg(test)] -mod tests { - use super::parse_pci_id; - - #[test] - fn test_parse_device_id() { - assert!(parse_pci_id("0xABCD").is_ok()); - assert!(parse_pci_id("ABCD").is_ok()); - assert!(parse_pci_id("abcd").is_ok()); - assert!(parse_pci_id("1234").is_ok()); - assert!(parse_pci_id("123").is_err()); - assert_eq!( - parse_pci_id(&format!("{:x}", 0x1234)).unwrap(), - parse_pci_id(&format!("{:X}", 0x1234)).unwrap(), - ); - - assert_eq!( - parse_pci_id(&format!("{:#x}", 0x1234)).unwrap(), - parse_pci_id(&format!("{:#X}", 0x1234)).unwrap(), - ); - } -} diff --git a/crates/gpui_pre_wgpu/src/wgpu_renderer.rs b/crates/gpui_pre_wgpu/src/wgpu_renderer.rs deleted file mode 100644 index b7a72b42..00000000 --- a/crates/gpui_pre_wgpu/src/wgpu_renderer.rs +++ /dev/null @@ -1,2261 +0,0 @@ -use crate::{CompositorGpuHint, WgpuAtlas, WgpuContext}; -use anyhow::{Context as _, Result}; -use bytemuck::{Pod, Zeroable}; -use gpui::{ - get_gamma_correction_ratios, AtlasTextureId, Background, Bounds, ContentMask, Corners, - DevicePixels, GpuSpecs, Path, Point, PrimitiveBatch, ScaledPixels, Scene, Size, -}; -use log::warn; -#[cfg(not(target_family = "wasm"))] -use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; -use std::cell::RefCell; -use std::num::NonZeroU64; -use std::ops::Range; -use std::rc::Rc; -use std::sync::{Arc, Mutex}; - -const MAX_INSTANCE_BUFFER_SIZE: u64 = 256 * 1024 * 1024; - -const INSTANCE_TEXTURE_TEXEL_SIZE: u64 = 16; - -use crate::shaders::{STORAGE_BUFFER_SHADERS, SUBPIXEL_SHADERS, WEBGL_SHADERS}; - -fn least_common_multiple(left: u64, right: u64) -> u64 { - let mut first = left; - let mut second = right; - while second != 0 { - let remainder = first % second; - first = second; - second = remainder; - } - left / first * right -} - -#[repr(C)] -#[derive(Clone, Copy, Pod, Zeroable)] -struct GlobalParams { - viewport_size: [f32; 2], - premultiplied_alpha: u32, - pad: u32, -} - -#[repr(C)] -#[derive(Clone, Copy, Pod, Zeroable)] -struct PodBounds { - origin: [f32; 2], - size: [f32; 2], -} - -impl From> for PodBounds { - fn from(bounds: Bounds) -> Self { - Self { - origin: [bounds.origin.x.0, bounds.origin.y.0], - size: [bounds.size.width.0, bounds.size.height.0], - } - } -} - -#[repr(C)] -#[derive(Clone, Copy, Pod, Zeroable)] -struct PodCorners { - top_left: f32, - top_right: f32, - bottom_right: f32, - bottom_left: f32, -} - -impl From> for PodCorners { - fn from(corners: Corners) -> Self { - Self { - top_left: corners.top_left.0, - top_right: corners.top_right.0, - bottom_right: corners.bottom_right.0, - bottom_left: corners.bottom_left.0, - } - } -} - -#[repr(C)] -#[derive(Clone, Copy, Pod, Zeroable)] -struct PodContentMask { - bounds: PodBounds, - corner_radii: PodCorners, - clip_index: u32, - clip_padding: u32, -} - -impl From> for PodContentMask { - fn from(mask: ContentMask) -> Self { - Self { - bounds: mask.bounds.into(), - corner_radii: mask.corner_radii.into(), - clip_index: mask.clip_index, - clip_padding: 0, - } - } -} - -#[repr(C)] -#[derive(Clone, Copy, Pod, Zeroable)] -struct SurfaceParams { - bounds: PodBounds, - content_mask: PodContentMask, - padding: [u32; 2], -} - -#[repr(C)] -#[derive(Clone, Copy, Pod, Zeroable)] -struct GammaParams { - gamma_ratios: [f32; 4], - grayscale_enhanced_contrast: f32, - subpixel_enhanced_contrast: f32, - is_bgr: u32, - _pad: u32, -} - -#[derive(Clone, Debug)] -#[repr(C)] -struct PathSprite { - bounds: Bounds, -} - -#[derive(Clone, Debug)] -#[repr(C)] -struct PathRasterizationVertex { - xy_position: Point, - st_position: Point, - color: Background, - bounds: Bounds, - content_mask: ContentMask, -} - -pub struct WgpuSurfaceConfig { - pub size: Size, - pub transparent: bool, - /// Preferred presentation mode. When `Some`, the renderer will use this - /// mode if supported by the surface, falling back to `Fifo`. - /// When `None`, defaults to `Fifo` (VSync). - /// - /// Mobile platforms may prefer `Mailbox` (triple-buffering) to avoid - /// blocking in `get_current_texture()` during lifecycle transitions. - pub preferred_present_mode: Option, -} - -struct WgpuPipelines { - quads: wgpu::RenderPipeline, - shadows: wgpu::RenderPipeline, - path_rasterization: wgpu::RenderPipeline, - paths: wgpu::RenderPipeline, - underlines: wgpu::RenderPipeline, - mono_sprites: wgpu::RenderPipeline, - subpixel_sprites: Option, - poly_sprites: wgpu::RenderPipeline, - #[allow(dead_code)] - surfaces: wgpu::RenderPipeline, -} - -/// One frame allocation of instance data, ready to bind. -struct InstanceBinding { - bind_group: wgpu::BindGroup, - /// Index of the allocation's first instance within the bound data. Always - /// zero on the storage-buffer path, where the binding offset already - /// positions the array; on the WebGL texture path the shader indexes the - /// shared instance texture absolutely, so draws must offset their - /// instance (or vertex) ranges by this value. - first_instance: u32, -} - -struct InstanceBindings { - clips: InstanceBinding, - quads: InstanceBinding, - shadows: InstanceBinding, - underlines: InstanceBinding, - monochrome_sprites: InstanceBinding, - subpixel_sprites: InstanceBinding, - polychrome_sprites: InstanceBinding, -} - -struct WgpuBindGroupLayouts { - globals: wgpu::BindGroupLayout, - instances: wgpu::BindGroupLayout, - texture: wgpu::BindGroupLayout, - surfaces: wgpu::BindGroupLayout, -} - -/// Shared GPU context reference, used to coordinate device recovery across multiple windows. -pub type GpuContext = Rc>>; - -enum InstanceData { - Storage(wgpu::Buffer), - // WebGL2 has no storage buffers. A uint texture keeps the records available to both shader - // stages while preserving integer and floating-point bit patterns exactly. - Texture { - texture: wgpu::Texture, - view: wgpu::TextureView, - width: u32, - height: u32, - }, -} - -/// GPU resources that must be dropped together during device recovery. -struct WgpuResources { - device: Arc, - queue: Arc, - surface: wgpu::Surface<'static>, - pipelines: WgpuPipelines, - bind_group_layouts: WgpuBindGroupLayouts, - atlas_sampler: wgpu::Sampler, - globals_buffer: wgpu::Buffer, - globals_bind_group: wgpu::BindGroup, - path_globals_bind_group: wgpu::BindGroup, - instance_data: InstanceData, - path_intermediate_texture: Option, - path_intermediate_view: Option, - path_msaa_texture: Option, - path_msaa_view: Option, -} - -impl WgpuResources { - fn invalidate_intermediate_textures(&mut self) { - self.path_intermediate_texture = None; - self.path_intermediate_view = None; - self.path_msaa_texture = None; - self.path_msaa_view = None; - } -} - -pub struct WgpuRenderer { - /// Shared GPU context for device recovery coordination (unused on WASM). - #[allow(dead_code)] - context: Option, - /// Compositor GPU hint for adapter selection (unused on WASM). - #[allow(dead_code)] - compositor_gpu: Option, - resources: Option, - surface_config: wgpu::SurfaceConfiguration, - atlas: Arc, - path_globals_offset: u64, - gamma_offset: u64, - instance_data_capacity: u64, - max_instance_data_size: u64, - instance_data_alignment: u64, - uses_webgl_instance_data: bool, - rendering_params: RenderingParameters, - is_bgr: bool, - dual_source_blending: bool, - adapter_info: wgpu::AdapterInfo, - transparent_alpha_mode: wgpu::CompositeAlphaMode, - opaque_alpha_mode: wgpu::CompositeAlphaMode, - max_texture_size: u32, - last_error: Arc>>, - failed_frame_count: u32, - device_lost: std::sync::Arc, - surface_configured: bool, - needs_redraw: bool, -} - -impl WgpuRenderer { - fn resources(&self) -> &WgpuResources { - self.resources - .as_ref() - .expect("GPU resources not available") - } - - fn resources_mut(&mut self) -> &mut WgpuResources { - self.resources - .as_mut() - .expect("GPU resources not available") - } - - /// Creates a new WgpuRenderer from raw window handles. - /// - /// The `gpu_context` is a shared reference that coordinates GPU context across - /// multiple windows. The first window to create a renderer will initialize the - /// context; subsequent windows will share it. - /// - /// # Safety - /// The caller must ensure that the window handle remains valid for the lifetime - /// of the returned renderer. - #[cfg(not(target_family = "wasm"))] - pub fn new( - gpu_context: GpuContext, - window: &W, - config: WgpuSurfaceConfig, - compositor_gpu: Option, - ) -> anyhow::Result - where - W: HasWindowHandle + HasDisplayHandle + std::fmt::Debug + Send + Sync + Clone + 'static, - { - let window_handle = window - .window_handle() - .map_err(|e| anyhow::anyhow!("Failed to get window handle: {e}"))?; - - let target = wgpu::SurfaceTargetUnsafe::RawHandle { - // Fall back to the display handle already provided via InstanceDescriptor::display. - raw_display_handle: None, - raw_window_handle: window_handle.as_raw(), - }; - - // Use the existing context's instance if available, otherwise create a new one. - // The surface must be created with the same instance that will be used for - // adapter selection, otherwise wgpu will panic. - let instance = gpu_context - .borrow() - .as_ref() - .map(|ctx| ctx.instance.clone()) - .unwrap_or_else(|| WgpuContext::instance(Box::new(window.clone()))); - - // Safety: The caller guarantees that the window handle is valid for the - // lifetime of this renderer. In practice, the RawWindow struct is created - // from the native window handles and the surface is dropped before the window. - let surface = unsafe { - instance - .create_surface_unsafe(target) - .map_err(|e| anyhow::anyhow!("Failed to create surface: {e}"))? - }; - - let mut ctx_ref = gpu_context.borrow_mut(); - let context = match ctx_ref.as_mut() { - Some(context) => { - context.check_compatible_with_surface(&surface)?; - context - } - None => ctx_ref.insert(WgpuContext::new(instance, &surface, compositor_gpu)?), - }; - - let atlas = Arc::new(WgpuAtlas::from_context(context)); - - Self::new_internal( - Some(Rc::clone(&gpu_context)), - context, - surface, - config, - compositor_gpu, - atlas, - ) - } - - #[cfg(target_family = "wasm")] - pub fn new_from_canvas( - context: &WgpuContext, - canvas: &web_sys::HtmlCanvasElement, - config: WgpuSurfaceConfig, - ) -> anyhow::Result { - let surface = context - .instance - .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone())) - .map_err(|e| anyhow::anyhow!("Failed to create surface: {e}"))?; - Self::new_from_surface(context, surface, config) - } - - #[cfg(target_family = "wasm")] - #[allow(clippy::arc_with_non_send_sync)] - pub fn new_from_surface( - context: &WgpuContext, - surface: wgpu::Surface<'static>, - config: WgpuSurfaceConfig, - ) -> anyhow::Result { - let atlas = Arc::new(WgpuAtlas::from_context(context)); - Self::new_internal(None, context, surface, config, None, atlas) - } - - fn new_internal( - gpu_context: Option, - context: &WgpuContext, - surface: wgpu::Surface<'static>, - config: WgpuSurfaceConfig, - compositor_gpu: Option, - atlas: Arc, - ) -> anyhow::Result { - let surface_caps = surface.get_capabilities(&context.adapter); - let preferred_formats = [ - wgpu::TextureFormat::Bgra8Unorm, - wgpu::TextureFormat::Rgba8Unorm, - ]; - let surface_format = preferred_formats - .iter() - .find(|f| surface_caps.formats.contains(f)) - .copied() - .or_else(|| surface_caps.formats.iter().find(|f| !f.is_srgb()).copied()) - .or_else(|| surface_caps.formats.first().copied()) - .ok_or_else(|| { - anyhow::anyhow!( - "Surface reports no supported texture formats for adapter {:?}", - context.adapter.get_info().name - ) - })?; - - let pick_alpha_mode = - |preferences: &[wgpu::CompositeAlphaMode]| -> anyhow::Result { - preferences - .iter() - .find(|p| surface_caps.alpha_modes.contains(p)) - .copied() - .or_else(|| surface_caps.alpha_modes.first().copied()) - .ok_or_else(|| { - anyhow::anyhow!( - "Surface reports no supported alpha modes for adapter {:?}", - context.adapter.get_info().name - ) - }) - }; - - let transparent_alpha_mode = pick_alpha_mode(&[ - wgpu::CompositeAlphaMode::PreMultiplied, - wgpu::CompositeAlphaMode::Inherit, - ])?; - - let opaque_alpha_mode = pick_alpha_mode(&[ - wgpu::CompositeAlphaMode::Opaque, - wgpu::CompositeAlphaMode::Inherit, - ])?; - - let alpha_mode = if config.transparent { - transparent_alpha_mode - } else { - opaque_alpha_mode - }; - - let device = Arc::clone(&context.device); - let max_texture_size = device.limits().max_texture_dimension_2d; - - let requested_width = config.size.width.0 as u32; - let requested_height = config.size.height.0 as u32; - let clamped_width = requested_width.min(max_texture_size); - let clamped_height = requested_height.min(max_texture_size); - - if clamped_width != requested_width || clamped_height != requested_height { - warn!( - "Requested surface size ({}, {}) exceeds maximum texture dimension {}. \ - Clamping to ({}, {}). Window content may not fill the entire window.", - requested_width, requested_height, max_texture_size, clamped_width, clamped_height - ); - } - - let surface_config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: surface_format, - width: clamped_width.max(1), - height: clamped_height.max(1), - present_mode: config - .preferred_present_mode - .filter(|mode| surface_caps.present_modes.contains(mode)) - .unwrap_or(wgpu::PresentMode::Fifo), - desired_maximum_frame_latency: 2, - alpha_mode, - view_formats: vec![], - }; - // Configure the surface immediately. The adapter selection process already validated - // that this adapter can successfully configure this surface. - surface.configure(&context.device, &surface_config); - - let queue = Arc::clone(&context.queue); - let rendering_params = RenderingParameters::new(&context.adapter, surface_format); - let uses_webgl_instance_data = context.uses_webgl_instance_data(); - let dual_source_blending = - context.supports_dual_source_blending() && !uses_webgl_instance_data; - let bind_group_layouts = Self::create_bind_group_layouts(&device, uses_webgl_instance_data); - let pipelines = Self::create_pipelines( - &device, - &bind_group_layouts, - surface_format, - alpha_mode, - rendering_params.path_sample_count, - dual_source_blending, - uses_webgl_instance_data, - ); - - let atlas_sampler = device.create_sampler(&wgpu::SamplerDescriptor { - label: Some("atlas_sampler"), - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Linear, - ..Default::default() - }); - - let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment as u64; - let globals_size = std::mem::size_of::() as u64; - let gamma_size = std::mem::size_of::() as u64; - let path_globals_offset = globals_size.next_multiple_of(uniform_alignment); - let gamma_offset = (path_globals_offset + globals_size).next_multiple_of(uniform_alignment); - - let globals_buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("globals_buffer"), - size: gamma_offset + gamma_size, - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - - let ( - instance_data, - instance_data_capacity, - max_instance_data_size, - instance_data_alignment, - ) = if uses_webgl_instance_data { - let max_texture_dimension = device.limits().max_texture_dimension_2d; - let max_instance_data_size = (u64::from(max_texture_dimension).pow(2) - * INSTANCE_TEXTURE_TEXEL_SIZE) - .min(MAX_INSTANCE_BUFFER_SIZE); - let initial_capacity = (2 * 1024 * 1024).min(max_instance_data_size); - let (instance_data, capacity) = - Self::create_instance_texture(&device, initial_capacity, max_texture_dimension); - ( - instance_data, - capacity, - max_instance_data_size, - INSTANCE_TEXTURE_TEXEL_SIZE, - ) - } else { - // Every frame allocation is exposed as one storage-buffer binding, so - // its backing buffer must satisfy both the allocation and binding limits. - let max_buffer_size = device - .limits() - .max_buffer_size - .min(device.limits().max_storage_buffer_binding_size) - .min(MAX_INSTANCE_BUFFER_SIZE); - let initial_capacity = (2 * 1024 * 1024).min(max_buffer_size); - let buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("instance_buffer"), - size: initial_capacity, - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - ( - InstanceData::Storage(buffer), - initial_capacity, - max_buffer_size, - device.limits().min_storage_buffer_offset_alignment as u64, - ) - }; - - let globals_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("globals_bind_group"), - layout: &bind_group_layouts.globals, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { - buffer: &globals_buffer, - offset: 0, - size: Some(NonZeroU64::new(globals_size).unwrap()), - }), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { - buffer: &globals_buffer, - offset: gamma_offset, - size: Some(NonZeroU64::new(gamma_size).unwrap()), - }), - }, - ], - }); - - let path_globals_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("path_globals_bind_group"), - layout: &bind_group_layouts.globals, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { - buffer: &globals_buffer, - offset: path_globals_offset, - size: Some(NonZeroU64::new(globals_size).unwrap()), - }), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { - buffer: &globals_buffer, - offset: gamma_offset, - size: Some(NonZeroU64::new(gamma_size).unwrap()), - }), - }, - ], - }); - - let adapter_info = context.adapter.get_info(); - - let last_error: Arc>> = Arc::new(Mutex::new(None)); - let last_error_clone = Arc::clone(&last_error); - device.on_uncaptured_error(Arc::new(move |error| { - let mut guard = last_error_clone.lock().unwrap(); - *guard = Some(error.to_string()); - })); - - let resources = WgpuResources { - device, - queue, - surface, - pipelines, - bind_group_layouts, - atlas_sampler, - globals_buffer, - globals_bind_group, - path_globals_bind_group, - instance_data, - // Defer intermediate texture creation to first draw call via ensure_intermediate_textures(). - // This avoids panics when the device/surface is in an invalid state during initialization. - path_intermediate_texture: None, - path_intermediate_view: None, - path_msaa_texture: None, - path_msaa_view: None, - }; - - Ok(Self { - context: gpu_context, - compositor_gpu, - resources: Some(resources), - surface_config, - atlas, - path_globals_offset, - gamma_offset, - instance_data_capacity, - max_instance_data_size, - instance_data_alignment, - uses_webgl_instance_data, - rendering_params, - is_bgr: false, - dual_source_blending, - adapter_info, - transparent_alpha_mode, - opaque_alpha_mode, - max_texture_size, - last_error, - failed_frame_count: 0, - device_lost: context.device_lost_flag(), - surface_configured: true, - needs_redraw: false, - }) - } - - fn create_bind_group_layouts( - device: &wgpu::Device, - uses_webgl_instance_data: bool, - ) -> WgpuBindGroupLayouts { - let globals = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("globals_layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: NonZeroU64::new( - std::mem::size_of::() as u64 - ), - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: NonZeroU64::new( - std::mem::size_of::() as u64 - ), - }, - count: None, - }, - ], - }); - - let instance_data_entry = wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: if uses_webgl_instance_data { - wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Uint, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - } - } else { - wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { read_only: true }, - has_dynamic_offset: false, - min_binding_size: None, - } - }, - count: None, - }; - - let instances = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("instances_layout"), - entries: &[instance_data_entry], - }); - - let texture = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("texture_layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); - - let surfaces = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("surfaces_layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: NonZeroU64::new( - std::mem::size_of::() as u64 - ), - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 3, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); - - WgpuBindGroupLayouts { - globals, - instances, - texture, - surfaces, - } - } - - fn create_instance_texture( - device: &wgpu::Device, - requested_capacity: u64, - max_texture_dimension: u32, - ) -> (InstanceData, u64) { - let texel_count = requested_capacity.div_ceil(INSTANCE_TEXTURE_TEXEL_SIZE); - let width = texel_count.min(u64::from(max_texture_dimension)).max(1) as u32; - let height = texel_count - .div_ceil(u64::from(width)) - .min(u64::from(max_texture_dimension)) - .max(1) as u32; - let texture = device.create_texture(&wgpu::TextureDescriptor { - label: Some("instance_texture"), - size: wgpu::Extent3d { - width, - height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba32Uint, - usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, - view_formats: &[], - }); - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - let capacity = u64::from(width) * u64::from(height) * INSTANCE_TEXTURE_TEXEL_SIZE; - ( - InstanceData::Texture { - texture, - view, - width, - height, - }, - capacity, - ) - } - - fn create_pipelines( - device: &wgpu::Device, - layouts: &WgpuBindGroupLayouts, - surface_format: wgpu::TextureFormat, - alpha_mode: wgpu::CompositeAlphaMode, - path_sample_count: u32, - dual_source_blending: bool, - uses_webgl_instance_data: bool, - ) -> WgpuPipelines { - // Diagnostic guard: verify the device actually has - // DUAL_SOURCE_BLENDING. We have a crash report (ZED-5G1) where a - // feature mismatch caused a wgpu-hal abort, but we haven't - // identified the code path that produces the mismatch. This - // guard prevents the crash and logs more evidence. - // Remove this check once: - // a) We find and fix the root cause, or - // b) There are no reports of this warning appearing for some time. - let device_has_feature = device - .features() - .contains(wgpu::Features::DUAL_SOURCE_BLENDING); - if dual_source_blending && !device_has_feature { - log::error!( - "BUG: dual_source_blending flag is true but device does not \ - have DUAL_SOURCE_BLENDING enabled (device features: {:?}). \ - Falling back to mono text rendering. Please report this at \ - https://github.com/zed-industries/zed/issues", - device.features(), - ); - } - let dual_source_blending = - dual_source_blending && device_has_feature && !uses_webgl_instance_data; - - let shader_source = if uses_webgl_instance_data { - WEBGL_SHADERS - } else { - STORAGE_BUFFER_SHADERS - }; - let shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("gpui_shaders"), - source: wgpu::ShaderSource::Wgsl(shader_source.into()), - }); - - let subpixel_shader_module = if dual_source_blending { - Some(device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("gpui_subpixel_shaders"), - source: wgpu::ShaderSource::Wgsl(SUBPIXEL_SHADERS.into()), - })) - } else { - None - }; - - let blend_mode = match alpha_mode { - wgpu::CompositeAlphaMode::PreMultiplied => { - wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING - } - _ => wgpu::BlendState::ALPHA_BLENDING, - }; - - let color_target = wgpu::ColorTargetState { - format: surface_format, - blend: Some(blend_mode), - write_mask: wgpu::ColorWrites::ALL, - }; - - let create_pipeline = |name: &str, - vs_entry: &str, - fs_entry: &str, - globals_layout: &wgpu::BindGroupLayout, - data_layout: &wgpu::BindGroupLayout, - texture_layout: Option<&wgpu::BindGroupLayout>, - topology: wgpu::PrimitiveTopology, - color_targets: &[Option], - sample_count: u32, - module: &wgpu::ShaderModule| { - let bind_group_layouts = [ - Some(globals_layout), - Some(data_layout), - texture_layout, - Some(&layouts.instances), - ]; - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some(&format!("{name}_layout")), - bind_group_layouts: &bind_group_layouts, - immediate_size: 0, - }); - - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some(name), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module, - entry_point: Some(vs_entry), - buffers: &[], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }, - fragment: Some(wgpu::FragmentState { - module, - entry_point: Some(fs_entry), - targets: color_targets, - compilation_options: wgpu::PipelineCompilationOptions::default(), - }), - primitive: wgpu::PrimitiveState { - topology, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: sample_count, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview_mask: None, - cache: None, - }) - }; - - let quads = create_pipeline( - "quads", - "vs_quad", - "fs_quad", - &layouts.globals, - &layouts.instances, - None, - wgpu::PrimitiveTopology::TriangleStrip, - &[Some(color_target.clone())], - 1, - &shader_module, - ); - - let shadows = create_pipeline( - "shadows", - "vs_shadow", - "fs_shadow", - &layouts.globals, - &layouts.instances, - None, - wgpu::PrimitiveTopology::TriangleStrip, - &[Some(color_target.clone())], - 1, - &shader_module, - ); - - let path_rasterization = create_pipeline( - "path_rasterization", - "vs_path_rasterization", - "fs_path_rasterization", - &layouts.globals, - &layouts.instances, - None, - wgpu::PrimitiveTopology::TriangleList, - &[Some(wgpu::ColorTargetState { - format: surface_format, - blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING), - write_mask: wgpu::ColorWrites::ALL, - })], - path_sample_count, - &shader_module, - ); - - let paths_blend = wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::One, - operation: wgpu::BlendOperation::Add, - }, - }; - - let paths = create_pipeline( - "paths", - "vs_path", - "fs_path", - &layouts.globals, - &layouts.instances, - Some(&layouts.texture), - wgpu::PrimitiveTopology::TriangleStrip, - &[Some(wgpu::ColorTargetState { - format: surface_format, - blend: Some(paths_blend), - write_mask: wgpu::ColorWrites::ALL, - })], - 1, - &shader_module, - ); - - let underlines = create_pipeline( - "underlines", - "vs_underline", - "fs_underline", - &layouts.globals, - &layouts.instances, - None, - wgpu::PrimitiveTopology::TriangleStrip, - &[Some(color_target.clone())], - 1, - &shader_module, - ); - - let mono_sprites = create_pipeline( - "mono_sprites", - "vs_mono_sprite", - "fs_mono_sprite", - &layouts.globals, - &layouts.instances, - Some(&layouts.texture), - wgpu::PrimitiveTopology::TriangleStrip, - &[Some(color_target.clone())], - 1, - &shader_module, - ); - - let subpixel_sprites = if let Some(subpixel_module) = &subpixel_shader_module { - let subpixel_blend = wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::Src1, - dst_factor: wgpu::BlendFactor::OneMinusSrc1, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - }; - - Some(create_pipeline( - "subpixel_sprites", - "vs_subpixel_sprite", - "fs_subpixel_sprite", - &layouts.globals, - &layouts.instances, - Some(&layouts.texture), - wgpu::PrimitiveTopology::TriangleStrip, - &[Some(wgpu::ColorTargetState { - format: surface_format, - blend: Some(subpixel_blend), - write_mask: wgpu::ColorWrites::COLOR, - })], - 1, - subpixel_module, - )) - } else { - None - }; - - let poly_sprites = create_pipeline( - "poly_sprites", - "vs_poly_sprite", - "fs_poly_sprite", - &layouts.globals, - &layouts.instances, - Some(&layouts.texture), - wgpu::PrimitiveTopology::TriangleStrip, - &[Some(color_target.clone())], - 1, - &shader_module, - ); - - let surfaces = create_pipeline( - "surfaces", - "vs_surface", - "fs_surface", - &layouts.globals, - &layouts.surfaces, - None, - wgpu::PrimitiveTopology::TriangleStrip, - &[Some(color_target)], - 1, - &shader_module, - ); - - WgpuPipelines { - quads, - shadows, - path_rasterization, - paths, - underlines, - mono_sprites, - subpixel_sprites, - poly_sprites, - surfaces, - } - } - - fn create_path_intermediate( - device: &wgpu::Device, - format: wgpu::TextureFormat, - width: u32, - height: u32, - ) -> (wgpu::Texture, wgpu::TextureView) { - let texture = device.create_texture(&wgpu::TextureDescriptor { - label: Some("path_intermediate"), - size: wgpu::Extent3d { - width: width.max(1), - height: height.max(1), - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, - view_formats: &[], - }); - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - (texture, view) - } - - fn create_msaa_if_needed( - device: &wgpu::Device, - format: wgpu::TextureFormat, - width: u32, - height: u32, - sample_count: u32, - ) -> Option<(wgpu::Texture, wgpu::TextureView)> { - if sample_count <= 1 { - return None; - } - let texture = device.create_texture(&wgpu::TextureDescriptor { - label: Some("path_msaa"), - size: wgpu::Extent3d { - width: width.max(1), - height: height.max(1), - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count, - dimension: wgpu::TextureDimension::D2, - format, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - view_formats: &[], - }); - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - Some((texture, view)) - } - - pub fn update_drawable_size(&mut self, size: Size) { - let width = size.width.0 as u32; - let height = size.height.0 as u32; - - if width != self.surface_config.width || height != self.surface_config.height { - let clamped_width = width.min(self.max_texture_size); - let clamped_height = height.min(self.max_texture_size); - - if clamped_width != width || clamped_height != height { - warn!( - "Requested surface size ({}, {}) exceeds maximum texture dimension {}. \ - Clamping to ({}, {}). Window content may not fill the entire window.", - width, height, self.max_texture_size, clamped_width, clamped_height - ); - } - - self.surface_config.width = clamped_width.max(1); - self.surface_config.height = clamped_height.max(1); - let surface_config = self.surface_config.clone(); - - let Some(resources) = self.resources.as_mut() else { - return; - }; - - // Wait for any in-flight GPU work to complete before destroying textures - if let Err(e) = resources.device.poll(wgpu::PollType::Wait { - submission_index: None, - timeout: None, - }) { - warn!("Failed to poll device during resize: {e:?}"); - } - - // Destroy old textures before allocating new ones to avoid GPU memory spikes - if let Some(ref texture) = resources.path_intermediate_texture { - texture.destroy(); - } - if let Some(ref texture) = resources.path_msaa_texture { - texture.destroy(); - } - - resources - .surface - .configure(&resources.device, &surface_config); - - // Invalidate intermediate textures - they will be lazily recreated - // in draw() after we confirm the surface is healthy. This avoids - // panics when the device/surface is in an invalid state during resize. - resources.invalidate_intermediate_textures(); - } - } - - fn ensure_intermediate_textures(&mut self) { - if self.resources().path_intermediate_texture.is_some() { - return; - } - - let format = self.surface_config.format; - let width = self.surface_config.width; - let height = self.surface_config.height; - let path_sample_count = self.rendering_params.path_sample_count; - let resources = self.resources_mut(); - - let (t, v) = Self::create_path_intermediate(&resources.device, format, width, height); - resources.path_intermediate_texture = Some(t); - resources.path_intermediate_view = Some(v); - - let (path_msaa_texture, path_msaa_view) = Self::create_msaa_if_needed( - &resources.device, - format, - width, - height, - path_sample_count, - ) - .map(|(t, v)| (Some(t), Some(v))) - .unwrap_or((None, None)); - resources.path_msaa_texture = path_msaa_texture; - resources.path_msaa_view = path_msaa_view; - } - - pub fn set_subpixel_layout(&mut self, is_bgr: bool) { - self.is_bgr = is_bgr; - } - - pub fn update_transparency(&mut self, transparent: bool) { - let new_alpha_mode = if transparent { - self.transparent_alpha_mode - } else { - self.opaque_alpha_mode - }; - - if new_alpha_mode != self.surface_config.alpha_mode { - self.surface_config.alpha_mode = new_alpha_mode; - let surface_config = self.surface_config.clone(); - let path_sample_count = self.rendering_params.path_sample_count; - let dual_source_blending = self.dual_source_blending; - let uses_webgl_instance_data = self.uses_webgl_instance_data; - let Some(resources) = self.resources.as_mut() else { - return; - }; - resources - .surface - .configure(&resources.device, &surface_config); - resources.pipelines = Self::create_pipelines( - &resources.device, - &resources.bind_group_layouts, - surface_config.format, - surface_config.alpha_mode, - path_sample_count, - dual_source_blending, - uses_webgl_instance_data, - ); - } - } - - #[allow(dead_code)] - pub fn viewport_size(&self) -> Size { - Size { - width: DevicePixels(self.surface_config.width as i32), - height: DevicePixels(self.surface_config.height as i32), - } - } - - pub fn sprite_atlas(&self) -> &Arc { - &self.atlas - } - - pub fn supports_dual_source_blending(&self) -> bool { - self.dual_source_blending - } - - pub fn gpu_specs(&self) -> GpuSpecs { - GpuSpecs { - is_software_emulated: self.adapter_info.device_type == wgpu::DeviceType::Cpu, - device_name: self.adapter_info.name.clone(), - driver_name: self.adapter_info.driver.clone(), - driver_info: self.adapter_info.driver_info.clone(), - } - } - - pub fn max_texture_size(&self) -> u32 { - self.max_texture_size - } - - pub fn draw(&mut self, scene: &Scene) -> bool { - #[cfg(target_family = "wasm")] - if self.device_lost() { - if self.surface_configured { - log::error!( - "Browser graphics context was lost; rendering has stopped. Reload the page to recover." - ); - self.surface_configured = false; - } - return false; - } - - // Bail out early if the surface has been unconfigured (e.g. during - // Android background/rotation transitions). Attempting to acquire - // a texture from an unconfigured surface can block indefinitely on - // some drivers (Adreno). - if !self.surface_configured { - return false; - } - - let last_error = self.last_error.lock().unwrap().take(); - if let Some(error) = last_error { - self.failed_frame_count += 1; - log::error!( - "GPU error during frame (failure {} of 10): {error}", - self.failed_frame_count - ); - - // TBD. Does retrying more actually help? - if self.failed_frame_count > 10 { - panic!("Too many consecutive GPU errors. Last error: {error}"); - } else if self.failed_frame_count > 5 { - if let Some(res) = self.resources.as_mut() { - res.invalidate_intermediate_textures(); - } - self.atlas.clear(); - self.needs_redraw = true; - self.failed_frame_count = 0; - return false; - } - } else { - self.failed_frame_count = 0; - } - - self.atlas.before_frame(); - - let frame = match self.resources().surface.get_current_texture() { - wgpu::CurrentSurfaceTexture::Success(frame) => frame, - wgpu::CurrentSurfaceTexture::Suboptimal(frame) => { - // Textures must be destroyed before the surface can be reconfigured. - drop(frame); - let surface_config = self.surface_config.clone(); - let resources = self.resources_mut(); - resources - .surface - .configure(&resources.device, &surface_config); - return false; - } - wgpu::CurrentSurfaceTexture::Lost | wgpu::CurrentSurfaceTexture::Outdated => { - let surface_config = self.surface_config.clone(); - let resources = self.resources_mut(); - resources - .surface - .configure(&resources.device, &surface_config); - return false; - } - wgpu::CurrentSurfaceTexture::Timeout | wgpu::CurrentSurfaceTexture::Occluded => { - return false; - } - wgpu::CurrentSurfaceTexture::Validation => { - *self.last_error.lock().unwrap() = - Some("Surface texture validation error".to_string()); - return false; - } - }; - - // Now that we know the surface is healthy, ensure intermediate textures exist - self.ensure_intermediate_textures(); - - let frame_view = frame - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - - let gamma_params = GammaParams { - gamma_ratios: self.rendering_params.gamma_ratios, - grayscale_enhanced_contrast: self.rendering_params.grayscale_enhanced_contrast, - subpixel_enhanced_contrast: self.rendering_params.subpixel_enhanced_contrast, - is_bgr: self.is_bgr as u32, - _pad: 0, - }; - - let globals = GlobalParams { - viewport_size: [ - self.surface_config.width as f32, - self.surface_config.height as f32, - ], - premultiplied_alpha: if self.surface_config.alpha_mode - == wgpu::CompositeAlphaMode::PreMultiplied - { - 1 - } else { - 0 - }, - pad: 0, - }; - - let path_globals = GlobalParams { - premultiplied_alpha: 0, - ..globals - }; - - { - let resources = self.resources(); - resources.queue.write_buffer( - &resources.globals_buffer, - 0, - bytemuck::bytes_of(&globals), - ); - resources.queue.write_buffer( - &resources.globals_buffer, - self.path_globals_offset, - bytemuck::bytes_of(&path_globals), - ); - resources.queue.write_buffer( - &resources.globals_buffer, - self.gamma_offset, - bytemuck::bytes_of(&gamma_params), - ); - } - - if let Err(error) = self.record_frame(scene, &frame_view) { - log::error!("{error:#}"); - self.resources().queue.submit(std::iter::empty()); - return false; - } - - frame.present(); - true - } - - fn record_frame(&mut self, scene: &Scene, frame_view: &wgpu::TextureView) -> Result<()> { - let mut instance_offset = 0; - let instance_bindings = self - .write_instances(scene, &mut instance_offset) - .with_context(|| { - format!( - "scene too large: {} paths, {} shadows, {} quads, {} underlines, {} monochrome sprites, {} subpixel sprites, {} polychrome sprites", - scene.paths.len(), - scene.shadows.len(), - scene.quads.len(), - scene.underlines.len(), - scene.monochrome_sprites.len(), - scene.subpixel_sprites.len(), - scene.polychrome_sprites.len(), - ) - })?; - - let mut encoder = - self.resources() - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("main_encoder"), - }); - - { - let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("main_pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: frame_view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), - store: wgpu::StoreOp::Store, - }, - depth_slice: None, - })], - depth_stencil_attachment: None, - ..Default::default() - }); - - pass.set_bind_group(3, &instance_bindings.clips.bind_group, &[]); - for batch in scene.batches() { - match batch { - PrimitiveBatch::Quads(range) => self.draw_instances( - &instance_bindings.quads, - &self.resources().pipelines.quads, - instance_range(range), - &mut pass, - ), - PrimitiveBatch::Shadows(range) => self.draw_instances( - &instance_bindings.shadows, - &self.resources().pipelines.shadows, - instance_range(range), - &mut pass, - ), - PrimitiveBatch::Paths(range) => { - let paths = &scene.paths[range]; - if paths.is_empty() { - continue; - } - - drop(pass); - let rasterized = self.draw_paths_to_intermediate( - &mut encoder, - paths, - &mut instance_offset, - &instance_bindings.clips, - )?; - - pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("main_pass_continued"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: frame_view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - depth_slice: None, - })], - depth_stencil_attachment: None, - ..Default::default() - }); - - pass.set_bind_group(3, &instance_bindings.clips.bind_group, &[]); - if rasterized { - self.draw_paths_from_intermediate( - paths, - &mut instance_offset, - &mut pass, - )?; - } - } - PrimitiveBatch::Underlines(range) => self.draw_instances( - &instance_bindings.underlines, - &self.resources().pipelines.underlines, - instance_range(range), - &mut pass, - ), - PrimitiveBatch::MonochromeSprites { texture_id, range } => self.draw_sprites( - &instance_bindings.monochrome_sprites, - texture_id, - &self.resources().pipelines.mono_sprites, - instance_range(range), - &mut pass, - ), - PrimitiveBatch::SubpixelSprites { texture_id, range } => { - let resources = self.resources(); - self.draw_sprites( - &instance_bindings.subpixel_sprites, - texture_id, - resources - .pipelines - .subpixel_sprites - .as_ref() - .unwrap_or(&resources.pipelines.mono_sprites), - instance_range(range), - &mut pass, - ); - } - PrimitiveBatch::PolychromeSprites { texture_id, range } => self.draw_sprites( - &instance_bindings.polychrome_sprites, - texture_id, - &self.resources().pipelines.poly_sprites, - instance_range(range), - &mut pass, - ), - // Surfaces are macOS-only for video playback and are not - // implemented by the WGPU renderer. - PrimitiveBatch::Surfaces(_surfaces) => {} - } - } - } - - self.resources() - .queue - .submit(std::iter::once(encoder.finish())); - Ok(()) - } - - fn write_instances( - &mut self, - scene: &Scene, - instance_offset: &mut u64, - ) -> Result { - let empty_clip = gpui::RoundedClip::::default(); - let clips = if scene.rounded_clips.is_empty() { - std::slice::from_ref(&empty_clip) - } else { - &scene.rounded_clips - }; - // Keep clip records first: WebGL addresses this binding's texture from zero. - Ok(InstanceBindings { - clips: self.write_instance_binding("clips_bind_group", instance_offset, clips)?, - quads: self.write_instance_binding( - "quads_bind_group", - instance_offset, - &scene.quads, - )?, - shadows: self.write_instance_binding( - "shadows_bind_group", - instance_offset, - &scene.shadows, - )?, - underlines: self.write_instance_binding( - "underlines_bind_group", - instance_offset, - &scene.underlines, - )?, - monochrome_sprites: self.write_instance_binding( - "monochrome_sprites_bind_group", - instance_offset, - &scene.monochrome_sprites, - )?, - subpixel_sprites: self.write_instance_binding( - "subpixel_sprites_bind_group", - instance_offset, - &scene.subpixel_sprites, - )?, - polychrome_sprites: self.write_instance_binding( - "polychrome_sprites_bind_group", - instance_offset, - &scene.polychrome_sprites, - )?, - }) - } - - fn create_texture_bind_group( - &self, - label: &str, - texture_view: &wgpu::TextureView, - ) -> wgpu::BindGroup { - let resources = self.resources(); - resources - .device - .create_bind_group(&wgpu::BindGroupDescriptor { - label: Some(label), - layout: &resources.bind_group_layouts.texture, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(texture_view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&resources.atlas_sampler), - }, - ], - }) - } - - fn draw_instances( - &self, - instances: &InstanceBinding, - pipeline: &wgpu::RenderPipeline, - range: Range, - pass: &mut wgpu::RenderPass<'_>, - ) { - if range.is_empty() { - return; - } - pass.set_pipeline(pipeline); - pass.set_bind_group(0, &self.resources().globals_bind_group, &[]); - pass.set_bind_group(1, &instances.bind_group, &[]); - pass.draw( - 0..4, - instances.first_instance + range.start..instances.first_instance + range.end, - ); - } - - fn draw_sprites( - &self, - sprite_instances: &InstanceBinding, - texture_id: AtlasTextureId, - pipeline: &wgpu::RenderPipeline, - range: Range, - pass: &mut wgpu::RenderPass<'_>, - ) { - if range.is_empty() { - return; - } - let texture_info = self.atlas.get_texture_info(texture_id); - let texture = - self.create_texture_bind_group("atlas_texture_bind_group", &texture_info.view); - pass.set_pipeline(pipeline); - pass.set_bind_group(0, &self.resources().globals_bind_group, &[]); - pass.set_bind_group(1, &sprite_instances.bind_group, &[]); - pass.set_bind_group(2, &texture, &[]); - pass.draw( - 0..4, - sprite_instances.first_instance + range.start - ..sprite_instances.first_instance + range.end, - ); - } - - unsafe fn instance_bytes(instances: &[T]) -> &[u8] { - unsafe { - std::slice::from_raw_parts( - instances.as_ptr() as *const u8, - std::mem::size_of_val(instances), - ) - } - } - - fn draw_paths_from_intermediate( - &mut self, - paths: &[Path], - instance_offset: &mut u64, - pass: &mut wgpu::RenderPass<'_>, - ) -> Result<()> { - let first_path = &paths[0]; - let sprites: Vec = if paths.last().map(|p| &p.order) == Some(&first_path.order) - { - paths - .iter() - .map(|p| PathSprite { - bounds: p.clipped_bounds(), - }) - .collect() - } else { - let mut bounds = first_path.clipped_bounds(); - for path in paths.iter().skip(1) { - bounds = bounds.union(&path.clipped_bounds()); - } - vec![PathSprite { bounds }] - }; - - let Some(path_intermediate_view) = self.resources().path_intermediate_view.clone() else { - return Ok(()); - }; - let instances = - self.write_instance_binding("path_sprites_bind_group", instance_offset, &sprites)?; - let texture = self.create_texture_bind_group( - "path_intermediate_texture_bind_group", - &path_intermediate_view, - ); - let resources = self.resources(); - pass.set_pipeline(&resources.pipelines.paths); - pass.set_bind_group(0, &resources.globals_bind_group, &[]); - pass.set_bind_group(1, &instances.bind_group, &[]); - pass.set_bind_group(2, &texture, &[]); - pass.draw( - 0..4, - instances.first_instance..instances.first_instance + sprites.len() as u32, - ); - Ok(()) - } - - fn draw_paths_to_intermediate( - &mut self, - encoder: &mut wgpu::CommandEncoder, - paths: &[Path], - instance_offset: &mut u64, - clips: &InstanceBinding, - ) -> Result { - let mut vertices = Vec::new(); - for path in paths { - let bounds = path.clipped_bounds(); - vertices.extend(path.vertices.iter().map(|v| PathRasterizationVertex { - xy_position: v.xy_position, - st_position: v.st_position, - color: path.color, - bounds, - content_mask: path.content_mask, - })); - } - - if vertices.is_empty() { - return Ok(false); - } - - let vertex_binding = self.write_instance_binding( - "path_rasterization_bind_group", - instance_offset, - &vertices, - )?; - - let resources = self.resources(); - let Some(path_intermediate_view) = resources.path_intermediate_view.as_ref() else { - return Ok(false); - }; - - let (target_view, resolve_target) = if let Some(ref msaa_view) = resources.path_msaa_view { - (msaa_view, Some(path_intermediate_view)) - } else { - (path_intermediate_view, None) - }; - - { - let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("path_rasterization_pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: target_view, - resolve_target, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), - store: wgpu::StoreOp::Store, - }, - depth_slice: None, - })], - depth_stencil_attachment: None, - ..Default::default() - }); - - pass.set_bind_group(3, &clips.bind_group, &[]); - pass.set_pipeline(&resources.pipelines.path_rasterization); - pass.set_bind_group(0, &resources.path_globals_bind_group, &[]); - pass.set_bind_group(1, &vertex_binding.bind_group, &[]); - // The path rasterization shader loads records by vertex index - // rather than instance index, so the allocation's base shifts the - // vertex range here. - pass.draw( - vertex_binding.first_instance - ..vertex_binding.first_instance + vertices.len() as u32, - 0..1, - ); - } - - Ok(true) - } - - fn write_instance_binding( - &mut self, - label: &str, - instance_offset: &mut u64, - instances: &[T], - ) -> Result { - let data = unsafe { Self::instance_bytes(instances) }; - // wgpu rejects zero-sized bindings, so empty primitive arrays still - // reserve the 16-byte minimum. - let size = (data.len() as u64).max(16); - let stride = (std::mem::size_of::() as u64).max(1); - let (alignment, allocation_size) = if self.uses_webgl_instance_data { - // The texture transport has no binding offset: the shader indexes - // the instance texture absolutely, so each allocation must start on - // a whole instance (a stride multiple) and a whole texel, and must - // end on a texel boundary so the zero padding of its final partial - // texel cannot overlap the next allocation. - ( - least_common_multiple(self.instance_data_alignment, stride), - size.next_multiple_of(INSTANCE_TEXTURE_TEXEL_SIZE), - ) - } else { - (self.instance_data_alignment.max(1), size) - }; - let mut offset = (*instance_offset).next_multiple_of(alignment); - if offset + allocation_size > self.instance_data_capacity { - self.grow_instance_data(allocation_size)?; - offset = 0; - } - *instance_offset = offset + allocation_size; - - let first_instance = if self.uses_webgl_instance_data { - u32::try_from(offset / stride).context("instance index exceeds u32 range")? - } else { - 0 - }; - - let resources = self.resources(); - if !data.is_empty() { - match &resources.instance_data { - InstanceData::Storage(buffer) => resources.queue.write_buffer(buffer, offset, data), - InstanceData::Texture { .. } => { - Self::write_instance_texture(resources, offset, data) - } - } - } - let bind_group = resources - .device - .create_bind_group(&wgpu::BindGroupDescriptor { - label: Some(label), - layout: &resources.bind_group_layouts.instances, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: match &resources.instance_data { - InstanceData::Storage(buffer) => { - wgpu::BindingResource::Buffer(wgpu::BufferBinding { - buffer, - offset, - size: NonZeroU64::new(size), - }) - } - InstanceData::Texture { view, .. } => { - wgpu::BindingResource::TextureView(view) - } - }, - }], - }); - Ok(InstanceBinding { - bind_group, - first_instance, - }) - } - - fn write_instance_texture(resources: &WgpuResources, offset: u64, data: &[u8]) { - let InstanceData::Texture { - texture, - width, - height, - .. - } = &resources.instance_data - else { - return; - }; - let mut byte_offset = 0usize; - let mut texel_offset = offset / INSTANCE_TEXTURE_TEXEL_SIZE; - while byte_offset < data.len() { - let x = (texel_offset % u64::from(*width)) as u32; - let y = (texel_offset / u64::from(*width)) as u32; - if y >= *height { - // The capacity check in write_instance_binding should make this - // unreachable. Truncating silently would leave stale bytes in the - // texture and draw garbage for the remaining instances. - debug_assert!( - false, - "instance texture write out of bounds: row {y} >= height {}", - *height - ); - log::error!( - "instance texture write out of bounds; dropping {} bytes of instance data", - data.len() - byte_offset - ); - return; - } - let available_texels = u64::from(*width - x); - let remaining_bytes = data.len() - byte_offset; - let complete_texels = remaining_bytes as u64 / INSTANCE_TEXTURE_TEXEL_SIZE; - let texels = complete_texels.min(available_texels); - if texels > 0 { - let byte_count = (texels * INSTANCE_TEXTURE_TEXEL_SIZE) as usize; - resources.queue.write_texture( - wgpu::TexelCopyTextureInfo { - texture, - mip_level: 0, - origin: wgpu::Origin3d { x, y, z: 0 }, - aspect: wgpu::TextureAspect::All, - }, - &data[byte_offset..byte_offset + byte_count], - wgpu::TexelCopyBufferLayout { - offset: 0, - bytes_per_row: Some(byte_count as u32), - rows_per_image: None, - }, - wgpu::Extent3d { - width: texels as u32, - height: 1, - depth_or_array_layers: 1, - }, - ); - byte_offset += byte_count; - texel_offset += texels; - continue; - } - - let mut final_texel = [0; INSTANCE_TEXTURE_TEXEL_SIZE as usize]; - final_texel[..remaining_bytes].copy_from_slice(&data[byte_offset..]); - resources.queue.write_texture( - wgpu::TexelCopyTextureInfo { - texture, - mip_level: 0, - origin: wgpu::Origin3d { x, y, z: 0 }, - aspect: wgpu::TextureAspect::All, - }, - &final_texel, - wgpu::TexelCopyBufferLayout { - offset: 0, - bytes_per_row: Some(INSTANCE_TEXTURE_TEXEL_SIZE as u32), - rows_per_image: None, - }, - wgpu::Extent3d { - width: 1, - height: 1, - depth_or_array_layers: 1, - }, - ); - break; - } - } - - fn grow_instance_data(&mut self, required: u64) -> Result<()> { - let capacity = (self.instance_data_capacity * 2) - .max(required.next_power_of_two()) - .min(self.max_instance_data_size); - anyhow::ensure!( - capacity >= required, - "instance data needs {required} bytes, above the maximum of {}", - self.max_instance_data_size - ); - anyhow::ensure!( - capacity > self.instance_data_capacity, - "frame instance data exceeds the {}-byte maximum", - self.max_instance_data_size - ); - log::debug!( - "instance data grown from {} to {capacity}", - self.instance_data_capacity - ); - // Bind groups created earlier in the frame keep the previous buffer or - // texture alive, so allocations written before the grow remain valid; - // only subsequent writes land in the new allocation. - let uses_webgl_instance_data = self.uses_webgl_instance_data; - let resources = self.resources_mut(); - if uses_webgl_instance_data { - let max_texture_dimension = resources.device.limits().max_texture_dimension_2d; - let (instance_data, actual_capacity) = - Self::create_instance_texture(&resources.device, capacity, max_texture_dimension); - resources.instance_data = instance_data; - self.instance_data_capacity = actual_capacity; - } else { - resources.instance_data = - InstanceData::Storage(resources.device.create_buffer(&wgpu::BufferDescriptor { - label: Some("instance_buffer"), - size: capacity, - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - })); - self.instance_data_capacity = capacity; - } - Ok(()) - } - - /// Mark the surface as unconfigured so rendering is skipped until a new - /// surface is provided via [`replace_surface`](Self::replace_surface). - /// - /// This does **not** drop the renderer — the device, queue, atlas, and - /// pipelines stay alive. Use this when the native window is destroyed - /// (e.g. Android `TerminateWindow`) but you intend to re-create the - /// surface later without losing cached atlas textures. - pub fn unconfigure_surface(&mut self) { - self.surface_configured = false; - // Drop intermediate textures since they reference the old surface size. - if let Some(res) = self.resources.as_mut() { - res.invalidate_intermediate_textures(); - } - } - - /// Replace the wgpu surface with a new one (e.g. after Android destroys - /// and recreates the native window). Keeps the device, queue, atlas, and - /// all pipelines intact so cached `AtlasTextureId`s remain valid. - /// - /// The `instance` **must** be the same [`wgpu::Instance`] that was used to - /// create the adapter and device (i.e. from the [`WgpuContext`]). Using a - /// different instance will cause a "Device does not exist" panic because - /// the wgpu device is bound to its originating instance. - #[cfg(not(target_family = "wasm"))] - pub fn replace_surface( - &mut self, - window: &W, - config: WgpuSurfaceConfig, - instance: &wgpu::Instance, - ) -> anyhow::Result<()> { - let window_handle = window - .window_handle() - .map_err(|e| anyhow::anyhow!("Failed to get window handle: {e}"))?; - - let surface = create_surface(instance, window_handle.as_raw())?; - - let width = (config.size.width.0 as u32).max(1); - let height = (config.size.height.0 as u32).max(1); - - let alpha_mode = if config.transparent { - self.transparent_alpha_mode - } else { - self.opaque_alpha_mode - }; - - self.surface_config.width = width; - self.surface_config.height = height; - self.surface_config.alpha_mode = alpha_mode; - if let Some(mode) = config.preferred_present_mode { - self.surface_config.present_mode = mode; - } - - { - let res = self - .resources - .as_mut() - .expect("GPU resources not available"); - surface.configure(&res.device, &self.surface_config); - res.surface = surface; - - // Invalidate intermediate textures — they'll be recreated lazily. - res.invalidate_intermediate_textures(); - } - - self.surface_configured = true; - - Ok(()) - } - - pub fn destroy(&mut self) { - // Release surface-bound GPU resources eagerly so the underlying native - // window can be destroyed before the renderer itself is dropped. - self.resources.take(); - } - - /// Returns true if the GPU device was lost and recovery is needed. - pub fn device_lost(&self) -> bool { - self.device_lost.load(std::sync::atomic::Ordering::SeqCst) - } - - /// Returns true if a redraw is needed because GPU state was cleared. - /// Calling this method clears the flag. - pub fn needs_redraw(&mut self) -> bool { - std::mem::take(&mut self.needs_redraw) - } - - /// Recovers from a lost GPU device by recreating the renderer with a new context. - /// - /// Call this after detecting `device_lost()` returns true. - /// - /// This method coordinates recovery across multiple windows: - /// - The first window to call this will recreate the shared context - /// - Subsequent windows will adopt the already-recovered context - #[cfg(not(target_family = "wasm"))] - pub fn recover(&mut self, window: &W) -> anyhow::Result<()> - where - W: HasWindowHandle + HasDisplayHandle + std::fmt::Debug + Send + Sync + Clone + 'static, - { - let gpu_context = self.context.as_ref().expect("recover requires gpu_context"); - - // Check if another window already recovered the context - let needs_new_context = gpu_context - .borrow() - .as_ref() - .is_none_or(|ctx| ctx.device_lost()); - - let window_handle = window - .window_handle() - .map_err(|e| anyhow::anyhow!("Failed to get window handle: {e}"))?; - - let surface = if needs_new_context { - log::warn!("GPU device lost, recreating context..."); - - // Drop old resources to release Arc/Arc and GPU resources - self.resources = None; - *gpu_context.borrow_mut() = None; - - // Wait briefly for the GPU driver to stabilize, then try to - // recreate the context without software renderers. If this fails - // the caller should request another frame and retry — the real GPU - // may need more time to come back (e.g. after suspend/resume). - std::thread::sleep(std::time::Duration::from_millis(350)); - - let instance = WgpuContext::instance(Box::new(window.clone())); - let surface = create_surface(&instance, window_handle.as_raw())?; - let new_context = - WgpuContext::new_rejecting_software(instance, &surface, self.compositor_gpu)?; - *gpu_context.borrow_mut() = Some(new_context); - surface - } else { - let ctx_ref = gpu_context.borrow(); - let instance = &ctx_ref.as_ref().unwrap().instance; - create_surface(instance, window_handle.as_raw())? - }; - - let config = WgpuSurfaceConfig { - size: gpui::Size { - width: gpui::DevicePixels(self.surface_config.width as i32), - height: gpui::DevicePixels(self.surface_config.height as i32), - }, - transparent: self.surface_config.alpha_mode != wgpu::CompositeAlphaMode::Opaque, - preferred_present_mode: Some(self.surface_config.present_mode), - }; - let gpu_context = Rc::clone(gpu_context); - let ctx_ref = gpu_context.borrow(); - let context = ctx_ref.as_ref().expect("context should exist"); - - self.resources = None; - self.atlas.handle_device_lost(context); - - *self = Self::new_internal( - Some(gpu_context.clone()), - context, - surface, - config, - self.compositor_gpu, - self.atlas.clone(), - )?; - - log::info!("GPU recovery complete"); - Ok(()) - } -} - -fn instance_range(range: Range) -> Range { - range.start as u32..range.end as u32 -} - -#[cfg(not(target_family = "wasm"))] -fn create_surface( - instance: &wgpu::Instance, - raw_window_handle: raw_window_handle::RawWindowHandle, -) -> anyhow::Result> { - unsafe { - instance - .create_surface_unsafe(wgpu::SurfaceTargetUnsafe::RawHandle { - // Fall back to the display handle already provided via InstanceDescriptor::display. - raw_display_handle: None, - raw_window_handle, - }) - .map_err(|e| anyhow::anyhow!("{e}")) - } -} - -struct RenderingParameters { - path_sample_count: u32, - gamma_ratios: [f32; 4], - grayscale_enhanced_contrast: f32, - subpixel_enhanced_contrast: f32, -} - -impl RenderingParameters { - fn new(adapter: &wgpu::Adapter, surface_format: wgpu::TextureFormat) -> Self { - use std::env; - - let format_features = adapter.get_texture_format_features(surface_format); - let path_sample_count = [4, 2, 1] - .into_iter() - .find(|&n| format_features.flags.sample_count_supported(n)) - .unwrap_or(1); - - let gamma = env::var("ZED_FONTS_GAMMA") - .ok() - .and_then(|v| v.parse().ok()) - .unwrap_or(1.8_f32) - .clamp(1.0, 2.2); - let gamma_ratios = get_gamma_correction_ratios(gamma); - - let grayscale_enhanced_contrast = env::var("ZED_FONTS_GRAYSCALE_ENHANCED_CONTRAST") - .ok() - .and_then(|v| v.parse().ok()) - .unwrap_or(1.0_f32) - .max(0.0); - - let subpixel_enhanced_contrast = env::var("ZED_FONTS_SUBPIXEL_ENHANCED_CONTRAST") - .ok() - .and_then(|v| v.parse().ok()) - .unwrap_or(0.5_f32) - .max(0.0); - - Self { - path_sample_count, - gamma_ratios, - grayscale_enhanced_contrast, - subpixel_enhanced_contrast, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use gpui::{MonochromeSprite, PolychromeSprite, Quad, Shadow, SubpixelSprite, Underline}; - - #[test] - fn webgl_record_sizes_match_shader_word_strides() { - assert_eq!(std::mem::size_of::(), 64); - assert_eq!(std::mem::size_of::(), 46 * 4); - assert_eq!(std::mem::size_of::(), 34 * 4); - assert_eq!(std::mem::size_of::(), 36 * 4); - assert_eq!(std::mem::size_of::(), 4 * 4); - assert_eq!( - std::mem::size_of::>(), - 14 * 4 - ); - assert_eq!(std::mem::size_of::(), 22 * 4); - assert_eq!(std::mem::size_of::(), 34 * 4); - assert_eq!(std::mem::size_of::(), 34 * 4); - assert_eq!(std::mem::size_of::(), 30 * 4); - } -} diff --git a/crates/gpui_pre_windows/Cargo.lock b/crates/gpui_pre_windows/Cargo.lock deleted file mode 100644 index 2990f4db..00000000 --- a/crates/gpui_pre_windows/Cargo.lock +++ /dev/null @@ -1,4881 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "accesskit" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b7f7f85a7e5f68090000ed7622545829afd484d210358702ae4cb97dd0c320" -dependencies = [ - "enumn", - "uuid", -] - -[[package]] -name = "accesskit_consumer" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d10a236f96f87d70732e44520046785431ef01d5bcd6b041317bfadd2f88245" -dependencies = [ - "accesskit", - "hashbrown 0.16.1", -] - -[[package]] -name = "accesskit_windows" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "106c2b961215864d1c2e703ee63269c25c4e80a577ffb2c1017b9c17dcdf83a1" -dependencies = [ - "accesskit", - "accesskit_consumer", - "hashbrown 0.16.1", - "static_assertions", - "windows 0.62.2", - "windows-core 0.62.2", -] - -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "aligned" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" -dependencies = [ - "as-slice", -] - -[[package]] -name = "aligned-vec" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" -dependencies = [ - "equator", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" - -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" - -[[package]] -name = "arg_enum_proc_macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "as-slice" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-compression" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a89bce6054c720275ac2432fbba080a66a2106a44a1b804553930ca6909f4e0" -dependencies = [ - "compression-codecs", - "compression-core", - "futures-core", - "futures-io", - "pin-project-lite", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "av-scenechange" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" -dependencies = [ - "aligned", - "anyhow", - "arg_enum_proc_macro", - "arrayvec", - "log", - "num-rational", - "num-traits", - "pastey", - "rayon", - "thiserror 2.0.17", - "v_frame", - "y4m", -] - -[[package]] -name = "av1-grain" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link 0.2.1", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags 2.13.1", - "cexpr", - "clang-sys", - "itertools 0.11.0", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex 1.3.0", - "syn 2.0.117", -] - -[[package]] -name = "bit-set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" - -[[package]] -name = "bit_field" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" - -[[package]] -name = "bitstream-io" -version = "4.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" -dependencies = [ - "core2", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "borsh" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" -dependencies = [ - "cfg_aliases", -] - -[[package]] -name = "built" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytemuck" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "bzip2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" -dependencies = [ - "libbz2-rs-sys", -] - -[[package]] -name = "cc" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex 2.0.1", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "cgl" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" -dependencies = [ - "libc", -] - -[[package]] -name = "chrono" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link 0.2.1", -] - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "cocoa" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation 0.9.4", - "core-graphics 0.23.2", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "libc", - "objc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "compression-codecs" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8a506ec4b81c460798f572caead636d57d3d7e940f998160f52bd254bf2d23" -dependencies = [ - "bzip2", - "compression-core", - "flate2", - "memchr", -] - -[[package]] -name = "compression-core" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "convert_case" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "core-graphics" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" -dependencies = [ - "bitflags 2.13.1", - "core-foundation 0.10.0", - "core-graphics-types 0.2.0", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-helmer-fork" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32eb7c354ae9f6d437a6039099ce7ecd049337a8109b23d73e48e8ffba8e9cd5" -dependencies = [ - "bitflags 2.13.1", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" -dependencies = [ - "bitflags 2.13.1", - "core-foundation 0.10.0", - "libc", -] - -[[package]] -name = "core-graphics2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4416167a69126e617f8d0a214af0e3c1dbdeffcb100ddf72dcd1a1ac9893c146" -dependencies = [ - "bitflags 2.13.1", - "block", - "cfg-if", - "core-foundation 0.10.0", - "libc", -] - -[[package]] -name = "core-text" -version = "21.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a593227b66cbd4007b2a050dfdd9e1d1318311409c8d600dc82ba1b15ca9c130" -dependencies = [ - "core-foundation 0.10.0", - "core-graphics 0.24.0", - "foreign-types", - "libc", -] - -[[package]] -name = "core-video" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139679cc63eb9504bdbe37e37874b0247136177655f0008588781e90863afa62" -dependencies = [ - "block", - "core-foundation 0.10.0", - "core-graphics2", - "io-surface", - "libc", - "metal", -] - -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - -[[package]] -name = "core_maths" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" -dependencies = [ - "libm", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - -[[package]] -name = "ctor" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d83cb7e7a873830708d6b02a78cd36a592c6fa14bf267b68725103b85c0d77f" -dependencies = [ - "link-section", - "linktime-proc-macro", -] - -[[package]] -name = "data-url" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" - -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "convert_case 0.10.0", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.117", - "unicode-xid", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "dlib" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" -dependencies = [ - "libloading", -] - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "dwrote" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b35532432acc8b19ceed096e35dfa088d3ea037fe4f3c085f1f97f33b4d02" -dependencies = [ - "lazy_static", - "libc", - "winapi", - "wio", -] - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "embed-resource" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a075fc573c64510038d7ee9abc7990635863992f83ebc52c8b433b8411a02e" -dependencies = [ - "cc", - "memchr", - "rustc_version", - "toml", - "vswhom", - "winreg", -] - -[[package]] -name = "enumn" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "equator" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" -dependencies = [ - "equator-macro", -] - -[[package]] -name = "equator-macro" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "erased-serde" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" -dependencies = [ - "serde", - "serde_core", - "typeid", -] - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "etagere" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" -dependencies = [ - "euclid", - "svg_fmt", -] - -[[package]] -name = "euclid" -version = "0.22.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" -dependencies = [ - "num-traits", -] - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - -[[package]] -name = "exr" -version = "1.74.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" -dependencies = [ - "bit_field", - "half", - "lebe", - "miniz_oxide", - "rayon-core", - "smallvec", - "zune-inflate", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "fax" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" -dependencies = [ - "fax_derive", -] - -[[package]] -name = "fax_derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" - -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - -[[package]] -name = "flate2" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" - -[[package]] -name = "float-ord" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" - -[[package]] -name = "float_next_after" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" - -[[package]] -name = "flume" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" -dependencies = [ - "fastrand", - "futures-core", - "futures-sink", - "spin 0.9.8", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - -[[package]] -name = "fontconfig-parser" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" -dependencies = [ - "roxmltree 0.20.0", -] - -[[package]] -name = "fontdb" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" -dependencies = [ - "fontconfig-parser", - "log", - "memmap2", - "slotmap", - "tinyvec", - "ttf-parser", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "freetype-sys" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-concurrency" -version = "7.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175cd8cca9e1d45b87f18ffa75088f2099e3c4fe5e2f83e42de112560bea8ea6" -dependencies = [ - "fixedbitset", - "futures-core", - "futures-lite", - "pin-project", - "smallvec", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", - "wasip3", -] - -[[package]] -name = "gif" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" -dependencies = [ - "color_quant", - "weezl", -] - -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "gpui-pre" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b0f6f593153b6eb84c336ea7ccf12a1fca186f0db99af628e72f85048c1d7b" -dependencies = [ - "accesskit", - "anyhow", - "async-channel", - "async-task", - "backtrace", - "bindgen", - "bitflags 2.13.1", - "chrono", - "core-video", - "ctor", - "derive_more", - "embed-resource", - "etagere", - "futures", - "futures-concurrency", - "getrandom 0.3.4", - "gpui-pre-collections", - "gpui-pre-http-client", - "gpui-pre-macros", - "gpui-pre-refineable", - "gpui-pre-scheduler", - "gpui-pre-shared-string", - "gpui-pre-sum-tree", - "gpui-pre-util", - "gpui-pre-util-macros", - "gpui-pre-ztracing", - "heapless", - "image", - "inventory", - "itertools 0.14.0", - "log", - "lyon", - "num_cpus", - "parking", - "parking_lot", - "pin-project", - "pollster 0.4.0", - "postage", - "profiling", - "proptest", - "rand 0.9.4", - "raw-window-handle", - "regex", - "resvg", - "schemars", - "seahash", - "serde", - "serde_json", - "slotmap", - "smallvec", - "spin 0.10.0", - "strum", - "taffy", - "thiserror 2.0.17", - "tracing", - "ttf-parser", - "url", - "usvg", - "uuid", - "waker-fn", - "web-time", - "windows 0.62.2", - "zed-font-kit", - "zed-scap", -] - -[[package]] -name = "gpui-pre-collections" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c8efa2e51e368c8538a7be1ea9a12127ca03abe6cc01d9d3474e9ac4f53016" -dependencies = [ - "gpui-pre-util", - "indexmap", - "rustc-hash", -] - -[[package]] -name = "gpui-pre-derive-refineable" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a098d319acc9f84bf159944f96c5ea43a4f4cd7ed759f984acbd15719495aa0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gpui-pre-http-client" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3495a45a28cb800c8626d2053406114bcaca26d390a4d4b183365b5bb8fdaf02" -dependencies = [ - "anyhow", - "async-compression", - "bytes", - "derive_more", - "futures", - "http", - "http-body", - "log", - "parking_lot", - "serde", - "serde_json", - "serde_urlencoded", - "url", -] - -[[package]] -name = "gpui-pre-macros" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be2db7b5097b4d523b2bce933604bcc5acdaf679bb9a150e8299e6c07efc29c" -dependencies = [ - "heck", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gpui-pre-perf" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1db0c046b93c2a29120f8ee4c04bc80a4d7d6117d1164ef349faface8943491" -dependencies = [ - "gpui-pre-collections", - "serde", - "serde_json", -] - -[[package]] -name = "gpui-pre-refineable" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864e2e54a3029481dae5b6aae3ba2905f2fb1dfe66ced913b68c9ff527f8e327" -dependencies = [ - "gpui-pre-derive-refineable", -] - -[[package]] -name = "gpui-pre-scheduler" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b58a78c4e0c032900704ea49922641c534cf8bbf1bf22eda6ba00a76e88c6c3" -dependencies = [ - "async-task", - "backtrace", - "chrono", - "flume", - "futures", - "parking_lot", - "rand 0.9.4", - "web-time", -] - -[[package]] -name = "gpui-pre-shared-string" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fc99fe88e44173758a500522d3f4059a6541da4b471af720e3f60cf34a2bc3" -dependencies = [ - "schemars", - "serde", - "smol_str", -] - -[[package]] -name = "gpui-pre-sum-tree" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002baed852f20cef1188d3d5e0f749025fc718e4d6cea026b9077a9a6c10d042" -dependencies = [ - "gpui-pre-ztracing", - "heapless", - "log", - "rayon", - "tracing", -] - -[[package]] -name = "gpui-pre-util" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe779c4cb00929aafcb2b307cd1240588aebb5d1eb328c59b80f77c05a41fad" -dependencies = [ - "anyhow", - "log", - "which", -] - -[[package]] -name = "gpui-pre-util-macros" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f0ccc4bccb6a31786095d15fc9f40d6a4c6a295522e3460331dd7e929ff2f79" -dependencies = [ - "gpui-pre-perf", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gpui-pre-windows" -version = "0.3.3" -dependencies = [ - "accesskit", - "accesskit_windows", - "anyhow", - "dunce", - "etagere", - "futures", - "gpui-pre", - "gpui-pre-collections", - "gpui-pre-util", - "image", - "itertools 0.14.0", - "log", - "parking_lot", - "rand 0.9.4", - "raw-window-handle", - "smallvec", - "uuid", - "windows 0.62.2", - "windows-core 0.62.2", - "windows-numerics 0.3.1", - "windows-registry", - "zed-scap", -] - -[[package]] -name = "gpui-pre-zlog" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1416ea5f018e3a8a1c8be266332c443583f28f8f87379a84ab1e77622173ac0" -dependencies = [ - "anyhow", - "chrono", - "gpui-pre-collections", - "log", -] - -[[package]] -name = "gpui-pre-ztracing" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cc64a25a3cc4e4f1d8acb00074d3925339dae657c020083735738ba8af96bf7" -dependencies = [ - "gpui-pre-zlog", - "gpui-pre-ztracing-macro", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "gpui-pre-ztracing-macro" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3ea75672348f37d94472e579f5979ee19b744d0fa5aaadc2fe129ccafa80b" - -[[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", -] - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash 0.1.5", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "foldhash 0.2.0", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "heapless" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" -dependencies = [ - "hash32", - "stable_deref_trait", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "http" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core 0.62.2", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" - -[[package]] -name = "icu_properties" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" - -[[package]] -name = "icu_provider" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "image" -version = "0.25.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" -dependencies = [ - "bytemuck", - "byteorder-lite", - "color_quant", - "exr", - "gif", - "image-webp", - "moxcms", - "num-traits", - "png 0.18.0", - "qoi", - "ravif", - "rayon", - "tiff", - "zune-core", - "zune-jpeg", -] - -[[package]] -name = "image-webp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" -dependencies = [ - "byteorder-lite", - "quick-error 2.0.1", -] - -[[package]] -name = "imagesize" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" - -[[package]] -name = "imgref" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", -] - -[[package]] -name = "interpolate_name" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "inventory" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" -dependencies = [ - "rustversion", -] - -[[package]] -name = "io-surface" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "554b8c5d64ec09a3a520fe58e4d48a73e00ff32899cdcbe32a4877afd4968b8e" -dependencies = [ - "cgl", - "core-foundation 0.10.0", - "core-foundation-sys", - "leaky-cow", -] - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" -dependencies = [ - "cfg-if", - "futures-util", - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "kurbo" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b60dfc32f652b926df6192e55525b16d186c69d47876c3ead4da5cc9f8450e2" -dependencies = [ - "arrayvec", - "euclid", - "polycool", - "smallvec", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leak" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd100e01f1154f2908dfa7d02219aeab25d0b9c7fa955164192e3245255a0c73" - -[[package]] -name = "leaky-cow" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a8225d44241fd324a8af2806ba635fc7c8a7e9a7de4d5cf3ef54e71f5926fc" -dependencies = [ - "leak", -] - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "lebe" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" - -[[package]] -name = "libbz2-rs-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "libfuzzer-sys" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" -dependencies = [ - "arbitrary", - "cc", -] - -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link 0.2.1", -] - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "libredox" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" -dependencies = [ - "bitflags 2.13.1", - "libc", -] - -[[package]] -name = "link-section" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee1a0d6e252afe82e7bc2db42fba60e02ddf3b1accaf8cb21d96e34ba61f3d4" - -[[package]] -name = "linktime-proc-macro" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348d0075b1fc163b26d72a7f75fc5141daf2fd1bdf128d873cbaf6785d495bdf" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "litemap" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -dependencies = [ - "serde_core", - "value-bag", -] - -[[package]] -name = "loop9" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" -dependencies = [ - "imgref", -] - -[[package]] -name = "lyon" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcb7d54d54c8937364c9d41902d066656817dce1e03a44e5533afebd1ef4352" -dependencies = [ - "lyon_algorithms", - "lyon_tessellation", -] - -[[package]] -name = "lyon_algorithms" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c0829e28c4f336396f250d850c3987e16ce6db057ffe047ce0dd54aab6b647" -dependencies = [ - "lyon_path", - "num-traits", -] - -[[package]] -name = "lyon_geom" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e16770d760c7848b0c1c2d209101e408207a65168109509f8483837a36cf2e7" -dependencies = [ - "arrayvec", - "euclid", - "num-traits", -] - -[[package]] -name = "lyon_path" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aeca86bcfd632a15984ba029b539ffb811e0a70bf55e814ef8b0f54f506fdeb" -dependencies = [ - "lyon_geom", - "num-traits", -] - -[[package]] -name = "lyon_tessellation" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f586142e1280335b1bc89539f7c97dd80f08fc43e9ab1b74ef0a42b04aa353" -dependencies = [ - "float_next_after", - "lyon_path", - "num-traits", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "maybe-rayon" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" -dependencies = [ - "cfg-if", - "rayon", -] - -[[package]] -name = "memchr" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" - -[[package]] -name = "memmap2" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" -dependencies = [ - "libc", -] - -[[package]] -name = "metal" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" -dependencies = [ - "bitflags 2.13.1", - "block", - "core-graphics-types 0.2.0", - "foreign-types", - "log", - "objc", - "paste", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "moxcms" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" -dependencies = [ - "num-traits", - "pxfm", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - -[[package]] -name = "ntapi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" -dependencies = [ - "winapi", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-bigint" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[package]] -name = "objc-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[package]] -name = "objc_id" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -dependencies = [ - "objc", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link 0.2.1", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pastey" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" - -[[package]] -name = "pathfinder_geometry" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" -dependencies = [ - "log", - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_simd" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4500030c302e4af1d423f36f3b958d1aecb6c04184356ed5a833bf6b60435777" -dependencies = [ - "rustc_version", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - -[[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "png" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" -dependencies = [ - "bitflags 2.13.1", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "pollster" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" - -[[package]] -name = "pollster" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" - -[[package]] -name = "polycool" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50596ddc09eb5ad5f75cacd40209568e66df71baf86e1499a0e99c4cff12a5a6" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "postage" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" -dependencies = [ - "atomic", - "crossbeam-queue", - "futures", - "log", - "parking_lot", - "pin-project", - "pollster 0.2.5", - "static_assertions", - "thiserror 1.0.69", -] - -[[package]] -name = "potential_utf" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" -dependencies = [ - "zerovec", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - -[[package]] -name = "proc-macro-crate" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" -dependencies = [ - "toml_edit", -] - -[[package]] -name = "proc-macro2" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "profiling" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" -dependencies = [ - "quote", - "syn 2.0.117", -] - -[[package]] -name = "proptest" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.13.1", - "num-traits", - "proptest-macro", - "rand 0.9.4", - "rand_chacha 0.9.0", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "proptest-macro" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaa288b896cb2b345da7b7f2110ab19e51565b83495b56fcec98a62f8b1f33e" -dependencies = [ - "convert_case 0.11.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pxfm" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cbdf373972bf78df4d3b518d07003938e2c7d1fb5891e55f9cb6df57009d84" -dependencies = [ - "num-traits", -] - -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quick-xml" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" -dependencies = [ - "memchr", -] - -[[package]] -name = "quote" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rand_xorshift" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" -dependencies = [ - "rand_core 0.9.3", -] - -[[package]] -name = "rav1e" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" -dependencies = [ - "aligned-vec", - "arbitrary", - "arg_enum_proc_macro", - "arrayvec", - "av-scenechange", - "av1-grain", - "bitstream-io", - "built", - "cfg-if", - "interpolate_name", - "itertools 0.14.0", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive", - "num-traits", - "paste", - "profiling", - "rand 0.9.4", - "rand_chacha 0.9.0", - "simd_helpers", - "thiserror 2.0.17", - "v_frame", - "wasm-bindgen", -] - -[[package]] -name = "ravif" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" -dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error 2.0.1", - "rav1e", - "rayon", - "rgb", -] - -[[package]] -name = "raw-window-handle" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" - -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.13.1", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror 1.0.69", -] - -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" - -[[package]] -name = "resvg" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b563218631706d614e23059436526d005b50ab5f2d506b55a17eb65c5eb83419" -dependencies = [ - "gif", - "image-webp", - "log", - "pico-args", - "rgb", - "svgtypes", - "tiny-skia", - "usvg", - "zune-jpeg", -] - -[[package]] -name = "rgb" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "roxmltree" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" - -[[package]] -name = "roxmltree" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb" -dependencies = [ - "memchr", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.13.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "rusty-fork" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" -dependencies = [ - "fnv", - "quick-error 1.2.3", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "rustybuzz" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" -dependencies = [ - "bitflags 2.13.1", - "bytemuck", - "core_maths", - "log", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schemars" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" -dependencies = [ - "dyn-clone", - "indexmap", - "ref-cast", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.117", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "screencapturekit" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5eeeb57ac94960cfe5ff4c402be6585ae4c8d29a2cf41b276048c2e849d64e" -dependencies = [ - "screencapturekit-sys", -] - -[[package]] -name = "screencapturekit-sys" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22411b57f7d49e7fe08025198813ee6fd65e1ee5eff4ebc7880c12c82bde4c60" -dependencies = [ - "block", - "dispatch", - "objc", - "objc-foundation", - "objc_id", - "once_cell", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_fmt" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_json" -version = "1.0.151" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" -dependencies = [ - "indexmap", - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_spanned" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" -dependencies = [ - "serde_core", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "simd_helpers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" -dependencies = [ - "quote", -] - -[[package]] -name = "simplecss" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" -dependencies = [ - "log", -] - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "slotmap" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" -dependencies = [ - "version_check", -] - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "smol_str" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aaa7368fcf4852a4c2dd92df0cace6a71f2091ca0a23391ce7f3a31833f1523" -dependencies = [ - "borsh", - "serde_core", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spin" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" -dependencies = [ - "lock_api", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -dependencies = [ - "float-cmp", -] - -[[package]] -name = "strum" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sval" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94c4464e595f0284970fd9c7e9013804d035d4a61ab74b113242c874c05814d" - -[[package]] -name = "sval_buffer" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0f46e34b20a39e6a2bf02b926983149b3af6609fd1ee8a6e63f6f340f3e2164" -dependencies = [ - "sval", - "sval_ref", -] - -[[package]] -name = "sval_dynamic" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d0970e53c92ab5381d3b2db1828da8af945954d4234225f6dd9c3afbcef3f5" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_fmt" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e5e6e1613e1e7fc2e1a9fdd709622e54c122ceb067a60d170d75efd491a839" -dependencies = [ - "itoa", - "ryu", - "sval", -] - -[[package]] -name = "sval_json" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aec382f7bfa6e367b23c9611f129b94eb7daaf3d8fae45a8d0a0211eb4d4c8e6" -dependencies = [ - "itoa", - "ryu", - "sval", -] - -[[package]] -name = "sval_nested" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3049d0f99ce6297f8f7d9953b35a0103b7584d8f638de40e64edb7105fa578ae" -dependencies = [ - "sval", - "sval_buffer", - "sval_ref", -] - -[[package]] -name = "sval_ref" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88913e77506085c0a8bf6912bb6558591a960faf5317df6c1d9b227224ca6e1" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_serde" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f579fd7254f4be6cd7b450034f856b78523404655848789c451bacc6aa8b387d" -dependencies = [ - "serde_core", - "sval", - "sval_nested", -] - -[[package]] -name = "svg_fmt" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" - -[[package]] -name = "svgtypes" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "695b5790b3131dafa99b3bbfd25a216edb3d216dad9ca208d4657bfb8f2abc3d" -dependencies = [ - "kurbo", - "siphasher", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sysinfo" -version = "0.31.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" -dependencies = [ - "core-foundation-sys", - "libc", - "memchr", - "ntapi", - "rayon", - "windows 0.57.0", -] - -[[package]] -name = "taffy" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c034e05f6ee85a12daa63863c2245797715075c70649947aa0da54f3f2ab1d0f" -dependencies = [ - "arrayvec", - "serde", - "slotmap", - "smallvec", -] - -[[package]] -name = "tao-core-video-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271450eb289cb4d8d0720c6ce70c72c8c858c93dd61fc625881616752e6b98f6" -dependencies = [ - "cfg-if", - "core-foundation-sys", - "libc", - "objc", -] - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.1", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl 2.0.17", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "tiff" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" -dependencies = [ - "fax", - "flate2", - "half", - "quick-error 2.0.1", - "weezl", - "zune-jpeg", -] - -[[package]] -name = "tiny-skia" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if", - "log", - "png 0.17.16", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - -[[package]] -name = "tinystr" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "toml" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" -dependencies = [ - "indexmap", - "serde_core", - "serde_spanned", - "toml_datetime", - "toml_parser", - "toml_writer", - "winnow", -] - -[[package]] -name = "toml_datetime" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" -dependencies = [ - "indexmap", - "toml_datetime", - "toml_parser", - "winnow", -] - -[[package]] -name = "toml_parser" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" -dependencies = [ - "winnow", -] - -[[package]] -name = "toml_writer" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" - -[[package]] -name = "tracing" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tracing-core" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "ttf-parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -dependencies = [ - "core_maths", -] - -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - -[[package]] -name = "unicode-bidi-mirroring" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" - -[[package]] -name = "unicode-ccc" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-properties" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" - -[[package]] -name = "unicode-script" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" - -[[package]] -name = "unicode-segmentation" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" - -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "url" -version = "2.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "usvg" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e419dff010bb12512b0ae9e3d2f318dfbdf0167fde7eb05465134d4e8756076f" -dependencies = [ - "base64", - "data-url", - "flate2", - "fontdb", - "imagesize", - "kurbo", - "log", - "pico-args", - "roxmltree 0.21.1", - "rustybuzz", - "simplecss", - "siphasher", - "strict-num", - "svgtypes", - "tiny-skia-path", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" -dependencies = [ - "getrandom 0.3.4", - "js-sys", - "serde", - "sha1_smol", - "wasm-bindgen", -] - -[[package]] -name = "v_frame" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "value-bag" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" -dependencies = [ - "value-bag-serde1", - "value-bag-sval2", -] - -[[package]] -name = "value-bag-serde1" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16530907bfe2999a1773ca5900a65101e092c70f642f25cc23ca0c43573262c5" -dependencies = [ - "erased-serde", - "serde_core", - "serde_fmt", -] - -[[package]] -name = "value-bag-sval2" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00ae130edd690eaa877e4f40605d534790d1cf1d651e7685bd6a144521b251f" -dependencies = [ - "sval", - "sval_buffer", - "sval_dynamic", - "sval_fmt", - "sval_json", - "sval_ref", - "sval_serde", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "vswhom" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" -dependencies = [ - "libc", - "vswhom-sys", -] - -[[package]] -name = "vswhom-sys" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "wait-timeout" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" -dependencies = [ - "libc", -] - -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen 0.46.0", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.117", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.13.1", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "weezl" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" - -[[package]] -name = "which" -version = "8.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3ef584124b911bcc3875c2f1472e80f24361ceb789bd1c62b3e9a3df9ff43c" -dependencies = [ - "libc", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" -dependencies = [ - "windows-core 0.57.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections 0.2.0", - "windows-core 0.61.2", - "windows-future 0.2.1", - "windows-link 0.1.3", - "windows-numerics 0.2.0", -] - -[[package]] -name = "windows" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" -dependencies = [ - "windows-collections 0.3.2", - "windows-core 0.62.2", - "windows-future 0.3.2", - "windows-numerics 0.3.1", -] - -[[package]] -name = "windows-capture" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a4df73e95feddb9ec1a7e9c2ca6323b8c97d5eeeff78d28f1eccdf19c882b24" -dependencies = [ - "parking_lot", - "rayon", - "thiserror 2.0.17", - "windows 0.61.3", - "windows-future 0.2.1", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", -] - -[[package]] -name = "windows-collections" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" -dependencies = [ - "windows-core 0.62.2", -] - -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", - "windows-threading 0.1.0", -] - -[[package]] -name = "windows-future" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" -dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", - "windows-threading 0.2.1", -] - -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", -] - -[[package]] -name = "windows-numerics" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" -dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", -] - -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-threading" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.55.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" -dependencies = [ - "cfg-if", - "windows-sys 0.59.0", -] - -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi", -] - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.13.1", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "writeable" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "xcb" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f07c123b796139bfe0603e654eaf08e132e52387ba95b252c78bad3640ba37ea" -dependencies = [ - "bitflags 1.3.2", - "libc", - "quick-xml", - "x11", -] - -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - -[[package]] -name = "y4m" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" - -[[package]] -name = "yeslogic-fontconfig-sys" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503a066b4c037c440169d995b869046827dbc71263f6e8f3be6d77d4f3229dbd" -dependencies = [ - "dlib", - "once_cell", - "pkg-config", -] - -[[package]] -name = "yoke" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zed-font-kit" -version = "0.14.1-zed" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3898e450f36f852edda72e3f985c34426042c4951790b23b107f93394f9bff5" -dependencies = [ - "bitflags 2.13.1", - "byteorder", - "core-foundation 0.10.0", - "core-graphics 0.24.0", - "core-text", - "dirs", - "dwrote", - "float-ord", - "freetype-sys", - "lazy_static", - "libc", - "log", - "pathfinder_geometry", - "pathfinder_simd", - "walkdir", - "winapi", - "yeslogic-fontconfig-sys", -] - -[[package]] -name = "zed-scap" -version = "0.0.8-zed" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b338d705ae33a43ca00287c11129303a7a0aa57b101b72a1c08c863f698ac8" -dependencies = [ - "anyhow", - "cocoa", - "core-graphics-helmer-fork", - "log", - "objc", - "rand 0.8.6", - "screencapturekit", - "screencapturekit-sys", - "sysinfo", - "tao-core-video-sys", - "windows 0.61.3", - "windows-capture", - "x11", - "xcb", -] - -[[package]] -name = "zerocopy" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zerotrie" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f212a141d820099d57ffafb9569be9617a6f27d3dc881fbee8fb56642f917a9" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.3", -] - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" - -[[package]] -name = "zune-core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "zune-jpeg" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" -dependencies = [ - "zune-core", -] diff --git a/crates/gpui_pre_windows/Cargo.toml b/crates/gpui_pre_windows/Cargo.toml deleted file mode 100644 index e5cee6eb..00000000 --- a/crates/gpui_pre_windows/Cargo.toml +++ /dev/null @@ -1,238 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies. -# -# If you are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2024" -name = "gpui-pre-windows" -version = "0.3.3" -build = "build.rs" -publish = true -autolib = false -autobins = false -autoexamples = false -autotests = false -autobenches = false -description = "Zed's `gpui_windows` crate (gpui-pre snapshot of zed@5b055fa)" -readme = false -license = "Apache-2.0" -repository = "https://github.com/zed-industries/zed" -resolver = "2" - -[package.metadata.cargo-shear] -ignored = ["scap"] - -[package.metadata.gpui-pre] -zed-crate = "gpui_windows" -zed-version = "0.1.0" -zed-rev = "5b055fa789a8b8d38ac951a6e0cde272f66b4495" - -[features] -default = ["gpui/default"] -screen-capture = [ - "gpui/screen-capture", - "scap", -] -test-support = ["gpui/test-support"] - -[lib] -name = "gpui_windows" -path = "src/gpui_windows.rs" - -[dependencies.gpui] -version = "=0.3.3" -default-features = false -package = "gpui-pre" - -[target.'cfg(target_os = "windows")'.dependencies.accesskit] -version = "0.24.0" -features = ["enumn"] - -[target.'cfg(target_os = "windows")'.dependencies.accesskit_windows] -version = "0.34" - -[target.'cfg(target_os = "windows")'.dependencies.anyhow] -version = "1.0.86" - -[target.'cfg(target_os = "windows")'.dependencies.collections] -version = "=0.3.3" -package = "gpui-pre-collections" - -[target.'cfg(target_os = "windows")'.dependencies.dunce] -version = "1.0" - -[target.'cfg(target_os = "windows")'.dependencies.etagere] -version = "0.2" - -[target.'cfg(target_os = "windows")'.dependencies.futures] -version = "0.3.32" - -[target.'cfg(target_os = "windows")'.dependencies.gpui_util] -version = "=0.3.3" -package = "gpui-pre-util" - -[target.'cfg(target_os = "windows")'.dependencies.image] -version = "0.25.1" -features = [ - "bmp", - "dds", - "exr", - "ff", - "gif", - "hdr", - "ico", - "jpeg", - "png", - "pnm", - "qoi", - "rayon", - "tga", - "tiff", - "webp", -] -default-features = false - -[target.'cfg(target_os = "windows")'.dependencies.itertools] -version = "0.14.0" - -[target.'cfg(target_os = "windows")'.dependencies.log] -version = "0.4.16" -features = [ - "kv_unstable_serde", - "serde", -] - -[target.'cfg(target_os = "windows")'.dependencies.parking_lot] -version = "0.12.1" - -[target.'cfg(target_os = "windows")'.dependencies.rand] -version = "0.9" - -[target.'cfg(target_os = "windows")'.dependencies.raw-window-handle] -version = "0.6" - -[target.'cfg(target_os = "windows")'.dependencies.scap] -version = "0.0.8-zed" -optional = true -default-features = false -package = "zed-scap" - -[target.'cfg(target_os = "windows")'.dependencies.smallvec] -version = "1.6" -features = [ - "union", - "const_new", -] - -[target.'cfg(target_os = "windows")'.dependencies.uuid] -version = "1.1.2" -features = [ - "v4", - "v5", - "v7", - "serde", -] - -[target.'cfg(target_os = "windows")'.dependencies.windows] -version = "0.62" -features = [ - "Data_Xml_Dom", - "Foundation_Numerics", - "Globalization_DateTimeFormatting", - "Storage_Search", - "Storage_Streams", - "System_Threading", - "UI_Notifications", - "UI_ViewManagement", - "Wdk_System_SystemServices", - "Win32_Foundation", - "Win32_Globalization", - "Win32_Graphics_Direct3D", - "Win32_Graphics_Direct3D11", - "Win32_Graphics_Direct3D_Fxc", - "Win32_Graphics_DirectComposition", - "Win32_Graphics_DirectWrite", - "Win32_Graphics_DirectManipulation", - "Win32_Graphics_Dwm", - "Win32_Graphics_Dxgi", - "Win32_Graphics_Dxgi_Common", - "Win32_Graphics_Gdi", - "Win32_Graphics_Imaging", - "Win32_Graphics_Hlsl", - "Win32_Networking_WinSock", - "Win32_Security", - "Win32_Security_Credentials", - "Win32_Security_Cryptography", - "Win32_Storage_FileSystem", - "Win32_Storage_Packaging_Appx", - "Win32_System_Com", - "Win32_System_Com_StructuredStorage", - "Win32_System_Console", - "Win32_System_Diagnostics_Debug", - "Win32_System_DataExchange", - "Win32_System_IO", - "Win32_System_JobObjects", - "Win32_System_LibraryLoader", - "Win32_System_Memory", - "Win32_System_Ole", - "Win32_System_Performance", - "Win32_System_Pipes", - "Win32_System_RestartManager", - "Win32_System_SystemInformation", - "Win32_System_SystemServices", - "Win32_System_Threading", - "Win32_System_Variant", - "Win32_System_WinRT", - "Win32_UI_Controls", - "Win32_UI_HiDpi", - "Win32_UI_Input_Ime", - "Win32_UI_Input_KeyboardAndMouse", - "Win32_UI_Input_Pointer", - "Win32_UI_Shell", - "Win32_UI_Shell_Common", - "Win32_UI_Shell_PropertiesSystem", - "Win32_UI_WindowsAndMessaging", - "Win32_Media", -] - -[target.'cfg(target_os = "windows")'.dependencies.windows-core] -version = "0.62" - -[target.'cfg(target_os = "windows")'.dependencies.windows-numerics] -version = "0.3" - -[target.'cfg(target_os = "windows")'.dependencies.windows-registry] -version = "0.6.0" - -[target.'cfg(target_os = "windows")'.build-dependencies.windows-registry] -version = "0.6.0" - -[lints.clippy] -dbg_macro = "deny" -declare_interior_mutable_const = "deny" -disallowed_methods = "deny" -large_enum_variant = "allow" -let_underscore_future = "allow" -nonminimal_bool = "allow" -redundant_clone = "deny" -single_range_in_vec_init = "allow" -todo = "deny" -too_many_arguments = "allow" -type_complexity = "allow" - -[lints.clippy.style] -level = "allow" -priority = -1 - -[lints.rust.unexpected_cfgs] -level = "allow" -priority = 0 - -[workspace] diff --git a/crates/gpui_pre_windows/LICENSE-APACHE b/crates/gpui_pre_windows/LICENSE-APACHE deleted file mode 100644 index 461a0fe5..00000000 --- a/crates/gpui_pre_windows/LICENSE-APACHE +++ /dev/null @@ -1,222 +0,0 @@ -Copyright 2022 - 2025 Zed Industries, Inc. - - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - - http://www.apache.org/licenses/LICENSE-2.0 - - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - - -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - - 1. Definitions. - - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - - END OF TERMS AND CONDITIONS diff --git a/crates/gpui_pre_windows/build.rs b/crates/gpui_pre_windows/build.rs deleted file mode 100644 index 1db95715..00000000 --- a/crates/gpui_pre_windows/build.rs +++ /dev/null @@ -1,242 +0,0 @@ -#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")] - -fn main() { - #[cfg(target_os = "windows")] - { - // Compile HLSL shaders - #[cfg(not(debug_assertions))] - compile_shaders(); - } -} - -#[cfg(all(target_os = "windows", not(debug_assertions)))] -mod shader_compilation { - use std::{ - fs, - io::Write, - path::{Path, PathBuf}, - process::{self, Command}, - }; - - pub fn compile_shaders() { - let shader_path = - PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("src/shaders.hlsl"); - let out_dir = std::env::var("OUT_DIR").unwrap(); - - println!("cargo:rerun-if-changed={}", shader_path.display()); - - // Check if fxc.exe is available - let fxc_path = find_fxc_compiler(); - - // Define all modules - let modules = [ - "quad", - "shadow", - "path_rasterization", - "path_sprite", - "underline", - "monochrome_sprite", - "subpixel_sprite", - "polychrome_sprite", - ]; - - let rust_binding_path = format!("{}/shaders_bytes.rs", out_dir); - if Path::new(&rust_binding_path).exists() { - fs::remove_file(&rust_binding_path) - .expect("Failed to remove existing Rust binding file"); - } - for module in modules { - compile_shader_for_module( - module, - &out_dir, - &fxc_path, - shader_path.to_str().unwrap(), - &rust_binding_path, - ); - } - - { - let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) - .join("src/color_text_raster.hlsl"); - compile_shader_for_module( - "emoji_rasterization", - &out_dir, - &fxc_path, - shader_path.to_str().unwrap(), - &rust_binding_path, - ); - } - } - - /// Locate `binary` in the newest installed Windows SDK. - pub fn find_latest_windows_sdk_binary( - binary: &str, - ) -> Result, Box> { - let key = windows_registry::LOCAL_MACHINE - .open("SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0")?; - - let install_folder: String = key.get_string("InstallationFolder")?; // "C:\Program Files (x86)\Windows Kits\10\" - let install_folder_bin = Path::new(&install_folder).join("bin"); - - let mut versions: Vec<_> = std::fs::read_dir(&install_folder_bin)? - .flatten() - .filter(|entry| entry.path().is_dir()) - .filter_map(|entry| entry.file_name().into_string().ok()) - .collect(); - - versions.sort_by_key(|s| { - s.split('.') - .filter_map(|p| p.parse().ok()) - .collect::>() - }); - - let arch = match std::env::consts::ARCH { - "x86_64" => "x64", - "aarch64" => "arm64", - _ => Err(format!( - "Unsupported architecture: {}", - std::env::consts::ARCH - ))?, - }; - - if let Some(highest_version) = versions.last() { - return Ok(Some( - install_folder_bin - .join(highest_version) - .join(arch) - .join(binary), - )); - } - - Ok(None) - } - - /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler. - fn find_fxc_compiler() -> String { - // Check environment variable - if let Ok(path) = std::env::var("GPUI_FXC_PATH") - && Path::new(&path).exists() - { - return path; - } - - // Try to find in PATH - // NOTE: This has to be `where.exe` on Windows, not `where`, it must be ended with `.exe` - if let Ok(output) = std::process::Command::new("where.exe") - .arg("fxc.exe") - .output() - && output.status.success() - { - let path = String::from_utf8_lossy(&output.stdout); - return path.trim().to_string(); - } - - if let Ok(Some(path)) = find_latest_windows_sdk_binary("fxc.exe") { - return path.to_string_lossy().into_owned(); - } - - panic!("Failed to find fxc.exe"); - } - - fn compile_shader_for_module( - module: &str, - out_dir: &str, - fxc_path: &str, - shader_path: &str, - rust_binding_path: &str, - ) { - // Compile vertex shader - let output_file = format!("{}/{}_vs.h", out_dir, module); - let const_name = format!("{}_VERTEX_BYTES", module.to_uppercase()); - compile_shader_impl( - fxc_path, - &format!("{module}_vertex"), - &output_file, - &const_name, - shader_path, - "vs_4_1", - ); - generate_rust_binding(&const_name, &output_file, rust_binding_path); - - // Compile fragment shader - let output_file = format!("{}/{}_ps.h", out_dir, module); - let const_name = format!("{}_FRAGMENT_BYTES", module.to_uppercase()); - compile_shader_impl( - fxc_path, - &format!("{module}_fragment"), - &output_file, - &const_name, - shader_path, - "ps_4_1", - ); - generate_rust_binding(&const_name, &output_file, rust_binding_path); - } - - fn compile_shader_impl( - fxc_path: &str, - entry_point: &str, - output_path: &str, - var_name: &str, - shader_path: &str, - target: &str, - ) { - let output = Command::new(fxc_path) - .args([ - "/T", - target, - "/E", - entry_point, - "/Fh", - output_path, - "/Vn", - var_name, - "/O3", - shader_path, - ]) - .output(); - - match output { - Ok(result) => { - if result.status.success() { - return; - } - println!( - "cargo::error=Shader compilation failed for {}:\n{}", - entry_point, - String::from_utf8_lossy(&result.stderr) - ); - process::exit(1); - } - Err(e) => { - println!("cargo::error=Failed to run fxc for {}: {}", entry_point, e); - process::exit(1); - } - } - } - - fn generate_rust_binding(const_name: &str, head_file: &str, output_path: &str) { - let header_content = fs::read_to_string(head_file).expect("Failed to read header file"); - let const_definition = { - let global_var_start = header_content.find("const BYTE").unwrap(); - let global_var = &header_content[global_var_start..]; - let equal = global_var.find('=').unwrap(); - global_var[equal + 1..].trim() - }; - let rust_binding = format!( - "const {}: &[u8] = &{}\n", - const_name, - const_definition.replace('{', "[").replace('}', "]") - ); - let mut options = fs::OpenOptions::new() - .create(true) - .append(true) - .open(output_path) - .expect("Failed to open Rust binding file"); - options - .write_all(rust_binding.as_bytes()) - .expect("Failed to write Rust binding file"); - } -} - -#[cfg(all(target_os = "windows", not(debug_assertions)))] -use shader_compilation::compile_shaders; diff --git a/crates/gpui_pre_windows/src/alpha_correction.hlsl b/crates/gpui_pre_windows/src/alpha_correction.hlsl deleted file mode 100644 index 5a34a4eb..00000000 --- a/crates/gpui_pre_windows/src/alpha_correction.hlsl +++ /dev/null @@ -1,49 +0,0 @@ -// Adapted from https://github.com/microsoft/terminal/blob/1283c0f5b99a2961673249fa77c6b986efb5086c/src/renderer/atlas/dwrite.hlsl -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -float color_brightness(float3 color) { - // REC. 601 luminance coefficients for perceived brightness - return dot(color, float3(0.30f, 0.59f, 0.11f)); -} - -float light_on_dark_contrast(float enhancedContrast, float3 color) { - float brightness = color_brightness(color); - float multiplier = saturate(4.0f * (0.75f - brightness)); - return enhancedContrast * multiplier; -} - -float enhance_contrast(float alpha, float k) { - return alpha * (k + 1.0f) / (alpha * k + 1.0f); -} - -float3 enhance_contrast3(float3 alpha, float k) { - return alpha * (k + 1.0f) / (alpha * k + 1.0f); -} - -float apply_alpha_correction(float a, float b, float4 g) { - float brightness_adjustment = g.x * b + g.y; - float correction = brightness_adjustment * a + (g.z * b + g.w); - return a + a * (1.0f - a) * correction; -} - -float3 apply_alpha_correction3(float3 a, float3 b, float4 g) { - float3 brightness_adjustment = g.x * b + g.y; - float3 correction = brightness_adjustment * a + (g.z * b + g.w); - return a + a * (1.0f - a) * correction; -} - -float apply_contrast_and_gamma_correction(float sample, float3 color, float enhanced_contrast_factor, float4 gamma_ratios) { - float enhanced_contrast = light_on_dark_contrast(enhanced_contrast_factor, color); - float brightness = color_brightness(color); - - float contrasted = enhance_contrast(sample, enhanced_contrast); - return apply_alpha_correction(contrasted, brightness, gamma_ratios); -} - -float3 apply_contrast_and_gamma_correction3(float3 sample, float3 color, float enhanced_contrast_factor, float4 gamma_ratios) { - float enhanced_contrast = light_on_dark_contrast(enhanced_contrast_factor, color); - - float3 contrasted = enhance_contrast3(sample, enhanced_contrast); - return apply_alpha_correction3(contrasted, color, gamma_ratios); -} diff --git a/crates/gpui_pre_windows/src/clipboard.rs b/crates/gpui_pre_windows/src/clipboard.rs deleted file mode 100644 index cd0694ab..00000000 --- a/crates/gpui_pre_windows/src/clipboard.rs +++ /dev/null @@ -1,388 +0,0 @@ -use std::sync::LazyLock; - -use anyhow::Result; -use collections::FxHashMap; -use itertools::Itertools; -use windows::Win32::{ - Foundation::{HANDLE, HGLOBAL}, - System::{ - DataExchange::{ - CloseClipboard, CountClipboardFormats, EmptyClipboard, EnumClipboardFormats, - GetClipboardData, GetClipboardFormatNameW, OpenClipboard, RegisterClipboardFormatW, - SetClipboardData, - }, - Memory::{GMEM_MOVEABLE, GlobalAlloc, GlobalLock, GlobalSize, GlobalUnlock}, - Ole::{CF_DIB, CF_HDROP, CF_UNICODETEXT}, - }, - UI::Shell::{DragQueryFileW, HDROP}, -}; -use windows::core::{Owned, PCWSTR}; - -use gpui::{ - ClipboardEntry, ClipboardItem, ClipboardString, ExternalPaths, Image, ImageFormat, hash, -}; - -const DRAGDROP_GET_FILES_COUNT: u32 = 0xFFFFFFFF; - -static CLIPBOARD_HASH_FORMAT: LazyLock = - LazyLock::new(|| register_clipboard_format(windows::core::w!("GPUI internal text hash"))); -static CLIPBOARD_METADATA_FORMAT: LazyLock = - LazyLock::new(|| register_clipboard_format(windows::core::w!("GPUI internal metadata"))); -static CLIPBOARD_SVG_FORMAT: LazyLock = - LazyLock::new(|| register_clipboard_format(windows::core::w!("image/svg+xml"))); -static CLIPBOARD_GIF_FORMAT: LazyLock = - LazyLock::new(|| register_clipboard_format(windows::core::w!("GIF"))); -static CLIPBOARD_PNG_FORMAT: LazyLock = - LazyLock::new(|| register_clipboard_format(windows::core::w!("PNG"))); -static CLIPBOARD_JPG_FORMAT: LazyLock = - LazyLock::new(|| register_clipboard_format(windows::core::w!("JFIF"))); - -static IMAGE_FORMATS_MAP: LazyLock> = LazyLock::new(|| { - let mut map = FxHashMap::default(); - map.insert(*CLIPBOARD_PNG_FORMAT, ImageFormat::Png); - map.insert(*CLIPBOARD_GIF_FORMAT, ImageFormat::Gif); - map.insert(*CLIPBOARD_JPG_FORMAT, ImageFormat::Jpeg); - map.insert(*CLIPBOARD_SVG_FORMAT, ImageFormat::Svg); - map -}); - -fn register_clipboard_format(format: PCWSTR) -> u32 { - let ret = unsafe { RegisterClipboardFormatW(format) }; - if ret == 0 { - panic!( - "Error when registering clipboard format: {}", - std::io::Error::last_os_error() - ); - } - log::debug!( - "Registered clipboard format {} as {}", - unsafe { format.display() }, - ret - ); - ret -} - -fn get_clipboard_data(format: u32) -> Option { - let global = HGLOBAL(unsafe { GetClipboardData(format).ok() }?.0); - LockedGlobal::lock(global) -} - -pub(crate) fn write_to_clipboard(item: ClipboardItem) { - let Some(_clip) = ClipboardGuard::open() else { - return; - }; - - let result: Result<()> = (|| { - unsafe { EmptyClipboard()? }; - for entry in item.entries() { - match entry { - ClipboardEntry::String(string) => write_string(string)?, - ClipboardEntry::Image(image) => write_image(image)?, - ClipboardEntry::ExternalPaths(_) => {} - } - } - Ok(()) - })(); - - if let Err(e) = result { - log::error!("Failed to write to clipboard: {e}"); - } -} - -pub(crate) fn read_from_clipboard() -> Option { - let _clip = ClipboardGuard::open()?; - - let mut entries = Vec::new(); - let mut have_text = false; - let mut have_image = false; - let mut have_files = false; - - let count = unsafe { CountClipboardFormats() }; - let mut format = 0; - for _ in 0..count { - format = unsafe { EnumClipboardFormats(format) }; - - if !have_text && format == CF_UNICODETEXT.0 as u32 { - if let Some(entry) = read_string() { - entries.push(entry); - have_text = true; - } - } else if !have_image && is_image_format(format) { - if let Some(entry) = read_image(format) { - entries.push(entry); - have_image = true; - } - } else if !have_files && format == CF_HDROP.0 as u32 { - if let Some(entry) = read_files() { - entries.push(entry); - have_files = true; - } - } - } - - if entries.is_empty() { - log_unsupported_clipboard_formats(); - return None; - } - Some(ClipboardItem { entries }) -} - -pub(crate) fn with_file_names(hdrop: HDROP, mut f: F) -where - F: FnMut(String), -{ - let file_count = unsafe { DragQueryFileW(hdrop, DRAGDROP_GET_FILES_COUNT, None) }; - for file_index in 0..file_count { - let filename_length = unsafe { DragQueryFileW(hdrop, file_index, None) } as usize; - let mut buffer = vec![0u16; filename_length + 1]; - let ret = unsafe { DragQueryFileW(hdrop, file_index, Some(buffer.as_mut_slice())) }; - if ret == 0 { - log::error!("unable to read file name of dragged file"); - continue; - } - match String::from_utf16(&buffer[0..filename_length]) { - Ok(file_name) => f(file_name), - Err(e) => log::error!("dragged file name is not UTF-16: {}", e), - } - } -} - -fn set_clipboard_bytes(data: &[T], format: u32) -> Result<()> { - unsafe { - let global = Owned::new(GlobalAlloc(GMEM_MOVEABLE, std::mem::size_of_val(data))?); - let ptr = GlobalLock(*global); - anyhow::ensure!(!ptr.is_null(), "GlobalLock returned null"); - std::ptr::copy_nonoverlapping(data.as_ptr(), ptr as _, data.len()); - GlobalUnlock(*global).ok(); - SetClipboardData(format, Some(HANDLE(global.0)))?; - // SetClipboardData succeeded — the system now owns the memory. - std::mem::forget(global); - } - Ok(()) -} - -fn get_clipboard_string(format: u32) -> Option { - let locked = get_clipboard_data(format)?; - let bytes = locked.as_bytes(); - let words_len = bytes.len() / std::mem::size_of::(); - if words_len == 0 { - return Some(String::new()); - } - let slice = unsafe { std::slice::from_raw_parts(bytes.as_ptr() as *const u16, words_len) }; - let actual_len = slice.iter().position(|&c| c == 0).unwrap_or(words_len); - Some(String::from_utf16_lossy(&slice[..actual_len])) -} - -fn is_image_format(format: u32) -> bool { - IMAGE_FORMATS_MAP.contains_key(&format) || format == CF_DIB.0 as u32 -} - -fn write_string(item: &ClipboardString) -> Result<()> { - let wide: Vec = item.text.encode_utf16().chain(Some(0)).collect_vec(); - set_clipboard_bytes(&wide, CF_UNICODETEXT.0 as u32)?; - - if let Some(metadata) = item.metadata.as_ref() { - let hash_bytes = ClipboardString::text_hash(&item.text).to_ne_bytes(); - set_clipboard_bytes(&hash_bytes, *CLIPBOARD_HASH_FORMAT)?; - - let wide: Vec = metadata.encode_utf16().chain(Some(0)).collect_vec(); - set_clipboard_bytes(&wide, *CLIPBOARD_METADATA_FORMAT)?; - } - Ok(()) -} - -fn write_image(item: &Image) -> Result<()> { - let native_format = match item.format { - ImageFormat::Svg => Some(*CLIPBOARD_SVG_FORMAT), - ImageFormat::Gif => Some(*CLIPBOARD_GIF_FORMAT), - ImageFormat::Png => Some(*CLIPBOARD_PNG_FORMAT), - ImageFormat::Jpeg => Some(*CLIPBOARD_JPG_FORMAT), - _ => None, - }; - if let Some(format) = native_format { - set_clipboard_bytes(item.bytes(), format)?; - } - - // Also provide a PNG copy for broad compatibility. - // SVG can't be rasterized by the image crate, so skip it. - if item.format != ImageFormat::Svg && native_format != Some(*CLIPBOARD_PNG_FORMAT) { - if let Some(png_bytes) = convert_to_png(item.bytes(), item.format) { - set_clipboard_bytes(&png_bytes, *CLIPBOARD_PNG_FORMAT)?; - } - } - Ok(()) -} - -fn convert_to_png(bytes: &[u8], format: ImageFormat) -> Option> { - let img_format = gpui_to_image_format(format)?; - let image = image::load_from_memory_with_format(bytes, img_format) - .map_err(|e| log::warn!("Failed to decode image for PNG conversion: {e}")) - .ok()?; - let mut buf = Vec::new(); - image - .write_to(&mut std::io::Cursor::new(&mut buf), image::ImageFormat::Png) - .map_err(|e| log::warn!("Failed to encode PNG: {e}")) - .ok()?; - Some(buf) -} - -fn read_string() -> Option { - let text = get_clipboard_string(CF_UNICODETEXT.0 as u32)?; - let metadata = read_clipboard_metadata(&text); - Some(ClipboardEntry::String(ClipboardString { text, metadata })) -} - -fn read_clipboard_metadata(text: &str) -> Option { - let locked = get_clipboard_data(*CLIPBOARD_HASH_FORMAT)?; - let hash_bytes: [u8; 8] = locked.as_bytes().get(..8)?.try_into().ok()?; - let hash = u64::from_ne_bytes(hash_bytes); - if hash != ClipboardString::text_hash(text) { - return None; - } - get_clipboard_string(*CLIPBOARD_METADATA_FORMAT) -} - -fn read_image(format: u32) -> Option { - let locked = get_clipboard_data(format)?; - let (bytes, image_format) = if format == CF_DIB.0 as u32 { - (convert_dib_to_bmp(locked.as_bytes())?, ImageFormat::Bmp) - } else { - let image_format = *IMAGE_FORMATS_MAP.get(&format)?; - (locked.as_bytes().to_vec(), image_format) - }; - let id = hash(&bytes); - Some(ClipboardEntry::Image(Image { - format: image_format, - bytes, - id, - })) -} - -fn read_files() -> Option { - let locked = get_clipboard_data(CF_HDROP.0 as u32)?; - let hdrop = HDROP(locked.ptr as *mut _); - let mut filenames = Vec::new(); - with_file_names(hdrop, |name| filenames.push(std::path::PathBuf::from(name))); - Some(ClipboardEntry::ExternalPaths(ExternalPaths( - filenames.into(), - ))) -} - -/// DIB is BMP without the 14-byte BITMAPFILEHEADER. Prepend one. -fn convert_dib_to_bmp(dib: &[u8]) -> Option> { - if dib.len() < 40 { - return None; - } - - let header_size = u32::from_le_bytes(dib[0..4].try_into().ok()?); - let bit_count = u16::from_le_bytes(dib[14..16].try_into().ok()?); - let compression = u32::from_le_bytes(dib[16..20].try_into().ok()?); - - let color_table_size = if bit_count <= 8 { - let colors_used = u32::from_le_bytes(dib[32..36].try_into().ok()?); - (if colors_used == 0 { - 1u32 << bit_count - } else { - colors_used - }) * 4 - } else if compression == 3 { - 12 // BI_BITFIELDS - } else { - 0 - }; - - let pixel_offset = 14 + header_size + color_table_size; - let file_size = 14 + dib.len() as u32; - - let mut bmp = Vec::with_capacity(file_size as usize); - bmp.extend_from_slice(b"BM"); - bmp.extend_from_slice(&file_size.to_le_bytes()); - bmp.extend_from_slice(&[0u8; 4]); // reserved - bmp.extend_from_slice(&pixel_offset.to_le_bytes()); - bmp.extend_from_slice(dib); - Some(bmp) -} - -fn log_unsupported_clipboard_formats() { - let count = unsafe { CountClipboardFormats() }; - let mut format = 0; - for _ in 0..count { - format = unsafe { EnumClipboardFormats(format) }; - let mut buffer = [0u16; 64]; - unsafe { GetClipboardFormatNameW(format, &mut buffer) }; - let format_name = String::from_utf16_lossy(&buffer); - log::warn!( - "Try to paste with unsupported clipboard format: {}, {}.", - format, - format_name - ); - } -} - -fn gpui_to_image_format(value: ImageFormat) -> Option { - match value { - ImageFormat::Png => Some(image::ImageFormat::Png), - ImageFormat::Jpeg => Some(image::ImageFormat::Jpeg), - ImageFormat::Webp => Some(image::ImageFormat::WebP), - ImageFormat::Gif => Some(image::ImageFormat::Gif), - ImageFormat::Bmp => Some(image::ImageFormat::Bmp), - ImageFormat::Tiff => Some(image::ImageFormat::Tiff), - other => { - log::warn!("No image crate equivalent for format: {other:?}"); - None - } - } -} - -struct ClipboardGuard; - -impl ClipboardGuard { - fn open() -> Option { - match unsafe { OpenClipboard(None) } { - Ok(()) => Some(Self), - Err(e) => { - log::error!("Failed to open clipboard: {e}"); - None - } - } - } -} - -impl Drop for ClipboardGuard { - fn drop(&mut self) { - if let Err(e) = unsafe { CloseClipboard() } { - log::error!("Failed to close clipboard: {e}"); - } - } -} - -struct LockedGlobal { - global: HGLOBAL, - ptr: *const u8, - size: usize, -} - -impl LockedGlobal { - fn lock(global: HGLOBAL) -> Option { - let size = unsafe { GlobalSize(global) }; - let ptr = unsafe { GlobalLock(global) }; - if ptr.is_null() { - return None; - } - Some(Self { - global, - ptr: ptr as *const u8, - size, - }) - } - - fn as_bytes(&self) -> &[u8] { - unsafe { std::slice::from_raw_parts(self.ptr, self.size) } - } -} - -impl Drop for LockedGlobal { - fn drop(&mut self) { - unsafe { GlobalUnlock(self.global).ok() }; - } -} diff --git a/crates/gpui_pre_windows/src/color_text_raster.hlsl b/crates/gpui_pre_windows/src/color_text_raster.hlsl deleted file mode 100644 index 2fbc156b..00000000 --- a/crates/gpui_pre_windows/src/color_text_raster.hlsl +++ /dev/null @@ -1,44 +0,0 @@ -#include "alpha_correction.hlsl" - -struct RasterVertexOutput { - float4 position : SV_Position; - float2 texcoord : TEXCOORD0; -}; - -RasterVertexOutput emoji_rasterization_vertex(uint vertexID : SV_VERTEXID) -{ - RasterVertexOutput output; - output.texcoord = float2((vertexID << 1) & 2, vertexID & 2); - output.position = float4(output.texcoord * 2.0f - 1.0f, 0.0f, 1.0f); - output.position.y = -output.position.y; - - return output; -} - -struct PixelInput { - float4 position: SV_Position; - float2 texcoord : TEXCOORD0; -}; - -struct Bounds { - int2 origin; - int2 size; -}; - -Texture2D t_layer : register(t0); -SamplerState s_layer : register(s0); - -cbuffer GlyphLayerTextureParams : register(b0) { - Bounds bounds; - float4 run_color; - float4 gamma_ratios; - float grayscale_enhanced_contrast; - float3 _pad; -}; - -float4 emoji_rasterization_fragment(PixelInput input): SV_Target { - float sample = t_layer.Sample(s_layer, input.texcoord.xy).r; - float alpha_corrected = apply_contrast_and_gamma_correction(sample, run_color.rgb, grayscale_enhanced_contrast, gamma_ratios); - float alpha = alpha_corrected * run_color.a; - return float4(run_color.rgb * alpha, alpha); -} diff --git a/crates/gpui_pre_windows/src/destination_list.rs b/crates/gpui_pre_windows/src/destination_list.rs deleted file mode 100644 index d6967c01..00000000 --- a/crates/gpui_pre_windows/src/destination_list.rs +++ /dev/null @@ -1,207 +0,0 @@ -use std::{path::PathBuf, sync::Arc}; - -use itertools::Itertools; -use smallvec::SmallVec; -use windows::{ - Win32::{ - Foundation::PROPERTYKEY, - Globalization::u_strlen, - System::Com::{CLSCTX_INPROC_SERVER, CoCreateInstance, StructuredStorage::PROPVARIANT}, - UI::{ - Controls::INFOTIPSIZE, - Shell::{ - Common::{IObjectArray, IObjectCollection}, - DestinationList, EnumerableObjectCollection, ICustomDestinationList, IShellLinkW, - PropertiesSystem::IPropertyStore, - ShellLink, - }, - }, - }, - core::{GUID, HSTRING, Interface}, -}; - -use gpui::{Action, MenuItem, SharedString}; - -pub(crate) struct JumpList { - pub(crate) dock_menus: Vec, - pub(crate) recent_workspaces: Arc<[SmallVec<[PathBuf; 2]>]>, -} - -impl JumpList { - pub(crate) fn new() -> Self { - Self { - dock_menus: Vec::default(), - recent_workspaces: Arc::default(), - } - } -} - -pub(crate) struct DockMenuItem { - pub(crate) name: SharedString, - pub(crate) description: SharedString, - pub(crate) action: Box, -} - -impl DockMenuItem { - pub(crate) fn new(item: MenuItem) -> anyhow::Result { - match item { - MenuItem::Action { name, action, .. } => Ok(Self { - name: name.clone(), - description: if name == "New Window" { - "Opens a new window".into() - } else { - name - }, - action, - }), - _ => anyhow::bail!("Only `MenuItem::Action` is supported for dock menu on Windows."), - } - } -} - -// This code is based on the example from Microsoft: -// https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/winui/shell/appshellintegration/RecipePropertyHandler/RecipePropertyHandler.cpp -pub(crate) fn update_jump_list( - recent_workspaces: &[SmallVec<[PathBuf; 2]>], - dock_menus: &[(SharedString, SharedString)], -) -> anyhow::Result>> { - let (list, removed) = create_destination_list()?; - add_recent_folders(&list, recent_workspaces, removed.as_ref())?; - add_dock_menu(&list, dock_menus)?; - unsafe { list.CommitList() }?; - Ok(removed) -} - -// Copied from: -// https://github.com/microsoft/windows-rs/blob/0fc3c2e5a13d4316d242bdeb0a52af611eba8bd4/crates/libs/windows/src/Windows/Win32/Storage/EnhancedStorage/mod.rs#L1881 -const PKEY_TITLE: PROPERTYKEY = PROPERTYKEY { - fmtid: GUID::from_u128(0xf29f85e0_4ff9_1068_ab91_08002b27b3d9), - pid: 2, -}; - -fn create_destination_list() -> anyhow::Result<(ICustomDestinationList, Vec>)> -{ - let list: ICustomDestinationList = - unsafe { CoCreateInstance(&DestinationList, None, CLSCTX_INPROC_SERVER) }?; - - let mut slots = 0; - let user_removed: IObjectArray = unsafe { list.BeginList(&mut slots) }?; - - let count = unsafe { user_removed.GetCount() }?; - if count == 0 { - return Ok((list, Vec::new())); - } - - let mut removed = Vec::with_capacity(count as usize); - for i in 0..count { - let shell_link: IShellLinkW = unsafe { user_removed.GetAt(i)? }; - let description = { - // INFOTIPSIZE is the maximum size of the buffer - // see https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishelllinkw-getdescription - let mut buffer = [0u16; INFOTIPSIZE as usize]; - unsafe { shell_link.GetDescription(&mut buffer)? }; - let len = unsafe { u_strlen(buffer.as_ptr()) }; - String::from_utf16_lossy(&buffer[..len as usize]) - }; - let args = description.split('\n').map(PathBuf::from).collect(); - - removed.push(args); - } - - Ok((list, removed)) -} - -fn add_dock_menu( - list: &ICustomDestinationList, - dock_menus: &[(SharedString, SharedString)], -) -> anyhow::Result<()> { - unsafe { - let tasks: IObjectCollection = - CoCreateInstance(&EnumerableObjectCollection, None, CLSCTX_INPROC_SERVER)?; - for (idx, (name, description)) in dock_menus.iter().enumerate() { - let argument = HSTRING::from(format!("--dock-action {}", idx)); - let description = HSTRING::from(description.as_str()); - let display = name.as_str(); - let task = create_shell_link(argument, description, None, display)?; - tasks.AddObject(&task)?; - } - list.AddUserTasks(&tasks)?; - Ok(()) - } -} - -fn add_recent_folders( - list: &ICustomDestinationList, - entries: &[SmallVec<[PathBuf; 2]>], - removed: &Vec>, -) -> anyhow::Result<()> { - unsafe { - let tasks: IObjectCollection = - CoCreateInstance(&EnumerableObjectCollection, None, CLSCTX_INPROC_SERVER)?; - - for folder_path in entries.iter().filter(|path| !removed.contains(path)) { - let argument = HSTRING::from( - folder_path - .iter() - .map(|path| format!("\"{}\"", path.display())) - .join(" "), - ); - - let description = HSTRING::from( - folder_path - .iter() - .map(|path| path.to_string_lossy()) - .collect::>() - .join("\n"), - ); - // simulate folder icon - // https://github.com/microsoft/vscode/blob/7a5dc239516a8953105da34f84bae152421a8886/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts#L380 - let icon = HSTRING::from("explorer.exe"); - - let display = folder_path - .iter() - .map(|p| { - p.file_name() - .map(|name| name.to_string_lossy()) - .unwrap_or_else(|| p.to_string_lossy()) - }) - .join(", "); - - tasks.AddObject(&create_shell_link( - argument, - description, - Some(icon), - &display, - )?)?; - } - - if tasks.GetCount().unwrap_or(0) > 0 { - list.AppendCategory(&HSTRING::from("Recent Folders"), &tasks)?; - } - Ok(()) - } -} - -fn create_shell_link( - argument: HSTRING, - description: HSTRING, - icon: Option, - display: &str, -) -> anyhow::Result { - unsafe { - let link: IShellLinkW = CoCreateInstance(&ShellLink, None, CLSCTX_INPROC_SERVER)?; - let exe_path = HSTRING::from(std::env::current_exe()?.as_os_str()); - link.SetPath(&exe_path)?; - link.SetArguments(&argument)?; - link.SetDescription(&description)?; - if let Some(icon) = icon { - link.SetIconLocation(&icon, 0)?; - } - let store: IPropertyStore = link.cast()?; - let title = PROPVARIANT::from(display); - store.SetValue(&PKEY_TITLE, &title)?; - store.Commit()?; - - Ok(link) - } -} diff --git a/crates/gpui_pre_windows/src/direct_manipulation.rs b/crates/gpui_pre_windows/src/direct_manipulation.rs deleted file mode 100644 index 9adcbed3..00000000 --- a/crates/gpui_pre_windows/src/direct_manipulation.rs +++ /dev/null @@ -1,359 +0,0 @@ -use std::cell::{Cell, RefCell}; -use std::rc::Rc; - -use anyhow::Result; -use gpui::*; -use gpui_util::ResultExt; -use windows::Win32::{ - Foundation::*, - Graphics::{DirectManipulation::*, Gdi::*}, - System::Com::*, - UI::{Input::Pointer::*, WindowsAndMessaging::*}, -}; - -use crate::*; - -/// Default viewport size in pixels. The actual content size doesn't matter -/// because we're using the viewport only for gesture recognition, not for -/// visual output. -const DEFAULT_VIEWPORT_SIZE: i32 = 1000; - -pub(crate) struct DirectManipulationHandler { - manager: IDirectManipulationManager, - update_manager: IDirectManipulationUpdateManager, - viewport: IDirectManipulationViewport, - _handler_cookie: u32, - window: HWND, - scale_factor: Rc>, - pending_events: Rc>>, -} - -impl DirectManipulationHandler { - pub fn new(window: HWND, scale_factor: f32) -> Result { - unsafe { - let manager: IDirectManipulationManager = - CoCreateInstance(&DirectManipulationManager, None, CLSCTX_INPROC_SERVER)?; - - let update_manager: IDirectManipulationUpdateManager = manager.GetUpdateManager()?; - - let viewport: IDirectManipulationViewport = manager.CreateViewport(None, window)?; - - let configuration = DIRECTMANIPULATION_CONFIGURATION_INTERACTION - | DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X - | DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y - | DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA - | DIRECTMANIPULATION_CONFIGURATION_RAILS_X - | DIRECTMANIPULATION_CONFIGURATION_RAILS_Y - | DIRECTMANIPULATION_CONFIGURATION_SCALING; - viewport.ActivateConfiguration(configuration)?; - - viewport.SetViewportOptions( - DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE - | DIRECTMANIPULATION_VIEWPORT_OPTIONS_DISABLEPIXELSNAPPING, - )?; - - let mut rect = RECT { - left: 0, - top: 0, - right: DEFAULT_VIEWPORT_SIZE, - bottom: DEFAULT_VIEWPORT_SIZE, - }; - viewport.SetViewportRect(&mut rect)?; - - manager.Activate(window)?; - viewport.Enable()?; - - let scale_factor = Rc::new(Cell::new(scale_factor)); - let pending_events = Rc::new(RefCell::new(Vec::new())); - - let event_handler: IDirectManipulationViewportEventHandler = - DirectManipulationEventHandler::new( - window, - Rc::clone(&scale_factor), - Rc::clone(&pending_events), - ) - .into(); - - let handler_cookie = viewport.AddEventHandler(Some(window), &event_handler)?; - - update_manager.Update(None)?; - - Ok(Self { - manager, - update_manager, - viewport, - _handler_cookie: handler_cookie, - window, - scale_factor, - pending_events, - }) - } - } - - pub fn set_scale_factor(&self, scale_factor: f32) { - self.scale_factor.set(scale_factor); - } - - pub fn on_pointer_hit_test(&self, wparam: WPARAM) { - unsafe { - let pointer_id = wparam.loword() as u32; - let mut pointer_type = POINTER_INPUT_TYPE::default(); - if GetPointerType(pointer_id, &mut pointer_type).is_ok() && pointer_type == PT_TOUCHPAD - { - self.viewport.SetContact(pointer_id).log_err(); - } - } - } - - pub fn update(&self) { - unsafe { - self.update_manager.Update(None).log_err(); - } - } - - pub fn drain_events(&self) -> Vec { - std::mem::take(&mut *self.pending_events.borrow_mut()) - } -} - -impl Drop for DirectManipulationHandler { - fn drop(&mut self) { - unsafe { - self.viewport.Stop().log_err(); - self.viewport.Abandon().log_err(); - self.manager.Deactivate(self.window).log_err(); - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum GestureKind { - None, - Scroll, - Pinch, -} - -#[windows_core::implement(IDirectManipulationViewportEventHandler)] -struct DirectManipulationEventHandler { - window: HWND, - scale_factor: Rc>, - gesture_kind: Cell, - last_scale: Cell, - last_x_offset: Cell, - last_y_offset: Cell, - scroll_phase: Cell, - pending_events: Rc>>, -} - -impl DirectManipulationEventHandler { - fn new( - window: HWND, - scale_factor: Rc>, - pending_events: Rc>>, - ) -> Self { - Self { - window, - scale_factor, - gesture_kind: Cell::new(GestureKind::None), - last_scale: Cell::new(1.0), - last_x_offset: Cell::new(0.0), - last_y_offset: Cell::new(0.0), - scroll_phase: Cell::new(TouchPhase::Started), - pending_events, - } - } - - fn end_gesture(&self) { - let position = self.mouse_position(); - let modifiers = current_modifiers(); - match self.gesture_kind.get() { - GestureKind::Scroll => { - self.pending_events - .borrow_mut() - .push(PlatformInput::ScrollWheel(ScrollWheelEvent { - position, - delta: ScrollDelta::Pixels(point(px(0.0), px(0.0))), - modifiers, - touch_phase: TouchPhase::Ended, - })); - } - GestureKind::Pinch => { - self.pending_events - .borrow_mut() - .push(PlatformInput::Pinch(PinchEvent { - position, - delta: 0.0, - modifiers, - phase: TouchPhase::Ended, - })); - } - GestureKind::None => {} - } - self.gesture_kind.set(GestureKind::None); - } - - fn mouse_position(&self) -> Point { - let scale_factor = self.scale_factor.get(); - unsafe { - let mut point: POINT = std::mem::zeroed(); - let _ = GetCursorPos(&mut point); - let _ = ScreenToClient(self.window, &mut point); - logical_point(point.x as f32, point.y as f32, scale_factor) - } - } -} - -impl IDirectManipulationViewportEventHandler_Impl for DirectManipulationEventHandler_Impl { - fn OnViewportStatusChanged( - &self, - viewport: windows_core::Ref<'_, IDirectManipulationViewport>, - current: DIRECTMANIPULATION_STATUS, - previous: DIRECTMANIPULATION_STATUS, - ) -> windows_core::Result<()> { - if current == previous { - return Ok(()); - } - - // A new gesture interrupted inertia, so end the old sequence. - if current == DIRECTMANIPULATION_RUNNING && previous == DIRECTMANIPULATION_INERTIA { - self.end_gesture(); - } - - if current == DIRECTMANIPULATION_READY { - self.end_gesture(); - - // Reset the content transform so the viewport is ready for the next gesture. - // ZoomToRect triggers a second RUNNING -> READY cycle, so prevent an infinite loop here. - if self.last_scale.get() != 1.0 - || self.last_x_offset.get() != 0.0 - || self.last_y_offset.get() != 0.0 - { - if let Some(viewport) = viewport.as_ref() { - unsafe { - viewport - .ZoomToRect( - 0.0, - 0.0, - DEFAULT_VIEWPORT_SIZE as f32, - DEFAULT_VIEWPORT_SIZE as f32, - false, - ) - .log_err(); - } - } - } - - self.last_scale.set(1.0); - self.last_x_offset.set(0.0); - self.last_y_offset.set(0.0); - } - - Ok(()) - } - - fn OnViewportUpdated( - &self, - _viewport: windows_core::Ref<'_, IDirectManipulationViewport>, - ) -> windows_core::Result<()> { - Ok(()) - } - - fn OnContentUpdated( - &self, - _viewport: windows_core::Ref<'_, IDirectManipulationViewport>, - content: windows_core::Ref<'_, IDirectManipulationContent>, - ) -> windows_core::Result<()> { - let content = content.as_ref().ok_or(E_POINTER)?; - - // Get the 6-element content transform: [scale, 0, 0, scale, tx, ty] - let mut xform = [0.0f32; 6]; - unsafe { - content.GetContentTransform(&mut xform)?; - } - - let scale = xform[0]; - let scale_factor = self.scale_factor.get(); - let x_offset = xform[4] / scale_factor; - let y_offset = xform[5] / scale_factor; - - if scale == 0.0 { - return Ok(()); - } - - let last_scale = self.last_scale.get(); - let last_x = self.last_x_offset.get(); - let last_y = self.last_y_offset.get(); - - if float_equals(scale, last_scale) - && float_equals(x_offset, last_x) - && float_equals(y_offset, last_y) - { - return Ok(()); - } - - let position = self.mouse_position(); - let modifiers = current_modifiers(); - - // Direct Manipulation reports both translation and scale in every content update. - // Translation values can shift during a pinch due to the zoom center shifting. - // We classify each gesture as either scroll or pinch and only emit one type of event. - // We allow Scroll -> Pinch (a pinch can start with a small pan) but not the reverse. - if !float_equals(scale, 1.0) { - if self.gesture_kind.get() != GestureKind::Pinch { - self.end_gesture(); - self.gesture_kind.set(GestureKind::Pinch); - self.pending_events - .borrow_mut() - .push(PlatformInput::Pinch(PinchEvent { - position, - delta: 0.0, - modifiers, - phase: TouchPhase::Started, - })); - } - } else if self.gesture_kind.get() == GestureKind::None { - self.gesture_kind.set(GestureKind::Scroll); - self.scroll_phase.set(TouchPhase::Started); - } - - match self.gesture_kind.get() { - GestureKind::Scroll => { - let dx = x_offset - last_x; - let dy = y_offset - last_y; - let touch_phase = self.scroll_phase.get(); - self.scroll_phase.set(TouchPhase::Moved); - self.pending_events - .borrow_mut() - .push(PlatformInput::ScrollWheel(ScrollWheelEvent { - position, - delta: ScrollDelta::Pixels(point(px(dx), px(dy))), - modifiers, - touch_phase, - })); - } - GestureKind::Pinch => { - let scale_delta = scale / last_scale; - self.pending_events - .borrow_mut() - .push(PlatformInput::Pinch(PinchEvent { - position, - delta: scale_delta - 1.0, - modifiers, - phase: TouchPhase::Moved, - })); - } - GestureKind::None => {} - } - - self.last_scale.set(scale); - self.last_x_offset.set(x_offset); - self.last_y_offset.set(y_offset); - - Ok(()) - } -} - -fn float_equals(f1: f32, f2: f32) -> bool { - const EPSILON_SCALE: f32 = 0.00001; - (f1 - f2).abs() < EPSILON_SCALE * f1.abs().max(f2.abs()).max(EPSILON_SCALE) -} diff --git a/crates/gpui_pre_windows/src/direct_write.rs b/crates/gpui_pre_windows/src/direct_write.rs deleted file mode 100644 index 5a761cce..00000000 --- a/crates/gpui_pre_windows/src/direct_write.rs +++ /dev/null @@ -1,2161 +0,0 @@ -use std::{ - borrow::Cow, - ffi::{c_uint, c_void}, - mem::ManuallyDrop, -}; - -use anyhow::{Context, Result}; -use collections::HashMap; -use gpui_util::{ResultExt, maybe}; -use parking_lot::{RwLock, RwLockUpgradableReadGuard}; -use windows::{ - Win32::{ - Foundation::*, - Globalization::GetUserDefaultLocaleName, - Graphics::{ - Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, Direct3D11::*, DirectWrite::*, - Dxgi::Common::*, Gdi::LOGFONTW, - }, - System::SystemServices::LOCALE_NAME_MAX_LENGTH, - UI::WindowsAndMessaging::*, - }, - core::*, -}; -use windows_numerics::Vector2; - -use crate::*; -use gpui::*; - -#[derive(Debug)] -struct FontInfo { - font_family_h: HSTRING, - font_face: IDWriteFontFace3, - features: IDWriteTypography, - fallbacks: Option, - font_collection: IDWriteFontCollection1, -} - -pub(crate) struct DirectWriteTextSystem { - components: DirectWriteComponents, - state: RwLock, -} - -struct DirectWriteComponents { - locale: HSTRING, - factory: IDWriteFactory5, - in_memory_loader: IDWriteInMemoryFontFileLoader, - builder: IDWriteFontSetBuilder1, - text_renderer: TextRendererWrapper, - system_ui_font_name: SharedString, - system_subpixel_rendering: bool, -} - -impl Drop for DirectWriteComponents { - fn drop(&mut self) { - unsafe { - let _ = self - .factory - .UnregisterFontFileLoader(&self.in_memory_loader); - } - } -} - -struct GPUState { - device: ID3D11Device, - device_context: ID3D11DeviceContext, - sampler: Option, - blend_state: ID3D11BlendState, - vertex_shader: ID3D11VertexShader, - pixel_shader: ID3D11PixelShader, -} - -struct DirectWriteState { - gpu_state: GPUState, - system_font_collection: IDWriteFontCollection1, - custom_font_collection: IDWriteFontCollection1, - fonts: Vec, - font_to_font_id: HashMap, - font_info_cache: HashMap, - layout_line_scratch: Vec, -} - -impl GPUState { - fn new(directx_devices: &DirectXDevices) -> Result { - let device = directx_devices.device.clone(); - let device_context = directx_devices.device_context.clone(); - - let blend_state = { - let mut blend_state = None; - let desc = D3D11_BLEND_DESC { - AlphaToCoverageEnable: false.into(), - IndependentBlendEnable: false.into(), - RenderTarget: [ - D3D11_RENDER_TARGET_BLEND_DESC { - BlendEnable: true.into(), - SrcBlend: D3D11_BLEND_ONE, - DestBlend: D3D11_BLEND_INV_SRC_ALPHA, - BlendOp: D3D11_BLEND_OP_ADD, - SrcBlendAlpha: D3D11_BLEND_ONE, - DestBlendAlpha: D3D11_BLEND_INV_SRC_ALPHA, - BlendOpAlpha: D3D11_BLEND_OP_ADD, - RenderTargetWriteMask: D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8, - }, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ], - }; - unsafe { device.CreateBlendState(&desc, Some(&mut blend_state)) }?; - blend_state.unwrap() - }; - - let sampler = { - let mut sampler = None; - let desc = D3D11_SAMPLER_DESC { - Filter: D3D11_FILTER_MIN_MAG_MIP_POINT, - AddressU: D3D11_TEXTURE_ADDRESS_BORDER, - AddressV: D3D11_TEXTURE_ADDRESS_BORDER, - AddressW: D3D11_TEXTURE_ADDRESS_BORDER, - MipLODBias: 0.0, - MaxAnisotropy: 1, - ComparisonFunc: D3D11_COMPARISON_ALWAYS, - BorderColor: [0.0, 0.0, 0.0, 0.0], - MinLOD: 0.0, - MaxLOD: 0.0, - }; - unsafe { device.CreateSamplerState(&desc, Some(&mut sampler)) }?; - sampler - }; - - let vertex_shader = { - let source = shader_resources::RawShaderBytes::new( - shader_resources::ShaderModule::EmojiRasterization, - shader_resources::ShaderTarget::Vertex, - )?; - let mut shader = None; - unsafe { device.CreateVertexShader(source.as_bytes(), None, Some(&mut shader)) }?; - shader.unwrap() - }; - - let pixel_shader = { - let source = shader_resources::RawShaderBytes::new( - shader_resources::ShaderModule::EmojiRasterization, - shader_resources::ShaderTarget::Fragment, - )?; - let mut shader = None; - unsafe { device.CreatePixelShader(source.as_bytes(), None, Some(&mut shader)) }?; - shader.unwrap() - }; - - Ok(Self { - device, - device_context, - sampler, - blend_state, - vertex_shader, - pixel_shader, - }) - } -} - -impl DirectWriteTextSystem { - pub(crate) fn new(directx_devices: &DirectXDevices) -> Result { - let factory: IDWriteFactory5 = unsafe { DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)? }; - // The `IDWriteInMemoryFontFileLoader` here is supported starting from - // Windows 10 Creators Update, which consequently requires the entire - // `DirectWriteTextSystem` to run on `win10 1703`+. - let in_memory_loader = unsafe { factory.CreateInMemoryFontFileLoader()? }; - unsafe { factory.RegisterFontFileLoader(&in_memory_loader)? }; - let builder = unsafe { factory.CreateFontSetBuilder()? }; - let mut locale = [0u16; LOCALE_NAME_MAX_LENGTH as usize]; - unsafe { GetUserDefaultLocaleName(&mut locale) }; - let locale = HSTRING::from_wide(&locale); - let text_renderer = TextRendererWrapper::new(locale.clone()); - - let gpu_state = GPUState::new(directx_devices)?; - - let system_subpixel_rendering = get_system_subpixel_rendering(); - let system_ui_font_name = get_system_ui_font_name(); - let components = DirectWriteComponents { - locale, - factory, - in_memory_loader, - builder, - text_renderer, - system_ui_font_name, - system_subpixel_rendering, - }; - - let system_font_collection = unsafe { - let mut result = None; - components - .factory - .GetSystemFontCollection(false, &mut result, true)?; - result.context("Failed to get system font collection")? - }; - let custom_font_set = unsafe { components.builder.CreateFontSet()? }; - let custom_font_collection = unsafe { - components - .factory - .CreateFontCollectionFromFontSet(&custom_font_set)? - }; - - Ok(Self { - components, - state: RwLock::new(DirectWriteState { - gpu_state, - system_font_collection, - custom_font_collection, - fonts: Vec::new(), - font_to_font_id: HashMap::default(), - font_info_cache: HashMap::default(), - layout_line_scratch: Vec::new(), - }), - }) - } - - pub(crate) fn handle_gpu_lost(&self, directx_devices: &DirectXDevices) -> Result<()> { - self.state.write().handle_gpu_lost(directx_devices) - } -} - -impl PlatformTextSystem for DirectWriteTextSystem { - fn add_fonts(&self, fonts: Vec>) -> Result<()> { - self.state.write().add_fonts(&self.components, fonts) - } - - fn all_font_names(&self) -> Vec { - self.state.read().all_font_names(&self.components) - } - - fn font_id(&self, font: &Font) -> Result { - let lock = self.state.upgradable_read(); - if let Some(font_id) = lock.font_to_font_id.get(font) { - Ok(*font_id) - } else { - RwLockUpgradableReadGuard::upgrade(lock) - .select_and_cache_font(&self.components, font) - .with_context(|| format!("Failed to select font: {:?}", font)) - } - } - - fn font_metrics(&self, font_id: FontId) -> FontMetrics { - self.state.read().font_metrics(font_id) - } - - fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { - self.state.read().get_typographic_bounds(font_id, glyph_id) - } - - fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result> { - self.state.read().get_advance(font_id, glyph_id) - } - - fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { - self.state.read().glyph_for_char(font_id, ch) - } - - fn glyph_raster_bounds( - &self, - params: &RenderGlyphParams, - ) -> anyhow::Result> { - self.state.read().raster_bounds(&self.components, params) - } - - fn rasterize_glyph( - &self, - params: &RenderGlyphParams, - raster_bounds: Bounds, - ) -> anyhow::Result<(Size, Vec)> { - self.state - .read() - .rasterize_glyph(&self.components, params, raster_bounds) - } - - fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout { - self.state - .write() - .layout_line(&self.components, text, font_size, runs) - .log_err() - .unwrap_or(LineLayout { - font_size, - ..Default::default() - }) - } - - fn recommended_rendering_mode( - &self, - _font_id: FontId, - _font_size: Pixels, - ) -> TextRenderingMode { - if self.components.system_subpixel_rendering { - TextRenderingMode::Subpixel - } else { - TextRenderingMode::Grayscale - } - } -} - -impl DirectWriteState { - fn select_and_cache_font( - &mut self, - components: &DirectWriteComponents, - font: &Font, - ) -> Option { - let select_font = |this: &mut DirectWriteState, font: &Font| -> Option { - let info = [&this.custom_font_collection, &this.system_font_collection] - .into_iter() - .find_map(|font_collection| unsafe { - DirectWriteState::make_font_from_font_collection( - font, - font_collection, - &components.factory, - &this.system_font_collection, - &components.system_ui_font_name, - ) - })?; - - let font_id = FontId(this.fonts.len()); - let font_face_key = info.font_face.cast::().unwrap().as_raw().addr(); - this.fonts.push(info); - this.font_info_cache.insert(font_face_key, font_id); - Some(font_id) - }; - - let mut font_id = select_font(self, font); - if font_id.is_none() { - // try updating system fonts and reselect - let mut collection = None; - let font_collection_updated = unsafe { - components - .factory - .GetSystemFontCollection(false, &mut collection, true) - } - .log_err() - .is_some(); - if font_collection_updated && let Some(collection) = collection { - self.system_font_collection = collection; - } - font_id = select_font(self, font); - }; - let font_id = font_id?; - self.font_to_font_id.insert(font.clone(), font_id); - Some(font_id) - } - - fn add_fonts( - &mut self, - components: &DirectWriteComponents, - fonts: Vec>, - ) -> Result<()> { - for font_data in fonts { - match font_data { - Cow::Borrowed(data) => unsafe { - let font_file = components - .in_memory_loader - .CreateInMemoryFontFileReference( - &components.factory, - data.as_ptr().cast(), - data.len() as _, - None, - )?; - components.builder.AddFontFile(&font_file)?; - }, - Cow::Owned(data) => unsafe { - let font_file = components - .in_memory_loader - .CreateInMemoryFontFileReference( - &components.factory, - data.as_ptr().cast(), - data.len() as _, - None, - )?; - components.builder.AddFontFile(&font_file)?; - }, - } - } - let set = unsafe { components.builder.CreateFontSet()? }; - let collection = unsafe { components.factory.CreateFontCollectionFromFontSet(&set)? }; - self.custom_font_collection = collection; - - Ok(()) - } - - fn generate_font_fallbacks( - fallbacks: &FontFallbacks, - factory: &IDWriteFactory5, - system_font_collection: &IDWriteFontCollection1, - ) -> Result> { - let fallback_list = fallbacks.fallback_list(); - if fallback_list.is_empty() { - return Ok(None); - } - unsafe { - let builder = factory.CreateFontFallbackBuilder()?; - let font_set = &system_font_collection.GetFontSet()?; - let mut unicode_ranges = Vec::new(); - for family_name in fallback_list { - let family_name = HSTRING::from(family_name); - let Some(fonts) = font_set - .GetMatchingFonts( - &family_name, - DWRITE_FONT_WEIGHT_NORMAL, - DWRITE_FONT_STRETCH_NORMAL, - DWRITE_FONT_STYLE_NORMAL, - ) - .log_err() - else { - continue; - }; - let Ok(font_face) = fonts.GetFontFaceReference(0) else { - continue; - }; - let font = font_face.CreateFontFace()?; - let mut count = 0; - font.GetUnicodeRanges(None, &mut count).ok(); - if count == 0 { - continue; - } - unicode_ranges.clear(); - unicode_ranges.resize_with(count as usize, DWRITE_UNICODE_RANGE::default); - let Some(_) = font - .GetUnicodeRanges(Some(&mut unicode_ranges), &mut count) - .log_err() - else { - continue; - }; - builder.AddMapping( - &unicode_ranges, - &[family_name.as_ptr()], - None, - None, - None, - 1.0, - )?; - } - let system_fallbacks = factory.GetSystemFontFallback()?; - builder.AddMappings(&system_fallbacks)?; - Ok(Some(builder.CreateFontFallback()?)) - } - } - - unsafe fn generate_font_features( - factory: &IDWriteFactory5, - font_features: &FontFeatures, - ) -> Result { - let direct_write_features = unsafe { factory.CreateTypography()? }; - apply_font_features(&direct_write_features, font_features)?; - Ok(direct_write_features) - } - - unsafe fn make_font_from_font_collection( - &Font { - ref family, - ref features, - ref fallbacks, - weight, - style, - }: &Font, - collection: &IDWriteFontCollection1, - factory: &IDWriteFactory5, - system_font_collection: &IDWriteFontCollection1, - system_ui_font_name: &SharedString, - ) -> Option { - const SYSTEM_UI_FONT_NAME: &str = ".SystemUIFont"; - let family = if family == SYSTEM_UI_FONT_NAME { - system_ui_font_name - } else { - gpui::font_name_with_fallbacks_shared(&family, &system_ui_font_name) - }; - let fontset = unsafe { collection.GetFontSet().log_err()? }; - let font_family_h = HSTRING::from(family.as_str()); - let font = unsafe { - fontset - .GetMatchingFonts( - &font_family_h, - font_weight_to_dwrite(weight), - DWRITE_FONT_STRETCH_NORMAL, - font_style_to_dwrite(style), - ) - .log_err()? - }; - let total_number = unsafe { font.GetFontCount() }; - for index in 0..total_number { - let res = maybe!({ - let font_face_ref = unsafe { font.GetFontFaceReference(index).log_err()? }; - let font_face = unsafe { font_face_ref.CreateFontFace().log_err()? }; - let direct_write_features = - unsafe { Self::generate_font_features(factory, features).log_err()? }; - let fallbacks = fallbacks.as_ref().and_then(|fallbacks| { - Self::generate_font_fallbacks(fallbacks, factory, system_font_collection) - .log_err() - .flatten() - }); - let font_info = FontInfo { - font_family_h: font_family_h.clone(), - font_face, - features: direct_write_features, - fallbacks, - font_collection: collection.clone(), - }; - Some(font_info) - }); - if res.is_some() { - return res; - } - } - None - } - - fn layout_line( - &mut self, - components: &DirectWriteComponents, - text: &str, - font_size: Pixels, - font_runs: &[FontRun], - ) -> Result { - if font_runs.is_empty() { - return Ok(LineLayout { - font_size, - ..Default::default() - }); - } - unsafe { - self.layout_line_scratch.clear(); - self.layout_line_scratch.extend(text.encode_utf16()); - let text_wide = &*self.layout_line_scratch; - - let mut utf8_offset = 0usize; - let mut utf16_offset = 0u32; - let text_layout = { - let first_run = &font_runs[0]; - let font_info = &self.fonts[first_run.font_id.0]; - let collection = &font_info.font_collection; - let format: IDWriteTextFormat1 = components - .factory - .CreateTextFormat( - &font_info.font_family_h, - collection, - font_info.font_face.GetWeight(), - font_info.font_face.GetStyle(), - DWRITE_FONT_STRETCH_NORMAL, - font_size.as_f32(), - &components.locale, - )? - .cast()?; - if let Some(ref fallbacks) = font_info.fallbacks { - format.SetFontFallback(fallbacks)?; - } - - let layout = components.factory.CreateTextLayout( - text_wide, - &format, - f32::INFINITY, - f32::INFINITY, - )?; - let current_text = &text[utf8_offset..(utf8_offset + first_run.len)]; - utf8_offset += first_run.len; - let current_text_utf16_length = current_text.encode_utf16().count() as u32; - let text_range = DWRITE_TEXT_RANGE { - startPosition: utf16_offset, - length: current_text_utf16_length, - }; - layout.SetTypography(&font_info.features, text_range)?; - utf16_offset += current_text_utf16_length; - - layout - }; - - let (ascent, descent) = { - let mut first_metrics = [DWRITE_LINE_METRICS::default(); 4]; - let mut line_count = 0u32; - text_layout.GetLineMetrics(Some(&mut first_metrics), &mut line_count)?; - ( - px(first_metrics[0].baseline), - px(first_metrics[0].height - first_metrics[0].baseline), - ) - }; - let mut break_ligatures = true; - for run in &font_runs[1..] { - let font_info = &self.fonts[run.font_id.0]; - let current_text = &text[utf8_offset..(utf8_offset + run.len)]; - utf8_offset += run.len; - let current_text_utf16_length = current_text.encode_utf16().count() as u32; - - let collection = &font_info.font_collection; - let text_range = DWRITE_TEXT_RANGE { - startPosition: utf16_offset, - length: current_text_utf16_length, - }; - utf16_offset += current_text_utf16_length; - text_layout.SetFontCollection(collection, text_range)?; - text_layout.SetFontFamilyName(&font_info.font_family_h, text_range)?; - let font_size = if break_ligatures { - font_size.as_f32().next_up() - } else { - font_size.as_f32() - }; - text_layout.SetFontSize(font_size, text_range)?; - text_layout.SetFontStyle(font_info.font_face.GetStyle(), text_range)?; - text_layout.SetFontWeight(font_info.font_face.GetWeight(), text_range)?; - text_layout.SetTypography(&font_info.features, text_range)?; - - break_ligatures = !break_ligatures; - } - - let mut runs = Vec::new(); - let mut renderer_context = RendererContext { - text_system: self, - components, - index_converter: StringIndexConverter::new(text), - runs: &mut runs, - width: 0.0, - }; - text_layout.Draw( - Some((&raw mut renderer_context).cast::().cast_const()), - &components.text_renderer.0, - 0.0, - 0.0, - )?; - let width = px(renderer_context.width); - - Ok(LineLayout { - font_size, - width, - ascent, - descent, - runs, - len: text.len(), - }) - } - } - - fn font_metrics(&self, font_id: FontId) -> FontMetrics { - unsafe { - let font_info = &self.fonts[font_id.0]; - let mut metrics = std::mem::zeroed(); - font_info.font_face.GetMetrics(&mut metrics); - - FontMetrics { - units_per_em: metrics.Base.designUnitsPerEm as _, - ascent: metrics.Base.ascent as _, - descent: -(metrics.Base.descent as f32), - line_gap: metrics.Base.lineGap as _, - underline_position: metrics.Base.underlinePosition as _, - underline_thickness: metrics.Base.underlineThickness as _, - cap_height: metrics.Base.capHeight as _, - x_height: metrics.Base.xHeight as _, - bounding_box: Bounds { - origin: Point { - x: metrics.glyphBoxLeft as _, - y: metrics.glyphBoxBottom as _, - }, - size: Size { - width: (metrics.glyphBoxRight - metrics.glyphBoxLeft) as _, - height: (metrics.glyphBoxTop - metrics.glyphBoxBottom) as _, - }, - }, - } - } - } - - fn create_glyph_run_analysis( - &self, - components: &DirectWriteComponents, - params: &RenderGlyphParams, - ) -> Result { - let font = &self.fonts[params.font_id.0]; - let glyph_id = [params.glyph_id.0 as u16]; - let advance = [0.0]; - let offset = [DWRITE_GLYPH_OFFSET::default()]; - let glyph_run = DWRITE_GLYPH_RUN { - fontFace: ManuallyDrop::new(Some(unsafe { std::ptr::read(&***font.font_face) })), - fontEmSize: params.font_size.as_f32(), - glyphCount: 1, - glyphIndices: glyph_id.as_ptr(), - glyphAdvances: advance.as_ptr(), - glyphOffsets: offset.as_ptr(), - isSideways: BOOL(0), - bidiLevel: 0, - }; - let transform = DWRITE_MATRIX { - m11: params.scale_factor, - m12: 0.0, - m21: 0.0, - m22: params.scale_factor, - dx: 0.0, - dy: 0.0, - }; - let baseline_origin_x = - params.subpixel_variant.x as f32 / SUBPIXEL_VARIANTS_X as f32 / params.scale_factor; - let baseline_origin_y = params.subpixel_variant.y as f32 - / gpui::SUBPIXEL_VARIANTS_Y as f32 - / params.scale_factor; - - let mut rendering_mode = DWRITE_RENDERING_MODE1::default(); - let mut grid_fit_mode = DWRITE_GRID_FIT_MODE::default(); - unsafe { - font.font_face.GetRecommendedRenderingMode( - params.font_size.as_f32(), - // Using 96 as scale is applied by the transform - 96.0, - 96.0, - Some(&transform), - false, - DWRITE_OUTLINE_THRESHOLD_ANTIALIASED, - DWRITE_MEASURING_MODE_NATURAL, - None, - &mut rendering_mode, - &mut grid_fit_mode, - )?; - } - let rendering_mode = match rendering_mode { - DWRITE_RENDERING_MODE1_OUTLINE => DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC, - m => m, - }; - - let antialias_mode = if params.subpixel_rendering { - DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE - } else { - DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE - }; - - let glyph_analysis = unsafe { - components.factory.CreateGlyphRunAnalysis( - &glyph_run, - Some(&transform), - rendering_mode, - DWRITE_MEASURING_MODE_NATURAL, - grid_fit_mode, - antialias_mode, - baseline_origin_x, - baseline_origin_y, - ) - }?; - Ok(glyph_analysis) - } - - fn raster_bounds( - &self, - components: &DirectWriteComponents, - params: &RenderGlyphParams, - ) -> Result> { - let glyph_analysis = self.create_glyph_run_analysis(components, params)?; - - let texture_type = if params.subpixel_rendering { - DWRITE_TEXTURE_CLEARTYPE_3x1 - } else { - DWRITE_TEXTURE_ALIASED_1x1 - }; - - let bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; - - if bounds.right < bounds.left { - Ok(Bounds { - origin: point(0.into(), 0.into()), - size: size(0.into(), 0.into()), - }) - } else { - Ok(Bounds { - origin: point(bounds.left.into(), bounds.top.into()), - size: size( - (bounds.right - bounds.left).into(), - (bounds.bottom - bounds.top).into(), - ), - }) - } - } - - fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { - let font_info = &self.fonts[font_id.0]; - let codepoints = ch as u32; - let mut glyph_indices = 0u16; - unsafe { - font_info - .font_face - .GetGlyphIndices(&raw const codepoints, 1, &raw mut glyph_indices) - .log_err() - } - .map(|_| GlyphId(glyph_indices as u32)) - } - - fn rasterize_glyph( - &self, - components: &DirectWriteComponents, - params: &RenderGlyphParams, - glyph_bounds: Bounds, - ) -> Result<(Size, Vec)> { - if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 { - anyhow::bail!("glyph bounds are empty"); - } - - let bitmap_data = if params.is_emoji { - if let Ok(color) = self.rasterize_color(components, params, glyph_bounds) { - color - } else { - let monochrome = self.rasterize_monochrome(components, params, glyph_bounds)?; - monochrome - .into_iter() - .flat_map(|pixel| [0, 0, 0, pixel]) - .collect::>() - } - } else { - self.rasterize_monochrome(components, params, glyph_bounds)? - }; - - Ok((glyph_bounds.size, bitmap_data)) - } - - fn rasterize_monochrome( - &self, - components: &DirectWriteComponents, - params: &RenderGlyphParams, - glyph_bounds: Bounds, - ) -> Result> { - let glyph_analysis = self.create_glyph_run_analysis(components, params)?; - if !params.subpixel_rendering { - let mut bitmap_data = - vec![0u8; glyph_bounds.size.width.0 as usize * glyph_bounds.size.height.0 as usize]; - unsafe { - glyph_analysis.CreateAlphaTexture( - DWRITE_TEXTURE_ALIASED_1x1, - &RECT { - left: glyph_bounds.origin.x.0, - top: glyph_bounds.origin.y.0, - right: glyph_bounds.size.width.0 + glyph_bounds.origin.x.0, - bottom: glyph_bounds.size.height.0 + glyph_bounds.origin.y.0, - }, - &mut bitmap_data, - )?; - } - - return Ok(bitmap_data); - } - - let width = glyph_bounds.size.width.0 as usize; - let height = glyph_bounds.size.height.0 as usize; - let pixel_count = width * height; - - let mut bitmap_data = vec![0u8; pixel_count * 4]; - - unsafe { - glyph_analysis.CreateAlphaTexture( - DWRITE_TEXTURE_CLEARTYPE_3x1, - &RECT { - left: glyph_bounds.origin.x.0, - top: glyph_bounds.origin.y.0, - right: glyph_bounds.size.width.0 + glyph_bounds.origin.x.0, - bottom: glyph_bounds.size.height.0 + glyph_bounds.origin.y.0, - }, - &mut bitmap_data[..pixel_count * 3], - )?; - } - - // The output buffer expects RGBA data, so pad the alpha channel with zeros. - for pixel_ix in (0..pixel_count).rev() { - let src = pixel_ix * 3; - let dst = pixel_ix * 4; - ( - bitmap_data[dst], - bitmap_data[dst + 1], - bitmap_data[dst + 2], - bitmap_data[dst + 3], - ) = ( - bitmap_data[src], - bitmap_data[src + 1], - bitmap_data[src + 2], - 0, - ); - } - - Ok(bitmap_data) - } - - fn rasterize_color( - &self, - components: &DirectWriteComponents, - params: &RenderGlyphParams, - glyph_bounds: Bounds, - ) -> Result> { - // INVARIANT: the code below drives the *shared* D3D11 immediate context - // (`Map`/`Unmap`/`Draw`/`CopyResource`), which `DirectXRenderer` and `DirectXAtlas` also - // touch. An immediate `ID3D11DeviceContext` is not thread-safe, so this must only run on - // the main UI thread (which it always is; text rasterization never leaves that thread). - let bitmap_size = glyph_bounds.size; - let subpixel_shift = params - .subpixel_variant - .map(|v| v as f32 / SUBPIXEL_VARIANTS_X as f32); - let baseline_origin_x = subpixel_shift.x / params.scale_factor; - let baseline_origin_y = subpixel_shift.y / params.scale_factor; - - let transform = DWRITE_MATRIX { - m11: params.scale_factor, - m12: 0.0, - m21: 0.0, - m22: params.scale_factor, - dx: 0.0, - dy: 0.0, - }; - - let font = &self.fonts[params.font_id.0]; - let glyph_id = [params.glyph_id.0 as u16]; - let advance = [glyph_bounds.size.width.0 as f32]; - let offset = [DWRITE_GLYPH_OFFSET { - advanceOffset: -glyph_bounds.origin.x.0 as f32 / params.scale_factor, - ascenderOffset: glyph_bounds.origin.y.0 as f32 / params.scale_factor, - }]; - let glyph_run = DWRITE_GLYPH_RUN { - fontFace: ManuallyDrop::new(Some(unsafe { std::ptr::read(&***font.font_face) })), - fontEmSize: params.font_size.as_f32(), - glyphCount: 1, - glyphIndices: glyph_id.as_ptr(), - glyphAdvances: advance.as_ptr(), - glyphOffsets: offset.as_ptr(), - isSideways: BOOL(0), - bidiLevel: 0, - }; - - // todo: support formats other than COLR - let color_enumerator = unsafe { - components.factory.TranslateColorGlyphRun( - Vector2::new(baseline_origin_x, baseline_origin_y), - &glyph_run, - None, - DWRITE_GLYPH_IMAGE_FORMATS_COLR, - DWRITE_MEASURING_MODE_NATURAL, - Some(&transform), - 0, - ) - }?; - - let mut glyph_layers = Vec::new(); - let mut alpha_data = Vec::new(); - loop { - let color_run = unsafe { color_enumerator.GetCurrentRun() }?; - let color_run = unsafe { &*color_run }; - let image_format = color_run.glyphImageFormat & !DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE; - if image_format == DWRITE_GLYPH_IMAGE_FORMATS_COLR { - let color_analysis = unsafe { - components.factory.CreateGlyphRunAnalysis( - &color_run.Base.glyphRun as *const _, - Some(&transform), - DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC, - DWRITE_MEASURING_MODE_NATURAL, - DWRITE_GRID_FIT_MODE_DEFAULT, - DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE, - baseline_origin_x, - baseline_origin_y, - ) - }?; - - let color_bounds = - unsafe { color_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_ALIASED_1x1) }?; - - let color_size = size( - color_bounds.right - color_bounds.left, - color_bounds.bottom - color_bounds.top, - ); - if color_size.width > 0 && color_size.height > 0 { - alpha_data.clear(); - alpha_data.resize((color_size.width * color_size.height) as usize, 0); - unsafe { - color_analysis.CreateAlphaTexture( - DWRITE_TEXTURE_ALIASED_1x1, - &color_bounds, - &mut alpha_data, - ) - }?; - - let run_color = { - let run_color = color_run.Base.runColor; - Rgba { - r: run_color.r, - g: run_color.g, - b: run_color.b, - a: run_color.a, - } - }; - let bounds = bounds(point(color_bounds.left, color_bounds.top), color_size); - glyph_layers.push(GlyphLayerTexture::new( - &self.gpu_state, - run_color, - bounds, - &alpha_data, - )?); - } - } - - let has_next = unsafe { color_enumerator.MoveNext() } - .map(|e| e.as_bool()) - .unwrap_or(false); - if !has_next { - break; - } - } - - let gpu_state = &self.gpu_state; - - let render_target_texture = { - let mut texture = None; - let desc = D3D11_TEXTURE2D_DESC { - Width: bitmap_size.width.0 as u32, - Height: bitmap_size.height.0 as u32, - MipLevels: 1, - ArraySize: 1, - Format: DXGI_FORMAT_B8G8R8A8_UNORM, - SampleDesc: DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - Usage: D3D11_USAGE_DEFAULT, - BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32, - CPUAccessFlags: 0, - MiscFlags: 0, - }; - unsafe { - gpu_state - .device - .CreateTexture2D(&desc, None, Some(&mut texture)) - }?; - texture.unwrap() - }; - - let render_target_view = { - let desc = D3D11_RENDER_TARGET_VIEW_DESC { - Format: DXGI_FORMAT_B8G8R8A8_UNORM, - ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D, - Anonymous: D3D11_RENDER_TARGET_VIEW_DESC_0 { - Texture2D: D3D11_TEX2D_RTV { MipSlice: 0 }, - }, - }; - let mut rtv = None; - unsafe { - gpu_state.device.CreateRenderTargetView( - &render_target_texture, - Some(&desc), - Some(&mut rtv), - ) - }?; - rtv - }; - - Self::composite_color_layers( - gpu_state, - &glyph_layers, - bitmap_size, - &render_target_texture, - &render_target_view, - ) - } - - fn composite_color_layers( - gpu_state: &GPUState, - glyph_layers: &[GlyphLayerTexture], - bitmap_size: Size, - render_target_texture: &ID3D11Texture2D, - render_target_view: &Option, - ) -> Result> { - let params_buffer = { - let desc = D3D11_BUFFER_DESC { - ByteWidth: std::mem::size_of::() as u32, - Usage: D3D11_USAGE_DYNAMIC, - BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32, - CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, - MiscFlags: 0, - StructureByteStride: 0, - }; - - let mut buffer = None; - unsafe { - gpu_state - .device - .CreateBuffer(&desc, None, Some(&mut buffer)) - }?; - buffer - }; - - let staging_texture = { - let mut texture = None; - let desc = D3D11_TEXTURE2D_DESC { - Width: bitmap_size.width.0 as u32, - Height: bitmap_size.height.0 as u32, - MipLevels: 1, - ArraySize: 1, - Format: DXGI_FORMAT_B8G8R8A8_UNORM, - SampleDesc: DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - Usage: D3D11_USAGE_STAGING, - BindFlags: 0, - CPUAccessFlags: D3D11_CPU_ACCESS_READ.0 as u32, - MiscFlags: 0, - }; - unsafe { - gpu_state - .device - .CreateTexture2D(&desc, None, Some(&mut texture)) - }?; - texture.unwrap() - }; - - let device_context = &gpu_state.device_context; - unsafe { device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP) }; - unsafe { device_context.VSSetShader(&gpu_state.vertex_shader, None) }; - unsafe { device_context.PSSetShader(&gpu_state.pixel_shader, None) }; - unsafe { - device_context.VSSetConstantBuffers(0, Some(std::slice::from_ref(¶ms_buffer))) - }; - unsafe { - device_context.PSSetConstantBuffers(0, Some(std::slice::from_ref(¶ms_buffer))) - }; - unsafe { - device_context.OMSetRenderTargets(Some(std::slice::from_ref(render_target_view)), None) - }; - unsafe { - if let Some(render_target_view) = render_target_view.as_ref() { - device_context.ClearRenderTargetView(render_target_view, &[0.0, 0.0, 0.0, 0.0]); - } - } - unsafe { device_context.PSSetSamplers(0, Some(std::slice::from_ref(&gpu_state.sampler))) }; - unsafe { device_context.OMSetBlendState(&gpu_state.blend_state, None, 0xffffffff) }; - - let crate::FontInfo { - gamma_ratios, - grayscale_enhanced_contrast, - .. - } = DirectXRenderer::get_font_info(); - - for layer in glyph_layers { - let params = GlyphLayerTextureParams { - run_color: layer.run_color, - bounds: layer.bounds, - gamma_ratios: *gamma_ratios, - grayscale_enhanced_contrast: *grayscale_enhanced_contrast, - _pad: [0f32; 3], - }; - unsafe { - let mut dest = std::mem::zeroed(); - gpu_state.device_context.Map( - params_buffer.as_ref().unwrap(), - 0, - D3D11_MAP_WRITE_DISCARD, - 0, - Some(&mut dest), - )?; - std::ptr::copy_nonoverlapping(¶ms as *const _, dest.pData as *mut _, 1); - gpu_state - .device_context - .Unmap(params_buffer.as_ref().unwrap(), 0); - }; - - let texture = [Some(layer.texture_view.clone())]; - unsafe { device_context.PSSetShaderResources(0, Some(&texture)) }; - - let viewport = [D3D11_VIEWPORT { - TopLeftX: layer.bounds.origin.x as f32, - TopLeftY: layer.bounds.origin.y as f32, - Width: layer.bounds.size.width as f32, - Height: layer.bounds.size.height as f32, - MinDepth: 0.0, - MaxDepth: 1.0, - }]; - unsafe { device_context.RSSetViewports(Some(&viewport)) }; - - unsafe { device_context.Draw(4, 0) }; - } - - unsafe { device_context.CopyResource(&staging_texture, render_target_texture) }; - - let mapped_data = { - let mut mapped_data = D3D11_MAPPED_SUBRESOURCE::default(); - unsafe { - device_context.Map( - &staging_texture, - 0, - D3D11_MAP_READ, - 0, - Some(&mut mapped_data), - ) - }?; - mapped_data - }; - let mut rasterized = - vec![0u8; (bitmap_size.width.0 as u32 * bitmap_size.height.0 as u32 * 4) as usize]; - - for y in 0..bitmap_size.height.0 as usize { - let width = bitmap_size.width.0 as usize; - unsafe { - std::ptr::copy_nonoverlapping::( - (mapped_data.pData as *const u8).byte_add(mapped_data.RowPitch as usize * y), - rasterized - .as_mut_ptr() - .byte_add(width * y * std::mem::size_of::()), - width * std::mem::size_of::(), - ) - }; - } - - // Release the mapping now that the rows have been copied out; leaving `staging_texture` - // mapped would leak the mapping and keep the resource pinned for later reuse. - unsafe { device_context.Unmap(&staging_texture, 0) }; - - // Convert from premultiplied to straight alpha - for chunk in rasterized.chunks_exact_mut(4) { - let b = chunk[0] as f32; - let g = chunk[1] as f32; - let r = chunk[2] as f32; - let a = chunk[3] as f32; - if a > 0.0 { - let inv_a = 255.0 / a; - chunk[0] = (b * inv_a).clamp(0.0, 255.0) as u8; - chunk[1] = (g * inv_a).clamp(0.0, 255.0) as u8; - chunk[2] = (r * inv_a).clamp(0.0, 255.0) as u8; - } - } - - Ok(rasterized) - } - - fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { - unsafe { - let font = &self.fonts[font_id.0].font_face; - let glyph_indices = [glyph_id.0 as u16]; - let mut metrics = [DWRITE_GLYPH_METRICS::default()]; - font.GetDesignGlyphMetrics(glyph_indices.as_ptr(), 1, metrics.as_mut_ptr(), false)?; - - let metrics = &metrics[0]; - let advance_width = metrics.advanceWidth as i32; - let advance_height = metrics.advanceHeight as i32; - let left_side_bearing = metrics.leftSideBearing; - let right_side_bearing = metrics.rightSideBearing; - let top_side_bearing = metrics.topSideBearing; - let bottom_side_bearing = metrics.bottomSideBearing; - let vertical_origin_y = metrics.verticalOriginY; - - let y_offset = vertical_origin_y + bottom_side_bearing - advance_height; - let width = advance_width - (left_side_bearing + right_side_bearing); - let height = advance_height - (top_side_bearing + bottom_side_bearing); - - Ok(Bounds { - origin: Point { - x: left_side_bearing as f32, - y: y_offset as f32, - }, - size: Size { - width: width as f32, - height: height as f32, - }, - }) - } - } - - fn get_advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { - unsafe { - let font = &self.fonts[font_id.0].font_face; - let glyph_indices = [glyph_id.0 as u16]; - let mut metrics = [DWRITE_GLYPH_METRICS::default()]; - font.GetDesignGlyphMetrics(glyph_indices.as_ptr(), 1, metrics.as_mut_ptr(), false)?; - - let metrics = &metrics[0]; - - Ok(Size { - width: metrics.advanceWidth as f32, - height: 0.0, - }) - } - } - - fn all_font_names(&self, components: &DirectWriteComponents) -> Vec { - let mut result = - get_font_names_from_collection(&self.system_font_collection, &components.locale); - result.extend(get_font_names_from_collection( - &self.custom_font_collection, - &components.locale, - )); - result - } - - fn handle_gpu_lost(&mut self, directx_devices: &DirectXDevices) -> Result<()> { - try_to_recover_from_device_lost(|| { - GPUState::new(directx_devices).context("Recreating GPU state for DirectWrite") - }) - .map(|gpu_state| self.gpu_state = gpu_state) - } -} - -struct GlyphLayerTexture { - run_color: Rgba, - bounds: Bounds, - texture_view: ID3D11ShaderResourceView, - // holding on to the texture to not RAII drop it - _texture: ID3D11Texture2D, -} - -impl GlyphLayerTexture { - fn new( - gpu_state: &GPUState, - run_color: Rgba, - bounds: Bounds, - alpha_data: &[u8], - ) -> Result { - let texture_size = bounds.size; - - let desc = D3D11_TEXTURE2D_DESC { - Width: texture_size.width as u32, - Height: texture_size.height as u32, - MipLevels: 1, - ArraySize: 1, - Format: DXGI_FORMAT_R8_UNORM, - SampleDesc: DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - Usage: D3D11_USAGE_DEFAULT, - BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32, - CPUAccessFlags: 0, - MiscFlags: 0, - }; - - let texture = { - let mut texture: Option = None; - unsafe { - gpu_state - .device - .CreateTexture2D(&desc, None, Some(&mut texture))? - }; - texture.unwrap() - }; - let texture_view = { - let mut view: Option = None; - unsafe { - gpu_state - .device - .CreateShaderResourceView(&texture, None, Some(&mut view))? - }; - view.unwrap() - }; - - unsafe { - gpu_state.device_context.UpdateSubresource( - &texture, - 0, - None, - alpha_data.as_ptr() as _, - texture_size.width as u32, - 0, - ) - }; - - Ok(GlyphLayerTexture { - run_color, - bounds, - texture_view, - _texture: texture, - }) - } -} - -#[repr(C)] -struct GlyphLayerTextureParams { - bounds: Bounds, - run_color: Rgba, - gamma_ratios: [f32; 4], - grayscale_enhanced_contrast: f32, - _pad: [f32; 3], -} - -struct TextRendererWrapper(IDWriteTextRenderer); - -impl TextRendererWrapper { - fn new(locale_str: HSTRING) -> Self { - let inner = TextRenderer::new(locale_str); - TextRendererWrapper(inner.into()) - } -} - -#[implement(IDWriteTextRenderer)] -struct TextRenderer { - locale: HSTRING, -} - -impl TextRenderer { - fn new(locale: HSTRING) -> Self { - TextRenderer { locale } - } -} - -struct RendererContext<'t, 'a, 'b> { - text_system: &'t mut DirectWriteState, - components: &'a DirectWriteComponents, - index_converter: StringIndexConverter<'a>, - runs: &'b mut Vec, - width: f32, -} - -#[derive(Debug)] -struct ClusterAnalyzer<'t> { - utf16_idx: usize, - glyph_idx: usize, - glyph_count: usize, - cluster_map: &'t [u16], -} - -impl<'t> ClusterAnalyzer<'t> { - fn new(cluster_map: &'t [u16], glyph_count: usize) -> Self { - ClusterAnalyzer { - utf16_idx: 0, - glyph_idx: 0, - glyph_count, - cluster_map, - } - } -} - -impl Iterator for ClusterAnalyzer<'_> { - type Item = (usize, usize); - - fn next(&mut self) -> Option<(usize, usize)> { - if self.utf16_idx >= self.cluster_map.len() { - return None; // No more clusters - } - let start_utf16_idx = self.utf16_idx; - let current_glyph = self.cluster_map[start_utf16_idx] as usize; - - // Find the end of current cluster (where glyph index changes) - let mut end_utf16_idx = start_utf16_idx + 1; - while end_utf16_idx < self.cluster_map.len() - && self.cluster_map[end_utf16_idx] as usize == current_glyph - { - end_utf16_idx += 1; - } - - let utf16_len = end_utf16_idx - start_utf16_idx; - - // Calculate glyph count for this cluster - let next_glyph = if end_utf16_idx < self.cluster_map.len() { - self.cluster_map[end_utf16_idx] as usize - } else { - self.glyph_count - }; - - let glyph_count = next_glyph - current_glyph; - - // Update state for next call - self.utf16_idx = end_utf16_idx; - self.glyph_idx = next_glyph; - - Some((utf16_len, glyph_count)) - } -} - -#[allow(non_snake_case)] -impl IDWritePixelSnapping_Impl for TextRenderer_Impl { - fn IsPixelSnappingDisabled( - &self, - _clientdrawingcontext: *const ::core::ffi::c_void, - ) -> windows::core::Result { - Ok(BOOL(0)) - } - - fn GetCurrentTransform( - &self, - _clientdrawingcontext: *const ::core::ffi::c_void, - transform: *mut DWRITE_MATRIX, - ) -> windows::core::Result<()> { - unsafe { - *transform = DWRITE_MATRIX { - m11: 1.0, - m12: 0.0, - m21: 0.0, - m22: 1.0, - dx: 0.0, - dy: 0.0, - }; - } - Ok(()) - } - - fn GetPixelsPerDip( - &self, - _clientdrawingcontext: *const ::core::ffi::c_void, - ) -> windows::core::Result { - Ok(1.0) - } -} - -#[allow(non_snake_case)] -impl IDWriteTextRenderer_Impl for TextRenderer_Impl { - fn DrawGlyphRun( - &self, - clientdrawingcontext: *const ::core::ffi::c_void, - _baselineoriginx: f32, - _baselineoriginy: f32, - _measuringmode: DWRITE_MEASURING_MODE, - glyphrun: *const DWRITE_GLYPH_RUN, - glyphrundescription: *const DWRITE_GLYPH_RUN_DESCRIPTION, - _clientdrawingeffect: windows::core::Ref, - ) -> windows::core::Result<()> { - let glyphrun = unsafe { &*glyphrun }; - let glyph_count = glyphrun.glyphCount as usize; - if glyph_count == 0 { - return Ok(()); - } - let desc = unsafe { &*glyphrundescription }; - let context = unsafe { &mut *(clientdrawingcontext.cast::().cast_mut()) }; - let Some(font_face) = glyphrun.fontFace.as_ref() else { - return Ok(()); - }; - // This `cast()` action here should never fail since we are running on Win10+, and - // `IDWriteFontFace3` requires Win10 - let Ok(font_face) = &font_face.cast::() else { - return Err(Error::new( - DWRITE_E_UNSUPPORTEDOPERATION, - "Failed to cast font face", - )); - }; - - let font_face_key = font_face.cast::().unwrap().as_raw().addr(); - let font_id = context - .text_system - .font_info_cache - .get(&font_face_key) - .copied() - // in some circumstances, we might be getting served a FontFace that we did not create ourselves - // so create a new font from it and cache it accordingly. The usual culprit here seems to be Segoe UI Symbol - .map_or_else( - || { - let font = font_face_to_font(font_face, &self.locale) - .ok_or_else(|| Error::new(DWRITE_E_NOFONT, "Failed to create font"))?; - let font_id = match context.text_system.font_to_font_id.get(&font) { - Some(&font_id) => font_id, - None => context - .text_system - .select_and_cache_font(context.components, &font) - .ok_or_else(|| Error::new(DWRITE_E_NOFONT, "Failed to create font"))?, - }; - context - .text_system - .font_info_cache - .insert(font_face_key, font_id); - windows::core::Result::Ok(font_id) - }, - Ok, - )?; - - let color_font = unsafe { font_face.IsColorFont().as_bool() }; - - let glyph_ids = unsafe { - slice_from_nullable( - glyphrun.glyphIndices, - glyph_count, - "DirectWrite returned a null glyph indices array", - )? - }; - let glyph_advances = unsafe { - slice_from_nullable( - glyphrun.glyphAdvances, - glyph_count, - "DirectWrite returned a null glyph advances array", - )? - }; - let glyph_offsets = unsafe { - slice_from_nullable( - glyphrun.glyphOffsets, - glyph_count, - "DirectWrite returned a null glyph offsets array", - )? - }; - let cluster_map = unsafe { - slice_from_nullable( - desc.clusterMap, - desc.stringLength as usize, - "DirectWrite returned a null cluster map", - )? - }; - - let cluster_analyzer = ClusterAnalyzer::new(cluster_map, glyph_count); - let mut utf16_idx = desc.textPosition as usize; - let mut glyph_idx = 0; - let mut glyphs = Vec::with_capacity(glyph_count); - for (cluster_utf16_len, cluster_glyph_count) in cluster_analyzer { - context.index_converter.advance_to_utf16_ix(utf16_idx); - utf16_idx += cluster_utf16_len; - for (cluster_glyph_idx, glyph_id) in glyph_ids - [glyph_idx..(glyph_idx + cluster_glyph_count)] - .iter() - .enumerate() - { - let id = GlyphId(*glyph_id as u32); - let is_emoji = - color_font && is_color_glyph(font_face, id, &context.components.factory); - let this_glyph_idx = glyph_idx + cluster_glyph_idx; - glyphs.push(ShapedGlyph { - id, - position: point( - px(context.width + glyph_offsets[this_glyph_idx].advanceOffset), - px(-glyph_offsets[this_glyph_idx].ascenderOffset), - ), - index: context.index_converter.utf8_ix, - is_emoji, - }); - context.width += glyph_advances[this_glyph_idx]; - } - glyph_idx += cluster_glyph_count; - } - context.runs.push(ShapedRun { font_id, glyphs }); - Ok(()) - } - - fn DrawUnderline( - &self, - _clientdrawingcontext: *const ::core::ffi::c_void, - _baselineoriginx: f32, - _baselineoriginy: f32, - _underline: *const DWRITE_UNDERLINE, - _clientdrawingeffect: windows::core::Ref, - ) -> windows::core::Result<()> { - Err(windows::core::Error::new( - E_NOTIMPL, - "DrawUnderline unimplemented", - )) - } - - fn DrawStrikethrough( - &self, - _clientdrawingcontext: *const ::core::ffi::c_void, - _baselineoriginx: f32, - _baselineoriginy: f32, - _strikethrough: *const DWRITE_STRIKETHROUGH, - _clientdrawingeffect: windows::core::Ref, - ) -> windows::core::Result<()> { - Err(windows::core::Error::new( - E_NOTIMPL, - "DrawStrikethrough unimplemented", - )) - } - - fn DrawInlineObject( - &self, - _clientdrawingcontext: *const ::core::ffi::c_void, - _originx: f32, - _originy: f32, - _inlineobject: windows::core::Ref, - _issideways: BOOL, - _isrighttoleft: BOOL, - _clientdrawingeffect: windows::core::Ref, - ) -> windows::core::Result<()> { - Err(windows::core::Error::new( - E_NOTIMPL, - "DrawInlineObject unimplemented", - )) - } -} - -/// Interprets an optional DirectWrite array pointer as a slice, treating a -/// null pointer with a zero length as an empty slice. A null pointer with a -/// nonzero length fails with `null_error_message`. -/// -/// # Safety -/// -/// When `ptr` is non-null, the caller must guarantee that it points to a valid -/// array of at least `len` elements that outlives the returned slice. -unsafe fn slice_from_nullable<'a, T>( - ptr: *const T, - len: usize, - null_error_message: &str, -) -> windows::core::Result<&'a [T]> { - if ptr.is_null() { - if len != 0 { - return Err(Error::new(E_INVALIDARG, null_error_message)); - } - Ok(&[]) - } else { - Ok(unsafe { std::slice::from_raw_parts(ptr, len) }) - } -} - -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, - } - } - - #[allow(dead_code)] - 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(); - } -} - -fn font_style_to_dwrite(style: FontStyle) -> DWRITE_FONT_STYLE { - match style { - FontStyle::Normal => DWRITE_FONT_STYLE_NORMAL, - FontStyle::Italic => DWRITE_FONT_STYLE_ITALIC, - FontStyle::Oblique => DWRITE_FONT_STYLE_OBLIQUE, - } -} - -fn font_style_from_dwrite(value: DWRITE_FONT_STYLE) -> FontStyle { - match value.0 { - 0 => FontStyle::Normal, - 1 => FontStyle::Italic, - 2 => FontStyle::Oblique, - _ => unreachable!(), - } -} - -fn font_weight_to_dwrite(weight: FontWeight) -> DWRITE_FONT_WEIGHT { - DWRITE_FONT_WEIGHT(weight.0 as i32) -} - -fn font_weight_from_dwrite(value: DWRITE_FONT_WEIGHT) -> FontWeight { - FontWeight(value.0 as f32) -} - -fn get_font_names_from_collection( - collection: &IDWriteFontCollection1, - locale: &HSTRING, -) -> Vec { - unsafe { - let mut result = Vec::new(); - let family_count = collection.GetFontFamilyCount(); - for index in 0..family_count { - let Some(font_family) = collection.GetFontFamily(index).log_err() else { - continue; - }; - let Some(localized_family_name) = font_family.GetFamilyNames().log_err() else { - continue; - }; - let Some(family_name) = get_name(localized_family_name, locale).log_err() else { - continue; - }; - result.push(family_name); - } - - result - } -} - -fn font_face_to_font(font_face: &IDWriteFontFace3, locale: &HSTRING) -> Option { - let localized_family_name = unsafe { font_face.GetFamilyNames().log_err() }?; - let family_name = get_name(localized_family_name, locale).log_err()?; - let weight = unsafe { font_face.GetWeight() }; - let style = unsafe { font_face.GetStyle() }; - Some(Font { - family: family_name.into(), - features: FontFeatures::default(), - weight: font_weight_from_dwrite(weight), - style: font_style_from_dwrite(style), - fallbacks: None, - }) -} - -// https://learn.microsoft.com/en-us/windows/win32/api/dwrite/ne-dwrite-dwrite_font_feature_tag -fn apply_font_features( - direct_write_features: &IDWriteTypography, - features: &FontFeatures, -) -> Result<()> { - let tag_values = features.tag_value_list(); - if tag_values.is_empty() { - return Ok(()); - } - - // All of these features are enabled by default by DirectWrite. - // If you want to (and can) peek into the source of DirectWrite - let mut feature_liga = make_direct_write_feature("liga", 1); - let mut feature_clig = make_direct_write_feature("clig", 1); - let mut feature_calt = make_direct_write_feature("calt", 1); - - for (tag, value) in tag_values { - if tag.as_str() == "liga" && *value == 0 { - feature_liga.parameter = 0; - continue; - } - if tag.as_str() == "clig" && *value == 0 { - feature_clig.parameter = 0; - continue; - } - if tag.as_str() == "calt" && *value == 0 { - feature_calt.parameter = 0; - continue; - } - - unsafe { - direct_write_features.AddFontFeature(make_direct_write_feature(tag, *value))?; - } - } - unsafe { - direct_write_features.AddFontFeature(feature_liga)?; - direct_write_features.AddFontFeature(feature_clig)?; - direct_write_features.AddFontFeature(feature_calt)?; - } - - Ok(()) -} - -#[inline] -const fn make_direct_write_feature(feature_name: &str, parameter: u32) -> DWRITE_FONT_FEATURE { - let tag = make_direct_write_tag(feature_name); - DWRITE_FONT_FEATURE { - nameTag: tag, - parameter, - } -} - -#[inline] -const fn make_open_type_tag(tag_name: &str) -> u32 { - let bytes = tag_name.as_bytes(); - debug_assert!(bytes.len() == 4); - u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) -} - -#[inline] -const fn make_direct_write_tag(tag_name: &str) -> DWRITE_FONT_FEATURE_TAG { - DWRITE_FONT_FEATURE_TAG(make_open_type_tag(tag_name)) -} - -#[inline] -fn get_name(string: IDWriteLocalizedStrings, locale: &HSTRING) -> Result { - let mut locale_name_index = 0u32; - let mut exists = BOOL(0); - unsafe { string.FindLocaleName(locale, &mut locale_name_index, &mut exists as _)? }; - if !exists.as_bool() { - unsafe { - string.FindLocaleName( - DEFAULT_LOCALE_NAME, - &mut locale_name_index as _, - &mut exists as _, - )? - }; - anyhow::ensure!(exists.as_bool(), "No localised string for {locale}"); - } - - let name_length = unsafe { string.GetStringLength(locale_name_index) }? as usize; - let mut name_vec = vec![0u16; name_length + 1]; - unsafe { - string.GetString(locale_name_index, &mut name_vec)?; - } - - Ok(String::from_utf16_lossy(&name_vec[..name_length])) -} - -fn get_system_subpixel_rendering() -> bool { - let mut value = c_uint::default(); - let result = unsafe { - SystemParametersInfoW( - SPI_GETFONTSMOOTHINGTYPE, - 0, - Some((&mut value) as *mut c_uint as *mut c_void), - SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS::default(), - ) - }; - if result.log_err().is_some() { - value == FE_FONTSMOOTHINGCLEARTYPE - } else { - true - } -} - -fn get_system_ui_font_name() -> SharedString { - unsafe { - let mut info: LOGFONTW = std::mem::zeroed(); - let font_family = if SystemParametersInfoW( - SPI_GETICONTITLELOGFONT, - std::mem::size_of::() as u32, - Some(&mut info as *mut _ as _), - SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS(0), - ) - .log_err() - .is_none() - { - // https://learn.microsoft.com/en-us/windows/win32/uxguide/vis-fonts - // Segoe UI is the Windows font intended for user interface text strings. - "Segoe UI".into() - } else { - let font_name = String::from_utf16_lossy(&info.lfFaceName); - font_name.trim_matches(char::from(0)).to_owned().into() - }; - log::info!("Use {} as UI font.", font_family); - font_family - } -} - -// One would think that with newer DirectWrite method: IDWriteFontFace4::GetGlyphImageFormats -// but that doesn't seem to work for some glyphs, say ❤ -fn is_color_glyph( - font_face: &IDWriteFontFace3, - glyph_id: GlyphId, - factory: &IDWriteFactory5, -) -> bool { - let glyph_run = DWRITE_GLYPH_RUN { - fontFace: ManuallyDrop::new(Some(unsafe { std::ptr::read(&****font_face) })), - fontEmSize: 14.0, - glyphCount: 1, - glyphIndices: &(glyph_id.0 as u16), - glyphAdvances: &0.0, - glyphOffsets: &DWRITE_GLYPH_OFFSET { - advanceOffset: 0.0, - ascenderOffset: 0.0, - }, - isSideways: BOOL(0), - bidiLevel: 0, - }; - unsafe { - factory.TranslateColorGlyphRun( - Vector2::default(), - &glyph_run as _, - None, - DWRITE_GLYPH_IMAGE_FORMATS_COLR - | DWRITE_GLYPH_IMAGE_FORMATS_SVG - | DWRITE_GLYPH_IMAGE_FORMATS_PNG - | DWRITE_GLYPH_IMAGE_FORMATS_JPEG - | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8, - DWRITE_MEASURING_MODE_NATURAL, - None, - 0, - ) - } - .is_ok() -} - -const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US"); - -#[cfg(test)] -mod tests { - use super::{DirectWriteState, DirectWriteTextSystem, GPUState, GlyphLayerTexture}; - use crate::direct_write::ClusterAnalyzer; - use crate::directx_devices::DirectXDevices; - use anyhow::Result; - use gpui::{ - DevicePixels, Font, PlatformTextSystem, RenderGlyphParams, Rgba, bounds, point, px, size, - }; - use std::ffi::c_void; - use windows::Win32::Graphics::Direct3D11::{ - D3D11_BIND_RENDER_TARGET, D3D11_RENDER_TARGET_VIEW_DESC, D3D11_RENDER_TARGET_VIEW_DESC_0, - D3D11_RTV_DIMENSION_TEXTURE2D, D3D11_SUBRESOURCE_DATA, D3D11_TEX2D_RTV, - D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, - }; - use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC}; - - #[test] - fn test_cluster_map() { - let cluster_map = [0]; - let mut analyzer = ClusterAnalyzer::new(&cluster_map, 1); - let next = analyzer.next(); - assert_eq!(next, Some((1, 1))); - let next = analyzer.next(); - assert_eq!(next, None); - - let cluster_map = [0, 1, 2]; - let mut analyzer = ClusterAnalyzer::new(&cluster_map, 3); - let next = analyzer.next(); - assert_eq!(next, Some((1, 1))); - let next = analyzer.next(); - assert_eq!(next, Some((1, 1))); - let next = analyzer.next(); - assert_eq!(next, Some((1, 1))); - let next = analyzer.next(); - assert_eq!(next, None); - // 👨‍👩‍👧‍👦👩‍💻 - let cluster_map = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4]; - let mut analyzer = ClusterAnalyzer::new(&cluster_map, 5); - let next = analyzer.next(); - assert_eq!(next, Some((11, 4))); - let next = analyzer.next(); - assert_eq!(next, Some((5, 1))); - let next = analyzer.next(); - assert_eq!(next, None); - // 👩‍💻 - let cluster_map = [0, 0, 0, 0, 0]; - let mut analyzer = ClusterAnalyzer::new(&cluster_map, 1); - let next = analyzer.next(); - assert_eq!(next, Some((5, 1))); - let next = analyzer.next(); - assert_eq!(next, None); - } - - #[test] - fn color_emoji_rasterization_is_stable_across_batches() -> Result<()> { - let devices = DirectXDevices::new()?; - let text_system = DirectWriteTextSystem::new(&devices)?; - - let font = Font { - family: "Segoe UI Emoji".into(), - ..Default::default() - }; - let font_id = text_system.font_id(&font)?; - - let mut params_list = Vec::new(); - for ch in ['🫠', '🥹', '🧗', '🏋', '🚀', '🥺'] { - let Some(glyph_id) = text_system.glyph_for_char(font_id, ch) else { - log::info!("no glyph found for {ch}"); - continue; - }; - let params = RenderGlyphParams { - font_id, - glyph_id, - font_size: px(48.0), - subpixel_variant: point(0u8, 0u8), - scale_factor: 1.0, - is_emoji: true, - subpixel_rendering: false, - dilation: 0, - }; - let raster_bounds = text_system.glyph_raster_bounds(¶ms)?; - if raster_bounds.size.width.0 == 0 || raster_bounds.size.height.0 == 0 { - log::info!("raster bounds are empty for {ch}"); - continue; - } - params_list.push((params, raster_bounds)); - } - assert!(!params_list.is_empty()); - - let first: Vec<_> = params_list - .iter() - .map(|(params, bounds)| text_system.rasterize_glyph(params, *bounds)) - .collect::>()?; - - // Churn the texture heap with further rasterization passes. If the color - // compositing leaks leftover texture data (the render target is not cleared), - // the second batch can pick up different contents and differ from the first. - // With an explicit clear both batches are deterministic and identical. - for _ in 0..3 { - for (params, bounds) in ¶ms_list { - text_system.rasterize_glyph(params, *bounds)?; - } - } - let second: Vec<_> = params_list - .iter() - .map(|(params, bounds)| text_system.rasterize_glyph(params, *bounds)) - .collect::>()?; - - assert_eq!( - first, second, - "color glyph rasterization changed between batches; \ - render target contents are leaking into the glyph bitmaps" - ); - Ok(()) - } - - #[test] - fn color_emoji_composites_over_cleared_texture() -> Result<()> { - let devices = DirectXDevices::new()?; - let gpu_state = GPUState::new(&devices)?; - - const SIZE: u32 = 32; - // Seed the render target with solid red so that any texel which the - // compositing pass fails to clear/overwrite remains identifiable after - // the readback. - let poison = { - let mut v = vec![0u8; (SIZE * SIZE * 4) as usize]; - for pixel in v.chunks_exact_mut(4) { - pixel[2] = 255; - pixel[3] = 255; - } - v - }; - let desc = D3D11_TEXTURE2D_DESC { - Width: SIZE, - Height: SIZE, - MipLevels: 1, - ArraySize: 1, - Format: DXGI_FORMAT_B8G8R8A8_UNORM, - SampleDesc: DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - Usage: D3D11_USAGE_DEFAULT, - BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32, - CPUAccessFlags: 0, - MiscFlags: 0, - }; - let initial_data = D3D11_SUBRESOURCE_DATA { - pSysMem: poison.as_ptr() as *const c_void, - SysMemPitch: SIZE * 4, - SysMemSlicePitch: 0, - }; - let texture = unsafe { - let mut texture = None; - gpu_state - .device - .CreateTexture2D(&desc, Some(&initial_data), Some(&mut texture))?; - texture.unwrap() - }; - let render_target_view = unsafe { - let desc = D3D11_RENDER_TARGET_VIEW_DESC { - Format: DXGI_FORMAT_B8G8R8A8_UNORM, - ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D, - Anonymous: D3D11_RENDER_TARGET_VIEW_DESC_0 { - Texture2D: D3D11_TEX2D_RTV { MipSlice: 0 }, - }, - }; - let mut rtv = None; - gpu_state - .device - .CreateRenderTargetView(&texture, Some(&desc), Some(&mut rtv))?; - rtv.unwrap() - }; - - // A single opaque layer in the top-left corner; the bottom-right corner - // of the texture is covered by no layer at all. - let layer_alpha = vec![255u8; 4 * 4]; - let layer = GlyphLayerTexture::new( - &gpu_state, - Rgba { - r: 1.0, - g: 1.0, - b: 1.0, - a: 1.0, - }, - bounds(point(0, 0), size(4, 4)), - &layer_alpha, - )?; - - let rasterized = DirectWriteState::composite_color_layers( - &gpu_state, - std::slice::from_ref(&layer), - size(DevicePixels(SIZE as i32), DevicePixels(SIZE as i32)), - &texture, - &Some(render_target_view), - )?; - - let corner = (SIZE as usize - 1 + (SIZE as usize - 1) * SIZE as usize) * 4; - assert_eq!( - &rasterized[corner..corner + 4], - &[0, 0, 0, 0], - "uncovered texel retained the poison from the uninitialized render target" - ); - Ok(()) - } -} diff --git a/crates/gpui_pre_windows/src/directx_atlas.rs b/crates/gpui_pre_windows/src/directx_atlas.rs deleted file mode 100644 index 62386997..00000000 --- a/crates/gpui_pre_windows/src/directx_atlas.rs +++ /dev/null @@ -1,420 +0,0 @@ -use collections::FxHashMap; -use etagere::BucketedAtlasAllocator; -use parking_lot::Mutex; -use windows::Win32::Graphics::{ - Direct3D11::{ - D3D11_BIND_SHADER_RESOURCE, D3D11_BOX, D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, - ID3D11Device, ID3D11DeviceContext, ID3D11ShaderResourceView, ID3D11Texture2D, - }, - Dxgi::Common::*, -}; - -use gpui::{ - AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTextureList, AtlasTile, Bounds, DevicePixels, - PlatformAtlas, Point, Size, -}; - -pub(crate) struct DirectXAtlas(Mutex); - -struct DirectXAtlasState { - device: ID3D11Device, - device_context: ID3D11DeviceContext, - monochrome_textures: AtlasTextureList, - polychrome_textures: AtlasTextureList, - subpixel_textures: AtlasTextureList, - tiles_by_key: FxHashMap, -} - -struct DirectXAtlasTexture { - id: AtlasTextureId, - bytes_per_pixel: u32, - allocator: BucketedAtlasAllocator, - texture: ID3D11Texture2D, - view: [Option; 1], - live_atlas_keys: u32, -} - -impl DirectXAtlas { - pub(crate) fn new(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Self { - DirectXAtlas(Mutex::new(DirectXAtlasState { - device: device.clone(), - device_context: device_context.clone(), - monochrome_textures: Default::default(), - polychrome_textures: Default::default(), - subpixel_textures: Default::default(), - tiles_by_key: Default::default(), - })) - } - - pub(crate) fn get_texture_view( - &self, - id: AtlasTextureId, - ) -> [Option; 1] { - let lock = self.0.lock(); - let tex = lock.texture(id); - tex.view.clone() - } - - pub(crate) fn handle_device_lost( - &self, - device: &ID3D11Device, - device_context: &ID3D11DeviceContext, - ) { - let mut lock = self.0.lock(); - lock.device = device.clone(); - lock.device_context = device_context.clone(); - lock.monochrome_textures = AtlasTextureList::default(); - lock.polychrome_textures = AtlasTextureList::default(); - lock.subpixel_textures = AtlasTextureList::default(); - lock.tiles_by_key.clear(); - } -} - -impl PlatformAtlas for DirectXAtlas { - fn get_or_insert_with<'a>( - &self, - key: &AtlasKey, - build: &mut dyn FnMut() -> anyhow::Result< - Option<(Size, std::borrow::Cow<'a, [u8]>)>, - >, - ) -> anyhow::Result> { - let mut lock = self.0.lock(); - if let Some(tile) = lock.tiles_by_key.get(key) { - Ok(Some(*tile)) - } else { - let Some((size, bytes)) = build()? else { - return Ok(None); - }; - let tile = lock - .allocate(size, key.texture_kind()) - .ok_or_else(|| anyhow::anyhow!("failed to allocate"))?; - let texture = lock.texture(tile.texture_id); - texture.upload(&lock.device_context, tile.bounds, &bytes); - lock.tiles_by_key.insert(key.clone(), tile); - Ok(Some(tile)) - } - } - - fn remove(&self, key: &AtlasKey) { - let mut lock = self.0.lock(); - - let Some(tile) = lock.tiles_by_key.remove(key) else { - return; - }; - let id = tile.texture_id; - - let textures = match id.kind { - AtlasTextureKind::Monochrome => &mut lock.monochrome_textures, - AtlasTextureKind::Polychrome => &mut lock.polychrome_textures, - AtlasTextureKind::Subpixel => &mut lock.subpixel_textures, - }; - - let Some(texture_slot) = textures.textures.get_mut(id.index as usize) else { - return; - }; - - if let Some(mut texture) = texture_slot.take() { - texture.allocator.deallocate(tile.tile_id.into()); - texture.decrement_ref_count(); - if texture.is_unreferenced() { - textures.free_list.push(texture.id.index as usize); - } else { - *texture_slot = Some(texture); - } - } - } -} - -impl DirectXAtlasState { - fn allocate( - &mut self, - size: Size, - texture_kind: AtlasTextureKind, - ) -> Option { - { - let textures = match texture_kind { - AtlasTextureKind::Monochrome => &mut self.monochrome_textures, - AtlasTextureKind::Polychrome => &mut self.polychrome_textures, - AtlasTextureKind::Subpixel => &mut self.subpixel_textures, - }; - - if let Some(tile) = textures - .iter_mut() - .rev() - .find_map(|texture| texture.allocate(size)) - { - return Some(tile); - } - } - - let texture = self.push_texture(size, texture_kind)?; - texture.allocate(size) - } - - fn push_texture( - &mut self, - min_size: Size, - kind: AtlasTextureKind, - ) -> Option<&mut DirectXAtlasTexture> { - const DEFAULT_ATLAS_SIZE: Size = Size { - width: DevicePixels(1024), - height: DevicePixels(1024), - }; - // Max texture size for DirectX. See: - // https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits - const MAX_ATLAS_SIZE: Size = Size { - width: DevicePixels(16384), - height: DevicePixels(16384), - }; - let size = min_size.min(&MAX_ATLAS_SIZE).max(&DEFAULT_ATLAS_SIZE); - let pixel_format; - let bind_flag; - let bytes_per_pixel; - match kind { - AtlasTextureKind::Monochrome => { - pixel_format = DXGI_FORMAT_R8_UNORM; - bind_flag = D3D11_BIND_SHADER_RESOURCE; - bytes_per_pixel = 1; - } - AtlasTextureKind::Polychrome => { - pixel_format = DXGI_FORMAT_B8G8R8A8_UNORM; - bind_flag = D3D11_BIND_SHADER_RESOURCE; - bytes_per_pixel = 4; - } - AtlasTextureKind::Subpixel => { - pixel_format = DXGI_FORMAT_R8G8B8A8_UNORM; - bind_flag = D3D11_BIND_SHADER_RESOURCE; - bytes_per_pixel = 4; - } - } - let texture_desc = D3D11_TEXTURE2D_DESC { - Width: size.width.0 as u32, - Height: size.height.0 as u32, - MipLevels: 1, - ArraySize: 1, - Format: pixel_format, - SampleDesc: DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - Usage: D3D11_USAGE_DEFAULT, - BindFlags: bind_flag.0 as u32, - CPUAccessFlags: 0, - MiscFlags: 0, - }; - let mut texture: Option = None; - unsafe { - // This only returns None if the device is lost, which we will recreate later. - // So it's ok to return None here. - self.device - .CreateTexture2D(&texture_desc, None, Some(&mut texture)) - .ok()?; - } - let texture = texture.unwrap(); - - let texture_list = match kind { - AtlasTextureKind::Monochrome => &mut self.monochrome_textures, - AtlasTextureKind::Polychrome => &mut self.polychrome_textures, - AtlasTextureKind::Subpixel => &mut self.subpixel_textures, - }; - let index = texture_list.free_list.pop(); - let view = unsafe { - let mut view = None; - self.device - .CreateShaderResourceView(&texture, None, Some(&mut view)) - .ok()?; - [view] - }; - let atlas_texture = DirectXAtlasTexture { - id: AtlasTextureId { - index: index.unwrap_or(texture_list.textures.len()) as u32, - kind, - }, - bytes_per_pixel, - allocator: etagere::BucketedAtlasAllocator::new(device_size_to_etagere(size)), - texture, - view, - live_atlas_keys: 0, - }; - if let Some(ix) = index { - texture_list.textures[ix] = Some(atlas_texture); - texture_list.textures.get_mut(ix).unwrap().as_mut() - } else { - texture_list.textures.push(Some(atlas_texture)); - texture_list.textures.last_mut().unwrap().as_mut() - } - } - - fn texture(&self, id: AtlasTextureId) -> &DirectXAtlasTexture { - match id.kind { - AtlasTextureKind::Monochrome => &self.monochrome_textures[id.index as usize] - .as_ref() - .unwrap(), - AtlasTextureKind::Polychrome => &self.polychrome_textures[id.index as usize] - .as_ref() - .unwrap(), - AtlasTextureKind::Subpixel => { - &self.subpixel_textures[id.index as usize].as_ref().unwrap() - } - } - } -} - -impl DirectXAtlasTexture { - fn allocate(&mut self, size: Size) -> Option { - let allocation = self.allocator.allocate(device_size_to_etagere(size))?; - let tile = AtlasTile { - texture_id: self.id, - tile_id: allocation.id.into(), - bounds: Bounds { - origin: etagere_point_to_device(allocation.rectangle.min), - size, - }, - padding: 0, - }; - self.live_atlas_keys += 1; - Some(tile) - } - - fn upload( - &self, - device_context: &ID3D11DeviceContext, - bounds: Bounds, - bytes: &[u8], - ) { - // `UpdateSubresource` reads `row_pitch * height` bytes from `bytes` based on the - // `D3D11_BOX` below. If the caller hands us a slice shorter than that, the driver would - // over-read past the end of the source buffer (potentially by multiple megabytes), so bail - // out instead. This is a first-insert path rather than a per-frame one, so the check is - // effectively free. - let row_bytes = bounds.size.width.to_bytes(self.bytes_per_pixel as u8) as usize; - let expected = row_bytes * bounds.size.height.0.max(0) as usize; - if bytes.len() < expected { - log::error!( - "DirectXAtlasTexture::upload: source slice is {} bytes but the {}x{} region \ - requires {} bytes; skipping upload to avoid a driver over-read", - bytes.len(), - bounds.size.width.0, - bounds.size.height.0, - expected, - ); - return; - } - unsafe { - device_context.UpdateSubresource( - &self.texture, - 0, - Some(&D3D11_BOX { - left: bounds.left().0 as u32, - top: bounds.top().0 as u32, - front: 0, - right: bounds.right().0 as u32, - bottom: bounds.bottom().0 as u32, - back: 1, - }), - bytes.as_ptr() as _, - bounds.size.width.to_bytes(self.bytes_per_pixel as u8), - 0, - ); - } - } - - fn decrement_ref_count(&mut self) { - self.live_atlas_keys -= 1; - } - - fn is_unreferenced(&mut self) -> bool { - self.live_atlas_keys == 0 - } -} - -fn device_size_to_etagere(size: Size) -> etagere::Size { - etagere::Size::new(size.width.into(), size.height.into()) -} - -fn etagere_point_to_device(value: etagere::Point) -> Point { - Point { - x: DevicePixels::from(value.x), - y: DevicePixels::from(value.y), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use gpui::{ImageId, RenderImageParams}; - use std::borrow::Cow; - use windows::Win32::{ - Foundation::HMODULE, - Graphics::{ - Direct3D::D3D_DRIVER_TYPE_WARP, - Direct3D11::{D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_SDK_VERSION, D3D11CreateDevice}, - }, - }; - - fn create_atlas() -> Option { - let mut device: Option = None; - let mut device_context: Option = None; - unsafe { - D3D11CreateDevice( - None, - D3D_DRIVER_TYPE_WARP, - HMODULE::default(), - D3D11_CREATE_DEVICE_BGRA_SUPPORT, - None, - D3D11_SDK_VERSION, - Some(&mut device), - None, - Some(&mut device_context), - ) - } - .ok()?; - Some(DirectXAtlas::new(&device?, &device_context?)) - } - - fn make_image_key(image_id: usize) -> AtlasKey { - AtlasKey::Image(RenderImageParams { - image_id: ImageId(image_id), - frame_index: 0, - }) - } - - fn insert_tile(atlas: &DirectXAtlas, key: &AtlasKey, size: Size) -> AtlasTile { - atlas - .get_or_insert_with(key, &mut || { - let byte_count = (size.width.0 as usize) * (size.height.0 as usize) * 4; - Ok(Some((size, Cow::Owned(vec![0u8; byte_count])))) - }) - .expect("allocation should succeed") - .expect("callback returns Some") - } - - #[test] - fn test_remove_deallocates_tile_space_for_reuse() { - let Some(atlas) = create_atlas() else { - return; - }; - - let small = Size { - width: DevicePixels(64), - height: DevicePixels(64), - }; - let big = Size { - width: DevicePixels(700), - height: DevicePixels(700), - }; - - let keeper_key = make_image_key(1); - let big_key_a = make_image_key(2); - let big_key_b = make_image_key(3); - - let keeper_tile = insert_tile(&atlas, &keeper_key, small); - let tile_a = insert_tile(&atlas, &big_key_a, big); - assert_eq!(keeper_tile.texture_id, tile_a.texture_id); - - atlas.remove(&big_key_a); - - let tile_b = insert_tile(&atlas, &big_key_b, big); - assert_eq!(tile_b.texture_id, keeper_tile.texture_id); - } -} diff --git a/crates/gpui_pre_windows/src/directx_devices.rs b/crates/gpui_pre_windows/src/directx_devices.rs deleted file mode 100644 index 8e65e6e6..00000000 --- a/crates/gpui_pre_windows/src/directx_devices.rs +++ /dev/null @@ -1,194 +0,0 @@ -use anyhow::{Context, Result}; -use gpui_util::ResultExt; -use itertools::Itertools; -use windows::Win32::{ - Foundation::HMODULE, - Graphics::{ - Direct3D::{ - D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL, D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1, - }, - Direct3D11::{ - D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_DEBUG, - D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS, D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS, - D3D11_SDK_VERSION, D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, - }, - Dxgi::{ - CreateDXGIFactory2, DXGI_CREATE_FACTORY_DEBUG, DXGI_CREATE_FACTORY_FLAGS, - IDXGIAdapter1, IDXGIFactory6, - }, - }, -}; -use windows::core::Interface; - -pub(crate) fn try_to_recover_from_device_lost(mut f: impl FnMut() -> Result) -> Result { - (0..5) - .map(|i| { - if i > 0 { - // Add a small delay before retrying - std::thread::sleep(std::time::Duration::from_millis(100 + i * 10)); - } - f() - }) - .find_or_last(Result::is_ok) - .unwrap() - .context("DirectXRenderer failed to recover from lost device after multiple attempts") -} - -#[derive(Clone)] -pub(crate) struct DirectXDevices { - pub(crate) adapter: IDXGIAdapter1, - pub(crate) dxgi_factory: IDXGIFactory6, - pub(crate) device: ID3D11Device, - pub(crate) device_context: ID3D11DeviceContext, -} - -impl DirectXDevices { - pub(crate) fn new() -> Result { - let debug_layer_available = check_debug_layer_available(); - let dxgi_factory = - get_dxgi_factory(debug_layer_available).context("Creating DXGI factory")?; - let (adapter, device, device_context, feature_level) = - get_adapter(&dxgi_factory, debug_layer_available).context("Getting DXGI adapter")?; - match feature_level { - D3D_FEATURE_LEVEL_11_1 => { - log::info!("Created device with Direct3D 11.1 feature level.") - } - D3D_FEATURE_LEVEL_11_0 => { - log::info!("Created device with Direct3D 11.0 feature level.") - } - D3D_FEATURE_LEVEL_10_1 => { - log::info!("Created device with Direct3D 10.1 feature level.") - } - _ => unreachable!(), - } - - Ok(Self { - adapter, - dxgi_factory, - device, - device_context, - }) - } -} - -#[inline] -fn check_debug_layer_available() -> bool { - #[cfg(debug_assertions)] - { - use windows::Win32::Graphics::Dxgi::{DXGIGetDebugInterface1, IDXGIInfoQueue}; - - unsafe { DXGIGetDebugInterface1::(0) } - .log_err() - .is_some() - } - #[cfg(not(debug_assertions))] - { - false - } -} - -#[inline] -fn get_dxgi_factory(debug_layer_available: bool) -> Result { - let factory_flag = if debug_layer_available { - DXGI_CREATE_FACTORY_DEBUG - } else { - #[cfg(debug_assertions)] - log::warn!( - "Failed to get DXGI debug interface. DirectX debugging features will be disabled." - ); - DXGI_CREATE_FACTORY_FLAGS::default() - }; - unsafe { Ok(CreateDXGIFactory2(factory_flag)?) } -} - -#[inline] -fn get_adapter( - dxgi_factory: &IDXGIFactory6, - debug_layer_available: bool, -) -> Result<( - IDXGIAdapter1, - ID3D11Device, - ID3D11DeviceContext, - D3D_FEATURE_LEVEL, -)> { - for adapter_index in 0.. { - let adapter: IDXGIAdapter1 = unsafe { dxgi_factory.EnumAdapters(adapter_index)?.cast()? }; - if let Ok(desc) = unsafe { adapter.GetDesc1() } { - let gpu_name = String::from_utf16_lossy(&desc.Description) - .trim_matches(char::from(0)) - .to_string(); - log::info!("Using GPU: {}", gpu_name); - } - // Check to see whether the adapter supports Direct3D 11 and create - // the device if it does. - let mut context: Option = None; - let mut feature_level = D3D_FEATURE_LEVEL::default(); - if let Some(device) = get_device( - &adapter, - Some(&mut context), - Some(&mut feature_level), - debug_layer_available, - ) - .log_err() - { - return Ok((adapter, device, context.unwrap(), feature_level)); - } - } - - unreachable!() -} - -#[inline] -fn get_device( - adapter: &IDXGIAdapter1, - context: Option<*mut Option>, - feature_level: Option<*mut D3D_FEATURE_LEVEL>, - debug_layer_available: bool, -) -> Result { - let mut device: Option = None; - let device_flags = if debug_layer_available { - D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG - } else { - D3D11_CREATE_DEVICE_BGRA_SUPPORT - }; - unsafe { - D3D11CreateDevice( - adapter, - D3D_DRIVER_TYPE_UNKNOWN, - HMODULE::default(), - device_flags, - // 4x MSAA is required for Direct3D Feature Level 10.1 or better - Some(&[ - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - ]), - D3D11_SDK_VERSION, - Some(&mut device), - feature_level, - context, - )?; - } - let device = device.unwrap(); - let mut data = D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS::default(); - unsafe { - device - .CheckFeatureSupport( - D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS, - &mut data as *mut _ as _, - std::mem::size_of::() as u32, - ) - .context("Checking GPU device feature support")?; - } - if data - .ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x - .as_bool() - { - Ok(device) - } else { - Err(anyhow::anyhow!( - "Required feature StructuredBuffer is not supported by GPU/driver" - )) - } -} diff --git a/crates/gpui_pre_windows/src/directx_renderer.rs b/crates/gpui_pre_windows/src/directx_renderer.rs deleted file mode 100644 index 4221e0aa..00000000 --- a/crates/gpui_pre_windows/src/directx_renderer.rs +++ /dev/null @@ -1,2102 +0,0 @@ -use std::{ - slice, - sync::{Arc, OnceLock}, -}; - -use anyhow::{Context, Result}; -use gpui_util::ResultExt; -use windows::{ - core::{Interface, HSTRING}, - Win32::{ - Foundation::HWND, - Graphics::{ - Direct3D::*, - Direct3D11::*, - DirectComposition::*, - DirectWrite::*, - Dxgi::{Common::*, *}, - }, - }, -}; - -use crate::directx_renderer::shader_resources::{RawShaderBytes, ShaderModule, ShaderTarget}; -use crate::*; -use gpui::*; - -pub(crate) const DISABLE_DIRECT_COMPOSITION: &str = "GPUI_DISABLE_DIRECT_COMPOSITION"; -const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; -// This configuration is used for MSAA rendering on paths only, and it's guaranteed to be supported by DirectX 11. -const PATH_MULTISAMPLE_COUNT: u32 = 4; -const MAX_INSTANCE_BUFFER_SIZE: usize = 256 * 1024 * 1024; - -pub(crate) struct FontInfo { - pub gamma_ratios: [f32; 4], - pub grayscale_enhanced_contrast: f32, - pub subpixel_enhanced_contrast: f32, - pub is_bgr: bool, -} - -pub(crate) struct DirectXRenderer { - hwnd: HWND, - atlas: Arc, - devices: Option, - resources: Option, - globals: DirectXGlobalElements, - pipelines: DirectXRenderPipelines, - direct_composition: Option, - font_info: &'static FontInfo, - - width: u32, - height: u32, - - /// Whether we want to skip drwaing due to device lost events. - /// - /// In that case we want to discard the first frame that we draw as we got reset in the middle of a frame - /// meaning we lost all the allocated gpu textures and scene resources. - skip_draws: bool, -} - -/// Direct3D objects -#[derive(Clone)] -pub(crate) struct DirectXRendererDevices { - pub(crate) adapter: IDXGIAdapter1, - pub(crate) dxgi_factory: IDXGIFactory6, - pub(crate) device: ID3D11Device, - pub(crate) device_context: ID3D11DeviceContext, - dxgi_device: Option, - annotation: Option, -} - -struct DirectXResources { - // Direct3D rendering objects - swap_chain: IDXGISwapChain1, - render_target: Option, - render_target_view: Option, - - // Path intermediate textures (with MSAA) - path_intermediate_texture: ID3D11Texture2D, - path_intermediate_srv: Option, - path_intermediate_msaa_texture: ID3D11Texture2D, - path_intermediate_msaa_view: Option, - - // Cached viewport - viewport: D3D11_VIEWPORT, -} - -struct DirectXRenderPipelines { - shadow_pipeline: PipelineState, - quad_pipeline: PipelineState, - path_rasterization_pipeline: PipelineState, - path_sprite_pipeline: PipelineState, - underline_pipeline: PipelineState, - mono_sprites: PipelineState, - subpixel_sprites: PipelineState, - poly_sprites: PipelineState, -} - -struct DirectXGlobalElements { - clip_buffer: ID3D11Buffer, - clip_view: Option, - clip_capacity: usize, - global_params_buffer: Option, - batch_params_buffer: Option, - sampler: Option, -} - -struct Annotation<'a>(&'a ID3DUserDefinedAnnotation); - -impl<'a> Annotation<'a> { - fn new(annotation: &'a ID3DUserDefinedAnnotation, label: HSTRING) -> Self { - unsafe { annotation.BeginEvent(&label) }; - Self(annotation) - } -} - -impl Drop for Annotation<'_> { - fn drop(&mut self) { - unsafe { self.0.EndEvent() }; - } -} - -struct DirectComposition { - comp_device: IDCompositionDevice, - comp_target: IDCompositionTarget, - comp_visual: IDCompositionVisual, -} - -impl DirectXRendererDevices { - pub(crate) fn new( - directx_devices: &DirectXDevices, - disable_direct_composition: bool, - ) -> Result { - let DirectXDevices { - adapter, - dxgi_factory, - device, - device_context, - } = directx_devices; - let dxgi_device = if disable_direct_composition { - None - } else { - Some(device.cast().context("Creating DXGI device")?) - }; - let annotation = device_context.cast().ok(); - - Ok(Self { - adapter: adapter.clone(), - dxgi_factory: dxgi_factory.clone(), - device: device.clone(), - device_context: device_context.clone(), - dxgi_device, - annotation, - }) - } -} - -impl DirectXRenderer { - pub(crate) fn new( - hwnd: HWND, - directx_devices: &DirectXDevices, - disable_direct_composition: bool, - ) -> Result { - if disable_direct_composition { - log::info!("Direct Composition is disabled."); - } - - let devices = DirectXRendererDevices::new(directx_devices, disable_direct_composition) - .context("Creating DirectX devices")?; - let atlas = Arc::new(DirectXAtlas::new(&devices.device, &devices.device_context)); - - let resources = DirectXResources::new(&devices, 1, 1, hwnd, disable_direct_composition) - .context("Creating DirectX resources")?; - let globals = DirectXGlobalElements::new(&devices.device) - .context("Creating DirectX global elements")?; - let pipelines = DirectXRenderPipelines::new(&devices.device) - .context("Creating DirectX render pipelines")?; - - let direct_composition = if disable_direct_composition { - None - } else { - let composition = DirectComposition::new(devices.dxgi_device.as_ref().unwrap(), hwnd) - .context("Creating DirectComposition")?; - composition - .set_swap_chain(&resources.swap_chain) - .context("Setting swap chain for DirectComposition")?; - Some(composition) - }; - - Ok(DirectXRenderer { - hwnd, - atlas, - devices: Some(devices), - resources: Some(resources), - globals, - pipelines, - direct_composition, - font_info: Self::get_font_info(), - width: 1, - height: 1, - skip_draws: false, - }) - } - - pub(crate) fn sprite_atlas(&self) -> Arc { - self.atlas.clone() - } - - fn pre_draw(&self, clear_color: &[f32; 4]) -> Result<()> { - let resources = self.resources.as_ref().expect("resources missing"); - let device_context = &self - .devices - .as_ref() - .expect("devices missing") - .device_context; - update_buffer( - device_context, - self.globals.global_params_buffer.as_ref().unwrap(), - &[GlobalParams { - gamma_ratios: self.font_info.gamma_ratios, - viewport_size: [resources.viewport.Width, resources.viewport.Height], - grayscale_enhanced_contrast: self.font_info.grayscale_enhanced_contrast, - subpixel_enhanced_contrast: self.font_info.subpixel_enhanced_contrast, - is_bgr: self.font_info.is_bgr as u32, - _pad: [0; 3], - }], - )?; - unsafe { - device_context.ClearRenderTargetView( - resources - .render_target_view - .as_ref() - .context("missing render target view")?, - clear_color, - ); - device_context - .OMSetRenderTargets(Some(slice::from_ref(&resources.render_target_view)), None); - device_context.RSSetViewports(Some(slice::from_ref(&resources.viewport))); - device_context - .VSSetConstantBuffers(0, Some(slice::from_ref(&self.globals.global_params_buffer))); - device_context - .VSSetConstantBuffers(1, Some(slice::from_ref(&self.globals.batch_params_buffer))); - device_context - .PSSetConstantBuffers(0, Some(slice::from_ref(&self.globals.global_params_buffer))); - } - Ok(()) - } - - #[inline] - fn present(&mut self) -> Result<()> { - let result = unsafe { - self.resources - .as_ref() - .expect("resources missing") - .swap_chain - .Present(0, DXGI_PRESENT(0)) - }; - result.ok().context("Presenting swap chain failed") - } - - pub(crate) fn handle_device_lost(&mut self, directx_devices: &DirectXDevices) -> Result<()> { - try_to_recover_from_device_lost(|| { - self.handle_device_lost_impl(directx_devices) - .context("DirectXRenderer handling device lost") - }) - } - - fn handle_device_lost_impl(&mut self, directx_devices: &DirectXDevices) -> Result<()> { - let disable_direct_composition = self.direct_composition.is_none(); - - unsafe { - #[cfg(debug_assertions)] - if let Some(devices) = &self.devices { - report_live_objects(&devices.device) - .context("Failed to report live objects after device lost") - .log_err(); - } - - self.resources.take(); - if let Some(devices) = &self.devices { - devices.device_context.OMSetRenderTargets(None, None); - devices.device_context.ClearState(); - devices.device_context.Flush(); - #[cfg(debug_assertions)] - report_live_objects(&devices.device) - .context("Failed to report live objects after device lost") - .log_err(); - } - - self.direct_composition.take(); - self.devices.take(); - } - - let devices = DirectXRendererDevices::new(directx_devices, disable_direct_composition) - .context("Recreating DirectX devices")?; - let resources = DirectXResources::new( - &devices, - self.width, - self.height, - self.hwnd, - disable_direct_composition, - ) - .context("Creating DirectX resources")?; - let globals = DirectXGlobalElements::new(&devices.device) - .context("Creating DirectXGlobalElements")?; - let pipelines = DirectXRenderPipelines::new(&devices.device) - .context("Creating DirectXRenderPipelines")?; - - let direct_composition = if disable_direct_composition { - None - } else { - let composition = - DirectComposition::new(devices.dxgi_device.as_ref().unwrap(), self.hwnd)?; - composition.set_swap_chain(&resources.swap_chain)?; - Some(composition) - }; - - self.atlas - .handle_device_lost(&devices.device, &devices.device_context); - - unsafe { - devices - .device_context - .OMSetRenderTargets(Some(slice::from_ref(&resources.render_target_view)), None); - } - self.devices = Some(devices); - self.resources = Some(resources); - self.globals = globals; - self.pipelines = pipelines; - self.direct_composition = direct_composition; - self.skip_draws = true; - Ok(()) - } - - pub(crate) fn draw( - &mut self, - scene: &Scene, - background_appearance: WindowBackgroundAppearance, - ) -> Result<()> { - if self.skip_draws { - // skip drawing this frame, we just recovered from a device lost event - // and so likely do not have the textures anymore that are required for drawing - return Ok(()); - } - self.render(scene, background_appearance)?; - self.present() - } - - /// Clear the render target for `background_appearance` and encode every - /// primitive batch of `scene` into it, without presenting. Shared by - /// [`draw`](Self::draw) (which then presents) and - /// [`render_to_image`](Self::render_to_image) (which reads the target back - /// instead), so the two cannot drift. - fn render( - &mut self, - scene: &Scene, - background_appearance: WindowBackgroundAppearance, - ) -> Result<()> { - self.pre_draw(&match background_appearance { - WindowBackgroundAppearance::Opaque => [1.0f32; 4], - _ => [0.0f32; 4], - })?; - self.upload_scene_buffers(scene)?; - - let annotation = self - .devices - .as_ref() - .and_then(|devices| devices.annotation.clone()) - .filter(|annotation| unsafe { annotation.GetStatus().as_bool() }); - for batch in scene.batches() { - let _annotation = annotation - .as_ref() - .map(|annotation| Annotation::new(annotation, HSTRING::from(batch.label()))); - match batch { - PrimitiveBatch::Shadows(range) => self.draw_shadows(range.start, range.len()), - PrimitiveBatch::Quads(range) => self.draw_quads(range.start, range.len()), - PrimitiveBatch::Paths(range) => { - let paths = &scene.paths[range]; - self.draw_paths_to_intermediate(paths)?; - self.draw_paths_from_intermediate(paths) - } - PrimitiveBatch::Underlines(range) => self.draw_underlines(range.start, range.len()), - PrimitiveBatch::MonochromeSprites { texture_id, range } => { - self.draw_monochrome_sprites(texture_id, range.start, range.len()) - } - PrimitiveBatch::SubpixelSprites { texture_id, range } => { - self.draw_subpixel_sprites(texture_id, range.start, range.len()) - } - PrimitiveBatch::PolychromeSprites { texture_id, range } => { - self.draw_polychrome_sprites(texture_id, range.start, range.len()) - } - PrimitiveBatch::Surfaces(range) => self.draw_surfaces(&scene.surfaces[range]), - } - .with_context(|| { - format!( - "scene too large:\ - {} paths, {} shadows, {} quads, {} underlines, {} mono, {} subpixel, {} poly, {} surfaces", - scene.paths.len(), - scene.shadows.len(), - scene.quads.len(), - scene.underlines.len(), - scene.monochrome_sprites.len(), - scene.subpixel_sprites.len(), - scene.polychrome_sprites.len(), - scene.surfaces.len(), - ) - })?; - } - Ok(()) - } - - /// Render `scene` to an offscreen CPU image **without presenting** so - /// the window need never be shown or visible (the macOS headless path - /// goes through MetalRenderer; this is the Windows analogue). Draws into - /// the existing render target, copies it into a `D3D11_USAGE_STAGING` - /// texture, maps it, and converts BGRA to RGBA. - #[cfg(any(test, feature = "test-support"))] - pub(crate) fn render_to_image( - &mut self, - scene: &Scene, - background_appearance: WindowBackgroundAppearance, - ) -> Result { - // A pending device-lost recovery (`skip_draws`) leaves the atlas holding - // tile references from the previous device; drawing before the forced - // re-render rebuilds them panics in `DirectXAtlasState::texture`. - anyhow::ensure!( - !self.skip_draws, - "render_to_image unavailable while recovering from a lost device" - ); - self.render(scene, background_appearance)?; - - let devices = self.devices.as_ref().context("devices missing")?; - let device = &devices.device; - let context = &devices.device_context; - let resources = self.resources.as_ref().context("resources missing")?; - let render_target = resources - .render_target - .as_ref() - .context("render target missing")?; - - // A CPU-readable copy of the render target. - let mut desc = D3D11_TEXTURE2D_DESC::default(); - unsafe { render_target.GetDesc(&mut desc) }; - let width = desc.Width; - let height = desc.Height; - let staging_desc = D3D11_TEXTURE2D_DESC { - Usage: D3D11_USAGE_STAGING, - BindFlags: 0, - CPUAccessFlags: D3D11_CPU_ACCESS_READ.0 as u32, - MiscFlags: 0, - MipLevels: 1, - ArraySize: 1, - SampleDesc: DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - ..desc - }; - let mut staging: Option = None; - unsafe { device.CreateTexture2D(&staging_desc, None, Some(&mut staging))? }; - let staging = staging.context("creating staging texture")?; - unsafe { context.CopyResource(&staging, render_target) }; - - let mut mapped = D3D11_MAPPED_SUBRESOURCE::default(); - unsafe { context.Map(&staging, 0, D3D11_MAP_READ, 0, Some(&mut mapped))? }; - let row_bytes = (width as usize) * 4; - let mut pixels = vec![0u8; row_bytes * height as usize]; - // SAFETY: `Map` succeeded, so `pData` points at `RowPitch * height` - // readable bytes for as long as the mapping is held, and `RowPitch >= - // row_bytes` (it only ever adds trailing padding). `pixels` is sized - // `row_bytes * height`, so every copy stays in bounds on both sides, - // and the regions cannot overlap (`pixels` is a fresh allocation). - unsafe { - let src = mapped.pData as *const u8; - for row in 0..height as usize { - let s = src.add(row * mapped.RowPitch as usize); - let d = pixels.as_mut_ptr().add(row * row_bytes); - std::ptr::copy_nonoverlapping(s, d, row_bytes); - } - context.Unmap(&staging, 0); - } - // The render target is BGRA; image::RgbaImage expects RGBA. - for px in pixels.chunks_exact_mut(4) { - px.swap(0, 2); - } - image::RgbaImage::from_raw(width, height, pixels) - .context("Failed to build RgbaImage from staging readback") - } - - pub(crate) fn resize(&mut self, new_size: Size) -> Result<()> { - let width = new_size.width.0.max(1) as u32; - let height = new_size.height.0.max(1) as u32; - if self.width == width && self.height == height { - return Ok(()); - } - self.width = width; - self.height = height; - - // Clear the render target before resizing - let devices = self.devices.as_ref().context("devices missing")?; - unsafe { devices.device_context.OMSetRenderTargets(None, None) }; - let resources = self.resources.as_mut().context("resources missing")?; - resources.render_target.take(); - resources.render_target_view.take(); - - // Resizing the swap chain requires a call to the underlying DXGI adapter, which can return the device removed error. - // The app might have moved to a monitor that's attached to a different graphics device. - // When a graphics device is removed or reset, the desktop resolution often changes, resulting in a window size change. - // But here we just return the error, because we are handling device lost scenarios elsewhere. - unsafe { - resources - .swap_chain - .ResizeBuffers( - BUFFER_COUNT as u32, - width, - height, - RENDER_TARGET_FORMAT, - DXGI_SWAP_CHAIN_FLAG(0), - ) - .context("Failed to resize swap chain")?; - } - - resources.recreate_resources(devices, width, height)?; - - unsafe { - devices - .device_context - .OMSetRenderTargets(Some(slice::from_ref(&resources.render_target_view)), None); - } - - Ok(()) - } - - fn upload_scene_buffers(&mut self, scene: &Scene) -> Result<()> { - let devices = self.devices.as_ref().context("devices missing")?; - let empty_clip = RoundedClip::::default(); - let clips = if scene.rounded_clips.is_empty() { - slice::from_ref(&empty_clip) - } else { - &scene.rounded_clips - }; - let element_size = std::mem::size_of::>(); - anyhow::ensure!( - std::mem::size_of_val(clips) <= MAX_INSTANCE_BUFFER_SIZE, - "rounded clip buffer exceeds renderer limit" - ); - if clips.len() > self.globals.clip_capacity { - let capacity = clips - .len() - .next_power_of_two() - .min(MAX_INSTANCE_BUFFER_SIZE / element_size); - let buffer = create_buffer(&devices.device, element_size, capacity)?; - self.globals.clip_view = create_buffer_view(&devices.device, &buffer)?; - self.globals.clip_buffer = buffer; - self.globals.clip_capacity = capacity; - } - update_buffer(&devices.device_context, &self.globals.clip_buffer, clips)?; - unsafe { - devices - .device_context - .PSSetShaderResources(2, Some(slice::from_ref(&self.globals.clip_view))); - } - - if !scene.shadows.is_empty() { - self.pipelines.shadow_pipeline.update_buffer( - &devices.device, - &devices.device_context, - &scene.shadows, - )?; - } - - if !scene.quads.is_empty() { - self.pipelines.quad_pipeline.update_buffer( - &devices.device, - &devices.device_context, - &scene.quads, - )?; - } - - if !scene.underlines.is_empty() { - self.pipelines.underline_pipeline.update_buffer( - &devices.device, - &devices.device_context, - &scene.underlines, - )?; - } - - if !scene.monochrome_sprites.is_empty() { - self.pipelines.mono_sprites.update_buffer( - &devices.device, - &devices.device_context, - &scene.monochrome_sprites, - )?; - } - - if !scene.subpixel_sprites.is_empty() { - self.pipelines.subpixel_sprites.update_buffer( - &devices.device, - &devices.device_context, - &scene.subpixel_sprites, - )?; - } - - if !scene.polychrome_sprites.is_empty() { - self.pipelines.poly_sprites.update_buffer( - &devices.device, - &devices.device_context, - &scene.polychrome_sprites, - )?; - } - - Ok(()) - } - - fn draw_shadows(&mut self, start: usize, len: usize) -> Result<()> { - if len == 0 { - return Ok(()); - } - let devices = self.devices.as_ref().context("devices missing")?; - self.pipelines.shadow_pipeline.draw_range( - &devices.device_context, - self.globals - .batch_params_buffer - .as_ref() - .context("batch params buffer missing")?, - start as u32, - len as u32, - ) - } - - fn draw_quads(&mut self, start: usize, len: usize) -> Result<()> { - if len == 0 { - return Ok(()); - } - let devices = self.devices.as_ref().context("devices missing")?; - self.pipelines.quad_pipeline.draw_range( - &devices.device_context, - self.globals - .batch_params_buffer - .as_ref() - .context("batch params buffer missing")?, - start as u32, - len as u32, - ) - } - - fn draw_paths_to_intermediate(&mut self, paths: &[Path]) -> Result<()> { - if paths.is_empty() { - return Ok(()); - } - - let devices = self.devices.as_ref().context("devices missing")?; - let resources = self.resources.as_ref().context("resources missing")?; - // Clear intermediate MSAA texture - unsafe { - devices.device_context.ClearRenderTargetView( - resources.path_intermediate_msaa_view.as_ref().unwrap(), - &[0.0; 4], - ); - // Set intermediate MSAA texture as render target - devices.device_context.OMSetRenderTargets( - Some(slice::from_ref(&resources.path_intermediate_msaa_view)), - None, - ); - } - - // Collect all vertices and sprites for a single draw call - let mut vertices = Vec::new(); - - for path in paths { - vertices.extend(path.vertices.iter().map(|v| PathRasterizationSprite { - xy_position: v.xy_position, - st_position: v.st_position, - color: path.color, - bounds: path.clipped_bounds(), - content_mask: path.content_mask, - })); - } - - self.pipelines.path_rasterization_pipeline.update_buffer( - &devices.device, - &devices.device_context, - &vertices, - )?; - - self.pipelines.path_rasterization_pipeline.draw( - &devices.device_context, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, - vertices.len() as u32, - 1, - )?; - - // Resolve MSAA to non-MSAA intermediate texture - unsafe { - devices.device_context.ResolveSubresource( - &resources.path_intermediate_texture, - 0, - &resources.path_intermediate_msaa_texture, - 0, - RENDER_TARGET_FORMAT, - ); - // Restore main render target - devices - .device_context - .OMSetRenderTargets(Some(slice::from_ref(&resources.render_target_view)), None); - } - - Ok(()) - } - - fn draw_paths_from_intermediate(&mut self, paths: &[Path]) -> Result<()> { - let Some(first_path) = paths.first() else { - return Ok(()); - }; - - // When copying paths from the intermediate texture to the drawable, - // each pixel must only be copied once, in case of transparent paths. - // - // If all paths have the same draw order, then their bounds are all - // disjoint, so we can copy each path's bounds individually. If this - // batch combines different draw orders, we perform a single copy - // for a minimal spanning rect. - let sprites = if paths.last().unwrap().order == first_path.order { - paths - .iter() - .map(|path| PathSprite { - bounds: path.clipped_bounds(), - }) - .collect::>() - } else { - let mut bounds = first_path.clipped_bounds(); - for path in paths.iter().skip(1) { - bounds = bounds.union(&path.clipped_bounds()); - } - vec![PathSprite { bounds }] - }; - - let devices = self.devices.as_ref().context("devices missing")?; - let resources = self.resources.as_ref().context("resources missing")?; - self.pipelines.path_sprite_pipeline.update_buffer( - &devices.device, - &devices.device_context, - &sprites, - )?; - - // Draw the sprites with the path texture - self.pipelines.path_sprite_pipeline.draw_with_texture( - &devices.device_context, - slice::from_ref(&resources.path_intermediate_srv), - slice::from_ref(&self.globals.sampler), - sprites.len() as u32, - ) - } - - fn draw_underlines(&mut self, start: usize, len: usize) -> Result<()> { - if len == 0 { - return Ok(()); - } - let devices = self.devices.as_ref().context("devices missing")?; - self.pipelines.underline_pipeline.draw_range( - &devices.device_context, - self.globals - .batch_params_buffer - .as_ref() - .context("batch params buffer missing")?, - start as u32, - len as u32, - ) - } - - fn draw_monochrome_sprites( - &mut self, - texture_id: AtlasTextureId, - start: usize, - len: usize, - ) -> Result<()> { - if len == 0 { - return Ok(()); - } - let devices = self.devices.as_ref().context("devices missing")?; - let texture_view = self.atlas.get_texture_view(texture_id); - self.pipelines.mono_sprites.draw_range_with_texture( - &devices.device_context, - &texture_view, - self.globals - .batch_params_buffer - .as_ref() - .context("batch params buffer missing")?, - slice::from_ref(&self.globals.sampler), - start as u32, - len as u32, - ) - } - - fn draw_subpixel_sprites( - &mut self, - texture_id: AtlasTextureId, - start: usize, - len: usize, - ) -> Result<()> { - if len == 0 { - return Ok(()); - } - let devices = self.devices.as_ref().context("devices missing")?; - let texture_view = self.atlas.get_texture_view(texture_id); - self.pipelines.subpixel_sprites.draw_range_with_texture( - &devices.device_context, - &texture_view, - self.globals - .batch_params_buffer - .as_ref() - .context("batch params buffer missing")?, - slice::from_ref(&self.globals.sampler), - start as u32, - len as u32, - ) - } - - fn draw_polychrome_sprites( - &mut self, - texture_id: AtlasTextureId, - start: usize, - len: usize, - ) -> Result<()> { - if len == 0 { - return Ok(()); - } - let devices = self.devices.as_ref().context("devices missing")?; - let texture_view = self.atlas.get_texture_view(texture_id); - self.pipelines.poly_sprites.draw_range_with_texture( - &devices.device_context, - &texture_view, - self.globals - .batch_params_buffer - .as_ref() - .context("batch params buffer missing")?, - slice::from_ref(&self.globals.sampler), - start as u32, - len as u32, - ) - } - - fn draw_surfaces(&mut self, surfaces: &[PaintSurface]) -> Result<()> { - if surfaces.is_empty() { - return Ok(()); - } - Ok(()) - } - - pub(crate) fn gpu_specs(&self) -> Result { - let devices = self.devices.as_ref().context("devices missing")?; - let desc = unsafe { devices.adapter.GetDesc1() }?; - let is_software_emulated = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32) != 0; - let device_name = String::from_utf16_lossy(&desc.Description) - .trim_matches(char::from(0)) - .to_string(); - let driver_name = match desc.VendorId { - 0x10DE => "NVIDIA Corporation".to_string(), - 0x1002 => "AMD Corporation".to_string(), - 0x8086 => "Intel Corporation".to_string(), - id => format!("Unknown Vendor (ID: {:#X})", id), - }; - let driver_version = match desc.VendorId { - 0x10DE => nvidia::get_driver_version(), - 0x1002 => amd::get_driver_version(), - // For Intel and other vendors, we use the DXGI API to get the driver version. - _ => dxgi::get_driver_version(&devices.adapter), - } - .context("Failed to get gpu driver info") - .log_err() - .unwrap_or("Unknown Driver".to_string()); - Ok(GpuSpecs { - is_software_emulated, - device_name, - driver_name, - driver_info: driver_version, - }) - } - - pub(crate) fn get_font_info() -> &'static FontInfo { - static CACHED_FONT_INFO: OnceLock = OnceLock::new(); - CACHED_FONT_INFO.get_or_init(|| unsafe { - let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED).unwrap(); - let render_params: IDWriteRenderingParams1 = - factory.CreateRenderingParams().unwrap().cast().unwrap(); - FontInfo { - gamma_ratios: gpui::get_gamma_correction_ratios(render_params.GetGamma()), - grayscale_enhanced_contrast: render_params.GetGrayscaleEnhancedContrast(), - subpixel_enhanced_contrast: render_params.GetEnhancedContrast(), - is_bgr: render_params.GetPixelGeometry() == DWRITE_PIXEL_GEOMETRY_BGR, - } - }) - } - - pub(crate) fn mark_drawable(&mut self) { - self.skip_draws = false; - } -} - -impl DirectXResources { - pub fn new( - devices: &DirectXRendererDevices, - width: u32, - height: u32, - hwnd: HWND, - disable_direct_composition: bool, - ) -> Result { - let swap_chain = if disable_direct_composition { - create_swap_chain(&devices.dxgi_factory, &devices.device, hwnd, width, height)? - } else { - create_swap_chain_for_composition( - &devices.dxgi_factory, - &devices.device, - width, - height, - )? - }; - - let ( - render_target, - render_target_view, - path_intermediate_texture, - path_intermediate_srv, - path_intermediate_msaa_texture, - path_intermediate_msaa_view, - viewport, - ) = create_resources(devices, &swap_chain, width, height)?; - set_rasterizer_state(&devices.device, &devices.device_context)?; - - Ok(Self { - swap_chain, - render_target: Some(render_target), - render_target_view, - path_intermediate_texture, - path_intermediate_msaa_texture, - path_intermediate_msaa_view, - path_intermediate_srv, - viewport, - }) - } - - #[inline] - fn recreate_resources( - &mut self, - devices: &DirectXRendererDevices, - width: u32, - height: u32, - ) -> Result<()> { - let ( - render_target, - render_target_view, - path_intermediate_texture, - path_intermediate_srv, - path_intermediate_msaa_texture, - path_intermediate_msaa_view, - viewport, - ) = create_resources(devices, &self.swap_chain, width, height)?; - self.render_target = Some(render_target); - self.render_target_view = render_target_view; - self.path_intermediate_texture = path_intermediate_texture; - self.path_intermediate_msaa_texture = path_intermediate_msaa_texture; - self.path_intermediate_msaa_view = path_intermediate_msaa_view; - self.path_intermediate_srv = path_intermediate_srv; - self.viewport = viewport; - Ok(()) - } -} - -impl DirectXRenderPipelines { - pub fn new(device: &ID3D11Device) -> Result { - let shadow_pipeline = PipelineState::new( - device, - "shadow_pipeline", - ShaderModule::Shadow, - 4, - create_blend_state(device)?, - )?; - let quad_pipeline = PipelineState::new( - device, - "quad_pipeline", - ShaderModule::Quad, - 64, - create_blend_state(device)?, - )?; - let path_rasterization_pipeline = PipelineState::new( - device, - "path_rasterization_pipeline", - ShaderModule::PathRasterization, - 32, - create_blend_state_for_path_rasterization(device)?, - )?; - let path_sprite_pipeline = PipelineState::new( - device, - "path_sprite_pipeline", - ShaderModule::PathSprite, - 4, - create_blend_state_for_path_sprite(device)?, - )?; - let underline_pipeline = PipelineState::new( - device, - "underline_pipeline", - ShaderModule::Underline, - 4, - create_blend_state(device)?, - )?; - let mono_sprites = PipelineState::new( - device, - "monochrome_sprite_pipeline", - ShaderModule::MonochromeSprite, - 512, - create_blend_state(device)?, - )?; - let subpixel_sprites = PipelineState::new( - device, - "subpixel_sprite_pipeline", - ShaderModule::SubpixelSprite, - 512, - create_blend_state_for_subpixel_rendering(device)?, - )?; - let poly_sprites = PipelineState::new( - device, - "polychrome_sprite_pipeline", - ShaderModule::PolychromeSprite, - 16, - create_blend_state(device)?, - )?; - - Ok(Self { - shadow_pipeline, - quad_pipeline, - path_rasterization_pipeline, - path_sprite_pipeline, - underline_pipeline, - mono_sprites, - subpixel_sprites, - poly_sprites, - }) - } -} - -impl DirectComposition { - pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result { - let comp_device = get_comp_device(dxgi_device)?; - let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?; - let comp_visual = unsafe { comp_device.CreateVisual() }?; - - Ok(Self { - comp_device, - comp_target, - comp_visual, - }) - } - - pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> { - unsafe { - self.comp_visual.SetContent(swap_chain)?; - self.comp_target.SetRoot(&self.comp_visual)?; - self.comp_device.Commit()?; - } - Ok(()) - } -} - -impl DirectXGlobalElements { - pub fn new(device: &ID3D11Device) -> Result { - let clip_buffer = - create_buffer(device, std::mem::size_of::>(), 1)?; - let clip_view = create_buffer_view(device, &clip_buffer)?; - let global_params_buffer = create_constant_buffer::(device)?; - let batch_params_buffer = create_constant_buffer::(device)?; - - let sampler = unsafe { - let desc = D3D11_SAMPLER_DESC { - Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR, - AddressU: D3D11_TEXTURE_ADDRESS_WRAP, - AddressV: D3D11_TEXTURE_ADDRESS_WRAP, - AddressW: D3D11_TEXTURE_ADDRESS_WRAP, - MipLODBias: 0.0, - MaxAnisotropy: 1, - ComparisonFunc: D3D11_COMPARISON_ALWAYS, - BorderColor: [0.0; 4], - MinLOD: 0.0, - MaxLOD: D3D11_FLOAT32_MAX, - }; - let mut output = None; - device.CreateSamplerState(&desc, Some(&mut output))?; - output - }; - - Ok(Self { - clip_buffer, - clip_view, - clip_capacity: 1, - global_params_buffer, - batch_params_buffer, - sampler, - }) - } -} - -#[derive(Debug, Default)] -#[repr(C)] -struct GlobalParams { - gamma_ratios: [f32; 4], - viewport_size: [f32; 2], - grayscale_enhanced_contrast: f32, - subpixel_enhanced_contrast: f32, - is_bgr: u32, - _pad: [u32; 3], -} - -#[derive(Clone, Copy, Debug, Default)] -#[repr(C, align(16))] -struct BatchParams { - start_index: u32, - _padding: [u32; 3], -} - -const _: () = assert!(std::mem::size_of::() == 16); - -struct PipelineState { - label: &'static str, - vertex: ID3D11VertexShader, - fragment: ID3D11PixelShader, - buffer: ID3D11Buffer, - buffer_size: usize, - view: Option, - blend_state: ID3D11BlendState, - _marker: std::marker::PhantomData, -} - -impl PipelineState { - fn new( - device: &ID3D11Device, - label: &'static str, - shader_module: ShaderModule, - buffer_size: usize, - blend_state: ID3D11BlendState, - ) -> Result { - let vertex = { - let raw_shader = RawShaderBytes::new(shader_module, ShaderTarget::Vertex)?; - create_vertex_shader(device, raw_shader.as_bytes())? - }; - let fragment = { - let raw_shader = RawShaderBytes::new(shader_module, ShaderTarget::Fragment)?; - create_fragment_shader(device, raw_shader.as_bytes())? - }; - let buffer = create_buffer(device, std::mem::size_of::(), buffer_size)?; - let view = create_buffer_view(device, &buffer)?; - - Ok(PipelineState { - label, - vertex, - fragment, - buffer, - buffer_size, - view, - blend_state, - _marker: std::marker::PhantomData, - }) - } - - fn update_buffer( - &mut self, - device: &ID3D11Device, - device_context: &ID3D11DeviceContext, - data: &[T], - ) -> Result<()> { - if self.buffer_size < data.len() { - let element_size = std::mem::size_of::(); - let required_size = std::mem::size_of_val(data); - anyhow::ensure!( - required_size <= MAX_INSTANCE_BUFFER_SIZE, - "{} buffer needs {required_size} bytes, above the maximum of {MAX_INSTANCE_BUFFER_SIZE}", - self.label - ); - let new_buffer_size = data - .len() - .next_power_of_two() - .min(MAX_INSTANCE_BUFFER_SIZE / element_size); - log::debug!( - "Updating {} buffer size from {} to {}", - self.label, - self.buffer_size, - new_buffer_size - ); - let buffer = create_buffer(device, std::mem::size_of::(), new_buffer_size)?; - let view = create_buffer_view(device, &buffer)?; - self.buffer = buffer; - self.view = view; - self.buffer_size = new_buffer_size; - } - update_buffer(device_context, &self.buffer, data) - } - - fn draw( - &self, - device_context: &ID3D11DeviceContext, - topology: D3D_PRIMITIVE_TOPOLOGY, - vertex_count: u32, - instance_count: u32, - ) -> Result<()> { - set_pipeline_state( - device_context, - slice::from_ref(&self.view), - topology, - &self.vertex, - &self.fragment, - &self.blend_state, - ); - unsafe { - device_context.DrawInstanced(vertex_count, instance_count, 0, 0); - } - Ok(()) - } - - fn draw_with_texture( - &self, - device_context: &ID3D11DeviceContext, - texture: &[Option], - sampler: &[Option], - instance_count: u32, - ) -> Result<()> { - set_pipeline_state( - device_context, - slice::from_ref(&self.view), - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - &self.vertex, - &self.fragment, - &self.blend_state, - ); - unsafe { - device_context.PSSetSamplers(0, Some(sampler)); - device_context.VSSetShaderResources(0, Some(texture)); - device_context.PSSetShaderResources(0, Some(texture)); - - device_context.DrawInstanced(4, instance_count, 0, 0); - } - Ok(()) - } - - fn draw_range( - &self, - device_context: &ID3D11DeviceContext, - batch_params_buffer: &ID3D11Buffer, - first_instance: u32, - instance_count: u32, - ) -> Result<()> { - anyhow::ensure!( - first_instance as usize + instance_count as usize <= self.buffer_size, - "DirectX instance range exceeds the {} buffer", - self.label - ); - update_batch_start(device_context, batch_params_buffer, first_instance)?; - set_pipeline_state( - device_context, - slice::from_ref(&self.view), - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - &self.vertex, - &self.fragment, - &self.blend_state, - ); - unsafe { - device_context.DrawInstanced(4, instance_count, 0, 0); - } - Ok(()) - } - - fn draw_range_with_texture( - &self, - device_context: &ID3D11DeviceContext, - texture: &[Option], - batch_params_buffer: &ID3D11Buffer, - sampler: &[Option], - first_instance: u32, - instance_count: u32, - ) -> Result<()> { - anyhow::ensure!( - first_instance as usize + instance_count as usize <= self.buffer_size, - "DirectX instance range exceeds the {} buffer", - self.label - ); - update_batch_start(device_context, batch_params_buffer, first_instance)?; - set_pipeline_state( - device_context, - slice::from_ref(&self.view), - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - &self.vertex, - &self.fragment, - &self.blend_state, - ); - unsafe { - device_context.PSSetSamplers(0, Some(sampler)); - device_context.VSSetShaderResources(0, Some(texture)); - device_context.PSSetShaderResources(0, Some(texture)); - device_context.DrawInstanced(4, instance_count, 0, 0); - } - Ok(()) - } -} - -#[derive(Clone, Copy)] -#[repr(C)] -struct PathRasterizationSprite { - xy_position: Point, - st_position: Point, - color: Background, - bounds: Bounds, - content_mask: ContentMask, -} - -#[derive(Clone, Copy)] -#[repr(C)] -struct PathSprite { - bounds: Bounds, -} - -impl Drop for DirectXRenderer { - fn drop(&mut self) { - #[cfg(debug_assertions)] - if let Some(devices) = &self.devices { - report_live_objects(&devices.device).ok(); - } - } -} - -#[inline] -fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result { - Ok(unsafe { DCompositionCreateDevice(dxgi_device)? }) -} - -fn create_swap_chain_for_composition( - dxgi_factory: &IDXGIFactory6, - device: &ID3D11Device, - width: u32, - height: u32, -) -> Result { - let desc = DXGI_SWAP_CHAIN_DESC1 { - Width: width, - Height: height, - Format: RENDER_TARGET_FORMAT, - Stereo: false.into(), - SampleDesc: DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, - BufferCount: BUFFER_COUNT as u32, - // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling. - Scaling: DXGI_SCALING_STRETCH, - SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, - AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED, - Flags: 0, - }; - Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? }) -} - -fn create_swap_chain( - dxgi_factory: &IDXGIFactory6, - device: &ID3D11Device, - hwnd: HWND, - width: u32, - height: u32, -) -> Result { - use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER; - - let desc = DXGI_SWAP_CHAIN_DESC1 { - Width: width, - Height: height, - Format: RENDER_TARGET_FORMAT, - Stereo: false.into(), - SampleDesc: DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, - BufferCount: BUFFER_COUNT as u32, - Scaling: DXGI_SCALING_NONE, - SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, - AlphaMode: DXGI_ALPHA_MODE_IGNORE, - Flags: 0, - }; - let swap_chain = - unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?; - unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?; - Ok(swap_chain) -} - -#[inline] -fn create_resources( - devices: &DirectXRendererDevices, - swap_chain: &IDXGISwapChain1, - width: u32, - height: u32, -) -> Result<( - ID3D11Texture2D, - Option, - ID3D11Texture2D, - Option, - ID3D11Texture2D, - Option, - D3D11_VIEWPORT, -)> { - let (render_target, render_target_view) = - create_render_target_and_its_view(swap_chain, &devices.device)?; - let (path_intermediate_texture, path_intermediate_srv) = - create_path_intermediate_texture(&devices.device, width, height)?; - let (path_intermediate_msaa_texture, path_intermediate_msaa_view) = - create_path_intermediate_msaa_texture_and_view(&devices.device, width, height)?; - let viewport = D3D11_VIEWPORT { - TopLeftX: 0.0, - TopLeftY: 0.0, - Width: width as f32, - Height: height as f32, - MinDepth: 0.0, - MaxDepth: 1.0, - }; - Ok(( - render_target, - render_target_view, - path_intermediate_texture, - path_intermediate_srv, - path_intermediate_msaa_texture, - path_intermediate_msaa_view, - viewport, - )) -} - -#[inline] -fn create_render_target_and_its_view( - swap_chain: &IDXGISwapChain1, - device: &ID3D11Device, -) -> Result<(ID3D11Texture2D, Option)> { - let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?; - let mut render_target_view = None; - unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? }; - Ok((render_target, render_target_view)) -} - -#[inline] -fn create_path_intermediate_texture( - device: &ID3D11Device, - width: u32, - height: u32, -) -> Result<(ID3D11Texture2D, Option)> { - let texture = unsafe { - let mut output = None; - let desc = D3D11_TEXTURE2D_DESC { - Width: width, - Height: height, - MipLevels: 1, - ArraySize: 1, - Format: RENDER_TARGET_FORMAT, - SampleDesc: DXGI_SAMPLE_DESC { - Count: 1, - Quality: 0, - }, - Usage: D3D11_USAGE_DEFAULT, - BindFlags: (D3D11_BIND_RENDER_TARGET.0 | D3D11_BIND_SHADER_RESOURCE.0) as u32, - CPUAccessFlags: 0, - MiscFlags: 0, - }; - device.CreateTexture2D(&desc, None, Some(&mut output))?; - output.unwrap() - }; - - let mut shader_resource_view = None; - unsafe { device.CreateShaderResourceView(&texture, None, Some(&mut shader_resource_view))? }; - - Ok((texture, Some(shader_resource_view.unwrap()))) -} - -#[inline] -fn create_path_intermediate_msaa_texture_and_view( - device: &ID3D11Device, - width: u32, - height: u32, -) -> Result<(ID3D11Texture2D, Option)> { - let msaa_texture = unsafe { - let mut output = None; - let desc = D3D11_TEXTURE2D_DESC { - Width: width, - Height: height, - MipLevels: 1, - ArraySize: 1, - Format: RENDER_TARGET_FORMAT, - SampleDesc: DXGI_SAMPLE_DESC { - Count: PATH_MULTISAMPLE_COUNT, - Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32, - }, - Usage: D3D11_USAGE_DEFAULT, - BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32, - CPUAccessFlags: 0, - MiscFlags: 0, - }; - device.CreateTexture2D(&desc, None, Some(&mut output))?; - output.unwrap() - }; - let mut msaa_view = None; - unsafe { device.CreateRenderTargetView(&msaa_texture, None, Some(&mut msaa_view))? }; - Ok((msaa_texture, Some(msaa_view.unwrap()))) -} - -#[inline] -fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> { - let desc = D3D11_RASTERIZER_DESC { - FillMode: D3D11_FILL_SOLID, - CullMode: D3D11_CULL_NONE, - FrontCounterClockwise: false.into(), - DepthBias: 0, - DepthBiasClamp: 0.0, - SlopeScaledDepthBias: 0.0, - DepthClipEnable: true.into(), - ScissorEnable: false.into(), - MultisampleEnable: true.into(), - AntialiasedLineEnable: false.into(), - }; - let rasterizer_state = unsafe { - let mut state = None; - device.CreateRasterizerState(&desc, Some(&mut state))?; - state.unwrap() - }; - unsafe { device_context.RSSetState(&rasterizer_state) }; - Ok(()) -} - -// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc -#[inline] -fn create_blend_state(device: &ID3D11Device) -> Result { - let mut desc = D3D11_BLEND_DESC::default(); - desc.RenderTarget[0].BlendEnable = true.into(); - desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; - desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; - desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; - desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; - desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; - desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE; - desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8; - unsafe { - let mut state = None; - device.CreateBlendState(&desc, Some(&mut state))?; - Ok(state.unwrap()) - } -} - -#[inline] -fn create_blend_state_for_subpixel_rendering(device: &ID3D11Device) -> Result { - let mut desc = D3D11_BLEND_DESC::default(); - desc.RenderTarget[0].BlendEnable = true.into(); - desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; - desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; - desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC1_COLOR; - desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC1_COLOR; - // It does not make sense to draw transparent subpixel-rendered text, since it cannot be meaningfully alpha-blended onto anything else. - desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; - desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; - desc.RenderTarget[0].RenderTargetWriteMask = - D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8 & !D3D11_COLOR_WRITE_ENABLE_ALPHA.0 as u8; - - unsafe { - let mut state = None; - device.CreateBlendState(&desc, Some(&mut state))?; - Ok(state.unwrap()) - } -} - -#[inline] -fn create_blend_state_for_path_rasterization(device: &ID3D11Device) -> Result { - // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display - // device performs the blend in linear space, which is ideal. - let mut desc = D3D11_BLEND_DESC::default(); - desc.RenderTarget[0].BlendEnable = true.into(); - desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; - desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; - desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; - desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; - desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; - desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; - desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8; - unsafe { - let mut state = None; - device.CreateBlendState(&desc, Some(&mut state))?; - Ok(state.unwrap()) - } -} - -#[inline] -fn create_blend_state_for_path_sprite(device: &ID3D11Device) -> Result { - // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display - // device performs the blend in linear space, which is ideal. - let mut desc = D3D11_BLEND_DESC::default(); - desc.RenderTarget[0].BlendEnable = true.into(); - desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; - desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; - desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; - desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; - desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; - desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE; - desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8; - unsafe { - let mut state = None; - device.CreateBlendState(&desc, Some(&mut state))?; - Ok(state.unwrap()) - } -} - -#[inline] -fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result { - unsafe { - let mut shader = None; - device.CreateVertexShader(bytes, None, Some(&mut shader))?; - Ok(shader.unwrap()) - } -} - -#[inline] -fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result { - unsafe { - let mut shader = None; - device.CreatePixelShader(bytes, None, Some(&mut shader))?; - Ok(shader.unwrap()) - } -} - -#[inline] -fn create_constant_buffer(device: &ID3D11Device) -> Result> { - const { assert!(std::mem::size_of::() != 0 && std::mem::size_of::().is_multiple_of(16)) }; - let desc = D3D11_BUFFER_DESC { - ByteWidth: std::mem::size_of::() as u32, - Usage: D3D11_USAGE_DYNAMIC, - BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32, - CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, - MiscFlags: 0, - StructureByteStride: 0, - }; - let mut buffer = None; - unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?; - Ok(buffer) -} - -#[inline] -fn create_buffer( - device: &ID3D11Device, - element_size: usize, - buffer_size: usize, -) -> Result { - let desc = D3D11_BUFFER_DESC { - ByteWidth: (element_size * buffer_size) as u32, - Usage: D3D11_USAGE_DYNAMIC, - BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32, - CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, - MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32, - StructureByteStride: element_size as u32, - }; - let mut buffer = None; - unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?; - Ok(buffer.unwrap()) -} - -#[inline] -fn create_buffer_view( - device: &ID3D11Device, - buffer: &ID3D11Buffer, -) -> Result> { - let mut view = None; - unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?; - Ok(view) -} - -#[inline] -fn update_buffer( - device_context: &ID3D11DeviceContext, - buffer: &ID3D11Buffer, - data: &[T], -) -> Result<()> { - unsafe { - let mut dest = std::mem::zeroed(); - device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?; - std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len()); - device_context.Unmap(buffer, 0); - } - Ok(()) -} - -#[inline] -fn update_batch_start( - device_context: &ID3D11DeviceContext, - buffer: &ID3D11Buffer, - first_instance: u32, -) -> Result<()> { - update_buffer( - device_context, - buffer, - &[BatchParams { - start_index: first_instance, - _padding: [0; 3], - }], - ) -} - -#[inline] -fn set_pipeline_state( - device_context: &ID3D11DeviceContext, - buffer_view: &[Option], - topology: D3D_PRIMITIVE_TOPOLOGY, - vertex_shader: &ID3D11VertexShader, - fragment_shader: &ID3D11PixelShader, - blend_state: &ID3D11BlendState, -) { - unsafe { - device_context.VSSetShaderResources(1, Some(buffer_view)); - device_context.PSSetShaderResources(1, Some(buffer_view)); - device_context.IASetPrimitiveTopology(topology); - device_context.VSSetShader(vertex_shader, None); - device_context.PSSetShader(fragment_shader, None); - device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF); - } -} - -#[cfg(debug_assertions)] -fn report_live_objects(device: &ID3D11Device) -> Result<()> { - let debug_device: ID3D11Debug = device.cast()?; - unsafe { - debug_device.ReportLiveDeviceObjects(D3D11_RLDO_DETAIL)?; - } - Ok(()) -} - -const BUFFER_COUNT: usize = 3; - -pub(crate) mod shader_resources { - use anyhow::Result; - - #[cfg(debug_assertions)] - use windows::{ - core::{HSTRING, PCSTR}, - Win32::Graphics::Direct3D::{ - Fxc::{D3DCompileFromFile, D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION}, - ID3DBlob, - }, - }; - - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - pub(crate) enum ShaderModule { - Quad, - Shadow, - Underline, - PathRasterization, - PathSprite, - MonochromeSprite, - SubpixelSprite, - PolychromeSprite, - EmojiRasterization, - } - - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - pub(crate) enum ShaderTarget { - Vertex, - Fragment, - } - - pub(crate) struct RawShaderBytes<'t> { - inner: &'t [u8], - - #[cfg(debug_assertions)] - _blob: ID3DBlob, - } - - impl<'t> RawShaderBytes<'t> { - pub(crate) fn new(module: ShaderModule, target: ShaderTarget) -> Result { - #[cfg(not(debug_assertions))] - { - Ok(Self::from_bytes(module, target)) - } - #[cfg(debug_assertions)] - { - let blob = build_shader_blob(module, target)?; - let inner = unsafe { - std::slice::from_raw_parts( - blob.GetBufferPointer() as *const u8, - blob.GetBufferSize(), - ) - }; - Ok(Self { inner, _blob: blob }) - } - } - - pub(crate) fn as_bytes(&'t self) -> &'t [u8] { - self.inner - } - - #[cfg(not(debug_assertions))] - fn from_bytes(module: ShaderModule, target: ShaderTarget) -> Self { - let bytes = match module { - ShaderModule::Quad => match target { - ShaderTarget::Vertex => QUAD_VERTEX_BYTES, - ShaderTarget::Fragment => QUAD_FRAGMENT_BYTES, - }, - ShaderModule::Shadow => match target { - ShaderTarget::Vertex => SHADOW_VERTEX_BYTES, - ShaderTarget::Fragment => SHADOW_FRAGMENT_BYTES, - }, - ShaderModule::Underline => match target { - ShaderTarget::Vertex => UNDERLINE_VERTEX_BYTES, - ShaderTarget::Fragment => UNDERLINE_FRAGMENT_BYTES, - }, - ShaderModule::PathRasterization => match target { - ShaderTarget::Vertex => PATH_RASTERIZATION_VERTEX_BYTES, - ShaderTarget::Fragment => PATH_RASTERIZATION_FRAGMENT_BYTES, - }, - ShaderModule::PathSprite => match target { - ShaderTarget::Vertex => PATH_SPRITE_VERTEX_BYTES, - ShaderTarget::Fragment => PATH_SPRITE_FRAGMENT_BYTES, - }, - ShaderModule::MonochromeSprite => match target { - ShaderTarget::Vertex => MONOCHROME_SPRITE_VERTEX_BYTES, - ShaderTarget::Fragment => MONOCHROME_SPRITE_FRAGMENT_BYTES, - }, - ShaderModule::SubpixelSprite => match target { - ShaderTarget::Vertex => SUBPIXEL_SPRITE_VERTEX_BYTES, - ShaderTarget::Fragment => SUBPIXEL_SPRITE_FRAGMENT_BYTES, - }, - ShaderModule::PolychromeSprite => match target { - ShaderTarget::Vertex => POLYCHROME_SPRITE_VERTEX_BYTES, - ShaderTarget::Fragment => POLYCHROME_SPRITE_FRAGMENT_BYTES, - }, - ShaderModule::EmojiRasterization => match target { - ShaderTarget::Vertex => EMOJI_RASTERIZATION_VERTEX_BYTES, - ShaderTarget::Fragment => EMOJI_RASTERIZATION_FRAGMENT_BYTES, - }, - }; - Self { inner: bytes } - } - } - - #[cfg(debug_assertions)] - pub(super) fn build_shader_blob(entry: ShaderModule, target: ShaderTarget) -> Result { - unsafe { - use windows::Win32::Graphics::{ - Direct3D::ID3DInclude, Hlsl::D3D_COMPILE_STANDARD_FILE_INCLUDE, - }; - - let shader_name = if matches!(entry, ShaderModule::EmojiRasterization) { - "color_text_raster.hlsl" - } else { - "shaders.hlsl" - }; - - let entry = format!( - "{}_{}\0", - entry.as_str(), - match target { - ShaderTarget::Vertex => "vertex", - ShaderTarget::Fragment => "fragment", - } - ); - let target = match target { - ShaderTarget::Vertex => "vs_4_1\0", - ShaderTarget::Fragment => "ps_4_1\0", - }; - - let mut compile_blob = None; - let mut error_blob = None; - let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join(&format!("src/{}", shader_name)) - .canonicalize()?; - - let entry_point = PCSTR::from_raw(entry.as_ptr()); - let target_cstr = PCSTR::from_raw(target.as_ptr()); - - // really dirty trick because winapi bindings are unhappy otherwise - let include_handler = &std::mem::transmute::( - D3D_COMPILE_STANDARD_FILE_INCLUDE as usize, - ); - - let ret = D3DCompileFromFile( - &HSTRING::from(shader_path.to_str().unwrap()), - None, - include_handler, - entry_point, - target_cstr, - D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION, - 0, - &mut compile_blob, - Some(&mut error_blob), - ); - if ret.is_err() { - let Some(error_blob) = error_blob else { - return Err(anyhow::anyhow!("{ret:?}")); - }; - - let error_string = - std::ffi::CStr::from_ptr(error_blob.GetBufferPointer() as *const i8) - .to_string_lossy(); - log::error!("Shader compile error: {}", error_string); - return Err(anyhow::anyhow!("Compile error: {}", error_string)); - } - Ok(compile_blob.unwrap()) - } - } - - #[cfg(not(debug_assertions))] - include!(concat!(env!("OUT_DIR"), "/shaders_bytes.rs")); - - #[cfg(debug_assertions)] - impl ShaderModule { - pub fn as_str(self) -> &'static str { - match self { - ShaderModule::Quad => "quad", - ShaderModule::Shadow => "shadow", - ShaderModule::Underline => "underline", - ShaderModule::PathRasterization => "path_rasterization", - ShaderModule::PathSprite => "path_sprite", - ShaderModule::MonochromeSprite => "monochrome_sprite", - ShaderModule::SubpixelSprite => "subpixel_sprite", - ShaderModule::PolychromeSprite => "polychrome_sprite", - ShaderModule::EmojiRasterization => "emoji_rasterization", - } - } - } -} - -mod nvidia { - use std::{ - ffi::CStr, - os::raw::{c_char, c_int, c_uint}, - }; - - use anyhow::Result; - use windows::{core::s, Win32::System::LibraryLoader::GetProcAddress}; - - use crate::with_dll_library; - - // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L180 - const NVAPI_SHORT_STRING_MAX: usize = 64; - - // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L235 - #[allow(non_camel_case_types)] - type NvAPI_ShortString = [c_char; NVAPI_SHORT_STRING_MAX]; - - // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L447 - #[allow(non_camel_case_types)] - type NvAPI_SYS_GetDriverAndBranchVersion_t = unsafe extern "C" fn( - driver_version: *mut c_uint, - build_branch_string: *mut NvAPI_ShortString, - ) -> c_int; - - pub(super) fn get_driver_version() -> Result { - #[cfg(target_pointer_width = "64")] - let nvidia_dll_name = s!("nvapi64.dll"); - #[cfg(target_pointer_width = "32")] - let nvidia_dll_name = s!("nvapi.dll"); - - with_dll_library(nvidia_dll_name, |nvidia_dll| unsafe { - let nvapi_query_addr = GetProcAddress(nvidia_dll, s!("nvapi_QueryInterface")) - .ok_or_else(|| anyhow::anyhow!("Failed to get nvapi_QueryInterface address"))?; - let nvapi_query: extern "C" fn(u32) -> *mut () = std::mem::transmute(nvapi_query_addr); - - // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_interface.h#L41 - let nvapi_get_driver_version_ptr = nvapi_query(0x2926aaad); - if nvapi_get_driver_version_ptr.is_null() { - anyhow::bail!("Failed to get NVIDIA driver version function pointer"); - } - let nvapi_get_driver_version: NvAPI_SYS_GetDriverAndBranchVersion_t = - std::mem::transmute(nvapi_get_driver_version_ptr); - - let mut driver_version: c_uint = 0; - let mut build_branch_string: NvAPI_ShortString = [0; NVAPI_SHORT_STRING_MAX]; - let result = nvapi_get_driver_version( - &mut driver_version as *mut c_uint, - &mut build_branch_string as *mut NvAPI_ShortString, - ); - - if result != 0 { - anyhow::bail!( - "Failed to get NVIDIA driver version, error code: {}", - result - ); - } - let major = driver_version / 100; - let minor = driver_version % 100; - let branch_string = CStr::from_ptr(build_branch_string.as_ptr()); - Ok(format!( - "{}.{} {}", - major, - minor, - branch_string.to_string_lossy() - )) - }) - } -} - -mod amd { - use std::os::raw::{c_char, c_int, c_void}; - - use anyhow::Result; - use windows::{core::s, Win32::System::LibraryLoader::GetProcAddress}; - - use crate::with_dll_library; - - // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L145 - const AGS_CURRENT_VERSION: i32 = (6 << 22) | (3 << 12); - - // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L204 - // This is an opaque type, using struct to represent it properly for FFI - #[repr(C)] - struct AGSContext { - _private: [u8; 0], - } - - #[repr(C)] - pub struct AGSGPUInfo { - pub driver_version: *const c_char, - pub radeon_software_version: *const c_char, - pub num_devices: c_int, - pub devices: *mut c_void, - } - - // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L429 - #[allow(non_camel_case_types)] - type agsInitialize_t = unsafe extern "C" fn( - version: c_int, - config: *const c_void, - context: *mut *mut AGSContext, - gpu_info: *mut AGSGPUInfo, - ) -> c_int; - - // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L436 - #[allow(non_camel_case_types)] - type agsDeInitialize_t = unsafe extern "C" fn(context: *mut AGSContext) -> c_int; - - pub(super) fn get_driver_version() -> Result { - #[cfg(target_pointer_width = "64")] - let amd_dll_name = s!("amd_ags_x64.dll"); - #[cfg(target_pointer_width = "32")] - let amd_dll_name = s!("amd_ags_x86.dll"); - - with_dll_library(amd_dll_name, |amd_dll| unsafe { - let ags_initialize_addr = GetProcAddress(amd_dll, s!("agsInitialize")) - .ok_or_else(|| anyhow::anyhow!("Failed to get agsInitialize address"))?; - let ags_deinitialize_addr = GetProcAddress(amd_dll, s!("agsDeInitialize")) - .ok_or_else(|| anyhow::anyhow!("Failed to get agsDeInitialize address"))?; - - let ags_initialize: agsInitialize_t = std::mem::transmute(ags_initialize_addr); - let ags_deinitialize: agsDeInitialize_t = std::mem::transmute(ags_deinitialize_addr); - - let mut context: *mut AGSContext = std::ptr::null_mut(); - let mut gpu_info: AGSGPUInfo = AGSGPUInfo { - driver_version: std::ptr::null(), - radeon_software_version: std::ptr::null(), - num_devices: 0, - devices: std::ptr::null_mut(), - }; - - let result = ags_initialize( - AGS_CURRENT_VERSION, - std::ptr::null(), - &mut context, - &mut gpu_info, - ); - if result != 0 { - anyhow::bail!("Failed to initialize AMD AGS, error code: {}", result); - } - - // Vulkan actually returns this as the driver version - let software_version = if !gpu_info.radeon_software_version.is_null() { - std::ffi::CStr::from_ptr(gpu_info.radeon_software_version) - .to_string_lossy() - .into_owned() - } else { - "Unknown Radeon Software Version".to_string() - }; - - let driver_version = if !gpu_info.driver_version.is_null() { - std::ffi::CStr::from_ptr(gpu_info.driver_version) - .to_string_lossy() - .into_owned() - } else { - "Unknown Radeon Driver Version".to_string() - }; - - ags_deinitialize(context); - Ok(format!("{} ({})", software_version, driver_version)) - }) - } -} - -mod dxgi { - use windows::{ - core::Interface, - Win32::Graphics::Dxgi::{IDXGIAdapter1, IDXGIDevice}, - }; - - pub(super) fn get_driver_version(adapter: &IDXGIAdapter1) -> anyhow::Result { - let number = unsafe { adapter.CheckInterfaceSupport(&IDXGIDevice::IID as _) }?; - Ok(format!( - "{}.{}.{}.{}", - number >> 48, - (number >> 32) & 0xFFFF, - (number >> 16) & 0xFFFF, - number & 0xFFFF - )) - } -} diff --git a/crates/gpui_pre_windows/src/dispatcher.rs b/crates/gpui_pre_windows/src/dispatcher.rs deleted file mode 100644 index 55f18b50..00000000 --- a/crates/gpui_pre_windows/src/dispatcher.rs +++ /dev/null @@ -1,191 +0,0 @@ -use std::{ - ffi::c_void, - ptr::NonNull, - sync::atomic::{AtomicBool, Ordering}, - thread::{ThreadId, current}, - time::Duration, -}; - -use anyhow::Context; -use gpui_util::ResultExt; -use windows::Win32::{ - Foundation::{FILETIME, LPARAM, WPARAM}, - Media::{timeBeginPeriod, timeEndPeriod}, - System::Threading::{ - CloseThreadpoolTimer, CreateThreadpoolTimer, GetCurrentThread, PTP_CALLBACK_INSTANCE, - PTP_TIMER, SetThreadPriority, SetThreadpoolTimer, THREAD_PRIORITY_TIME_CRITICAL, - TP_CALLBACK_ENVIRON_V3, TP_CALLBACK_PRIORITY, TP_CALLBACK_PRIORITY_HIGH, - TP_CALLBACK_PRIORITY_LOW, TP_CALLBACK_PRIORITY_NORMAL, TrySubmitThreadpoolCallback, - }, - UI::WindowsAndMessaging::PostMessageW, -}; - -use crate::{HWND, SafeHwnd, WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD}; -use gpui::{ - PlatformDispatcher, Priority, PriorityQueueSender, RunnableVariant, TimerResolutionGuard, -}; - -pub(crate) struct WindowsDispatcher { - pub(crate) wake_posted: AtomicBool, - main_sender: PriorityQueueSender, - main_thread_id: ThreadId, - pub(crate) platform_window_handle: SafeHwnd, - validation_number: usize, -} - -impl WindowsDispatcher { - pub(crate) fn new( - main_sender: PriorityQueueSender, - platform_window_handle: HWND, - validation_number: usize, - ) -> Self { - let main_thread_id = current().id(); - let platform_window_handle = platform_window_handle.into(); - - WindowsDispatcher { - main_sender, - main_thread_id, - platform_window_handle, - validation_number, - wake_posted: AtomicBool::new(false), - } - } - - fn dispatch_on_threadpool(&self, priority: TP_CALLBACK_PRIORITY, runnable: RunnableVariant) { - let environ = TP_CALLBACK_ENVIRON_V3 { - Version: 3, - CallbackPriority: priority, - Size: size_of::() as u32, - ..Default::default() - }; - - // If the thread pool never runs our callback, the matching `from_raw` is never called, which leaks the runnable. - // Dropping the scheduled runnable would cancel its task and make the next poll of any awaiter panic. Since we expect - // the scenario to usually happen during shutdown, this leak is acceptable. - let context = runnable.into_raw().as_ptr() as *mut c_void; - - unsafe { - TrySubmitThreadpoolCallback(Some(run_work_callback), Some(context), Some(&environ)) - .log_err(); - } - } - - fn dispatch_on_threadpool_after(&self, runnable: RunnableVariant, duration: Duration) { - let context = runnable.into_raw().as_ptr() as *mut c_void; - - unsafe { - if let Ok(timer) = CreateThreadpoolTimer(Some(run_timer_callback), Some(context), None) - { - // Negative FILETIME expresses a relative delay in 100ns ticks - let ticks = (duration.as_nanos() / 100).min(i64::MAX as u128) as i64; - let due = (-ticks) as u64; - let due_time = FILETIME { - dwLowDateTime: due as u32, - dwHighDateTime: (due >> 32) as u32, - }; - SetThreadpoolTimer(timer, Some(&due_time), 0, None); - } - } - } - - #[inline(always)] - pub(crate) fn execute_runnable(runnable: RunnableVariant) { - let location = runnable.metadata().location; - let spawned = runnable.metadata().spawned; - gpui::profiler::update_running_task(spawned, location); - runnable.run(); - gpui::profiler::save_task_timing(); - } -} - -impl PlatformDispatcher for WindowsDispatcher { - fn is_main_thread(&self) -> bool { - current().id() == self.main_thread_id - } - - fn dispatch(&self, runnable: RunnableVariant, priority: Priority) { - let priority = match priority { - Priority::RealtimeAudio => { - panic!("RealtimeAudio priority should use spawn_realtime, not dispatch") - } - Priority::High => TP_CALLBACK_PRIORITY_HIGH, - Priority::Medium => TP_CALLBACK_PRIORITY_NORMAL, - Priority::Low => TP_CALLBACK_PRIORITY_LOW, - }; - self.dispatch_on_threadpool(priority, runnable); - } - - fn dispatch_on_main_thread(&self, runnable: RunnableVariant, priority: Priority) { - match self.main_sender.send(priority, runnable) { - Ok(_) => { - if !self.wake_posted.swap(true, Ordering::AcqRel) { - unsafe { - PostMessageW( - Some(self.platform_window_handle.as_raw()), - WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD, - WPARAM(self.validation_number), - LPARAM(0), - ) - .log_err(); - } - } - } - Err(runnable) => { - // NOTE: Runnable may wrap a Future that is !Send. - // - // This is usually safe because we only poll it on the main thread. - // However if the send fails, we know that: - // 1. main_receiver has been dropped (which implies the app is shutting down) - // 2. we are on a background thread. - // It is not safe to drop something !Send on the wrong thread, and - // the app will exit soon anyway, so we must forget the runnable. - std::mem::forget(runnable); - } - } - } - - fn dispatch_after(&self, duration: Duration, runnable: RunnableVariant) { - self.dispatch_on_threadpool_after(runnable, duration); - } - - fn spawn_realtime(&self, f: Box) { - std::thread::spawn(move || { - // SAFETY: always safe to call - let thread_handle = unsafe { GetCurrentThread() }; - - // SAFETY: thread_handle is a valid handle to the current thread - unsafe { SetThreadPriority(thread_handle, THREAD_PRIORITY_TIME_CRITICAL) } - .context("thread priority") - .log_err(); - - f(); - }); - } - - fn increase_timer_resolution(&self) -> TimerResolutionGuard { - unsafe { - timeBeginPeriod(1); - } - gpui_util::defer(Box::new(|| unsafe { - timeEndPeriod(1); - })) - } -} - -unsafe extern "system" fn run_work_callback( - _instance: PTP_CALLBACK_INSTANCE, - context: *mut c_void, -) { - let runnable = unsafe { RunnableVariant::from_raw(NonNull::new_unchecked(context as *mut ())) }; - WindowsDispatcher::execute_runnable(runnable); -} - -unsafe extern "system" fn run_timer_callback( - _instance: PTP_CALLBACK_INSTANCE, - context: *mut c_void, - timer: PTP_TIMER, -) { - let runnable = unsafe { RunnableVariant::from_raw(NonNull::new_unchecked(context as *mut ())) }; - WindowsDispatcher::execute_runnable(runnable); - unsafe { CloseThreadpoolTimer(timer) }; -} diff --git a/crates/gpui_pre_windows/src/display.rs b/crates/gpui_pre_windows/src/display.rs deleted file mode 100644 index ee02309d..00000000 --- a/crates/gpui_pre_windows/src/display.rs +++ /dev/null @@ -1,210 +0,0 @@ -use gpui_util::ResultExt; -use itertools::Itertools; -use smallvec::SmallVec; -use std::rc::Rc; -use uuid::Uuid; -use windows::{ - Win32::{ - Foundation::*, - Graphics::Gdi::*, - UI::{ - HiDpi::{GetDpiForMonitor, MDT_EFFECTIVE_DPI}, - WindowsAndMessaging::USER_DEFAULT_SCREEN_DPI, - }, - }, - core::*, -}; - -use crate::logical_point; -use gpui::{Bounds, DevicePixels, DisplayId, Pixels, PlatformDisplay, point, size}; - -#[derive(Debug, Clone, Copy)] -pub(crate) struct WindowsDisplay { - pub handle: HMONITOR, - pub display_id: DisplayId, - scale_factor: f32, - bounds: Bounds, - visible_bounds: Bounds, - physical_bounds: Bounds, - uuid: Uuid, -} - -// The `HMONITOR` is thread-safe. -unsafe impl Send for WindowsDisplay {} -unsafe impl Sync for WindowsDisplay {} - -impl WindowsDisplay { - pub(crate) fn new(display_id: DisplayId) -> Option { - let handle = HMONITOR(u64::from(display_id) as _); - let info = get_monitor_info(handle).log_err()?; - let monitor_size = info.monitorInfo.rcMonitor; - let work_area = info.monitorInfo.rcWork; - let uuid = generate_uuid(&info.szDevice); - let scale_factor = get_scale_factor_for_monitor(handle).log_err()?; - let physical_size = size( - (monitor_size.right - monitor_size.left).into(), - (monitor_size.bottom - monitor_size.top).into(), - ); - - Some(WindowsDisplay { - handle, - display_id, - scale_factor, - bounds: Bounds { - origin: logical_point( - monitor_size.left as f32, - monitor_size.top as f32, - scale_factor, - ), - size: physical_size.to_pixels(scale_factor), - }, - visible_bounds: Bounds { - origin: logical_point(work_area.left as f32, work_area.top as f32, scale_factor), - size: size( - (work_area.right - work_area.left) as f32 / scale_factor, - (work_area.bottom - work_area.top) as f32 / scale_factor, - ) - .map(gpui::px), - }, - physical_bounds: Bounds { - origin: point(monitor_size.left.into(), monitor_size.top.into()), - size: physical_size, - }, - uuid, - }) - } - - pub(crate) fn display_id_for_monitor(monitor: HMONITOR) -> DisplayId { - DisplayId::new(monitor.0 as u64) - } - - pub fn primary_monitor() -> Option { - // https://devblogs.microsoft.com/oldnewthing/20070809-00/?p=25643 - const POINT_ZERO: POINT = POINT { x: 0, y: 0 }; - let monitor = unsafe { MonitorFromPoint(POINT_ZERO, MONITOR_DEFAULTTOPRIMARY) }; - if monitor.is_invalid() { - log::error!( - "can not find the primary monitor: {}", - std::io::Error::last_os_error() - ); - return None; - } - WindowsDisplay::new(Self::display_id_for_monitor(monitor)) - } - - /// The DPI scale factor of this monitor, independent of whatever monitor - /// a not-yet-positioned window currently happens to be on. - pub(crate) fn scale_factor(&self) -> f32 { - self.scale_factor - } - - /// Check if the center point of given bounds is inside this monitor - pub fn check_given_bounds(&self, bounds: Bounds) -> bool { - let center = bounds.center(); - let center = POINT { - x: (center.x.as_f32() * self.scale_factor) as i32, - y: (center.y.as_f32() * self.scale_factor) as i32, - }; - let monitor = unsafe { MonitorFromPoint(center, MONITOR_DEFAULTTONULL) }; - if monitor.is_invalid() { - false - } else { - let Some(display) = WindowsDisplay::new(Self::display_id_for_monitor(monitor)) else { - return false; - }; - display.uuid == self.uuid - } - } - - pub fn displays() -> Vec> { - available_monitors() - .into_iter() - .filter_map(|handle| { - Some( - Rc::new(WindowsDisplay::new(Self::display_id_for_monitor(handle))?) - as Rc, - ) - }) - .collect() - } - - pub fn physical_bounds(&self) -> Bounds { - self.physical_bounds - } -} - -impl PlatformDisplay for WindowsDisplay { - fn id(&self) -> DisplayId { - self.display_id - } - - fn uuid(&self) -> anyhow::Result { - Ok(self.uuid) - } - - fn bounds(&self) -> Bounds { - self.bounds - } - - fn visible_bounds(&self) -> Bounds { - self.visible_bounds - } -} - -fn available_monitors() -> SmallVec<[HMONITOR; 4]> { - let mut monitors: SmallVec<[HMONITOR; 4]> = SmallVec::new(); - unsafe { - EnumDisplayMonitors( - None, - None, - Some(monitor_enum_proc), - LPARAM(&mut monitors as *mut _ as _), - ) - .ok() - .log_err(); - } - monitors -} - -unsafe extern "system" fn monitor_enum_proc( - hmonitor: HMONITOR, - _hdc: HDC, - _place: *mut RECT, - data: LPARAM, -) -> BOOL { - let monitors = data.0 as *mut SmallVec<[HMONITOR; 4]>; - unsafe { (*monitors).push(hmonitor) }; - BOOL(1) -} - -fn get_monitor_info(hmonitor: HMONITOR) -> anyhow::Result { - let mut monitor_info: MONITORINFOEXW = unsafe { std::mem::zeroed() }; - monitor_info.monitorInfo.cbSize = std::mem::size_of::() as u32; - let status = unsafe { - GetMonitorInfoW( - hmonitor, - &mut monitor_info as *mut MONITORINFOEXW as *mut MONITORINFO, - ) - }; - if status.as_bool() { - Ok(monitor_info) - } else { - Err(anyhow::anyhow!(std::io::Error::last_os_error())) - } -} - -fn generate_uuid(device_name: &[u16]) -> Uuid { - let name = device_name - .iter() - .flat_map(|&a| a.to_be_bytes()) - .collect_vec(); - Uuid::new_v5(&Uuid::NAMESPACE_DNS, &name) -} - -fn get_scale_factor_for_monitor(monitor: HMONITOR) -> Result { - let mut dpi_x = 0; - let mut dpi_y = 0; - unsafe { GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) }?; - assert_eq!(dpi_x, dpi_y); - Ok(dpi_x as f32 / USER_DEFAULT_SCREEN_DPI as f32) -} diff --git a/crates/gpui_pre_windows/src/events.rs b/crates/gpui_pre_windows/src/events.rs deleted file mode 100644 index e700cb0f..00000000 --- a/crates/gpui_pre_windows/src/events.rs +++ /dev/null @@ -1,1773 +0,0 @@ -use std::{cell::Cell, rc::Rc, sync::atomic::Ordering}; - -use anyhow::Context as _; -use gpui_util::ResultExt; -use windows::{ - Win32::{ - Foundation::*, - Graphics::Gdi::*, - System::SystemServices::*, - UI::{ - Controls::*, - HiDpi::*, - Input::{Ime::*, KeyboardAndMouse::*}, - WindowsAndMessaging::*, - }, - }, - core::PCWSTR, -}; - -use crate::*; -use gpui::*; - -pub(crate) const WM_GPUI_CURSOR_STYLE_CHANGED: u32 = WM_USER + 1; -pub(crate) const WM_GPUI_CLOSE_ONE_WINDOW: u32 = WM_USER + 2; -pub(crate) const WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD: u32 = WM_USER + 3; -pub(crate) const WM_GPUI_DOCK_MENU_ACTION: u32 = WM_USER + 4; -pub(crate) const WM_GPUI_FORCE_UPDATE_WINDOW: u32 = WM_USER + 5; -pub(crate) const WM_GPUI_KEYBOARD_LAYOUT_CHANGED: u32 = WM_USER + 6; -pub(crate) const WM_GPUI_GPU_DEVICE_LOST: u32 = WM_USER + 7; -pub(crate) const WM_GPUI_KEYDOWN: u32 = WM_USER + 8; -pub(crate) const WM_GPUI_END_SESSION: u32 = WM_USER + 9; - -const SIZE_MOVE_LOOP_TIMER_ID: usize = 1; - -/// Coordinates window draws on the UI thread. Owned by the platform and -/// shared with every window (like `WindowsPlatformState::cursor_visible`), -/// because the coordination is inherently cross-window: while window A is -/// drawing, a re-entrant paint request for window B must be deferred. -pub(crate) struct DrawCoordinator { - /// Whether some window is currently inside `draw_window`. Win32 can - /// re-enter the window procedure while a draw is in progress (e.g. - /// cross-thread `SendMessage` dispatch during message pumping, or modal - /// message loops entered by COM calls), and drawing re-entrantly would - /// nest GPUI draws. Nested draws are wasted work whose output is - /// immediately redrawn, so we defer them instead. - drawing: Cell, -} - -impl DrawCoordinator { - pub(crate) fn new() -> Self { - Self { - drawing: Cell::new(false), - } - } - - fn try_begin_draw(&self) -> Option> { - // This only covers the `draw_window` span (which extends past the GPUI - // draw, through presentation and IME updates). Requests that arrive - // re-entrantly during GPUI-initiated draws (e.g. key dispatch or - // opening a window draws synchronously) are deferred by GPUI's - // `on_request_frame` callback itself, which no-ops in that case. - if self.drawing.get() { - None - } else { - self.drawing.set(true); - Some(DrawWindowGuard { coordinator: self }) - } - } -} - -struct DrawWindowGuard<'a> { - coordinator: &'a DrawCoordinator, -} - -impl Drop for DrawWindowGuard<'_> { - fn drop(&mut self) { - self.coordinator.drawing.set(false); - } -} - -impl WindowsWindowInner { - pub(crate) fn handle_msg( - self: &Rc, - handle: HWND, - msg: u32, - wparam: WPARAM, - lparam: LPARAM, - ) -> LRESULT { - let handled = match msg { - // `DefWindowProc` answers `MA_NOACTIVATE` for a left click on `HTCAPTION`. - // The activation is only triggered when `DefWindowProc` handles the following `WM_NCLBUTTONDOWN`. - // The GPUI event is dispatched in between, so a click handler runs while `active_window` is still - // whichever window was active before the click. If that handler consumes the - // press, `DefWindowProc` never sees it, so the window is never activated at all. - // So, let's eagerly activate the window. - WM_MOUSEACTIVATE => Some(MA_ACTIVATE as isize), - WM_ACTIVATE => self.handle_activate_msg(wparam), - WM_CREATE => self.handle_create_msg(handle), - WM_MOVE => self.handle_move_msg(handle, lparam), - WM_SIZE => self.handle_size_msg(wparam, lparam), - WM_GETMINMAXINFO => self.handle_get_min_max_info_msg(lparam), - WM_ENTERSIZEMOVE | WM_ENTERMENULOOP => self.handle_size_move_loop(handle), - WM_EXITSIZEMOVE | WM_EXITMENULOOP => self.handle_size_move_loop_exit(handle), - WM_TIMER => self.handle_timer_msg(handle, wparam), - WM_NCCALCSIZE => self.handle_calc_client_size(handle, wparam, lparam), - WM_DPICHANGED => self.handle_dpi_changed_msg(handle, wparam, lparam), - WM_DISPLAYCHANGE => self.handle_display_change_msg(handle), - WM_NCHITTEST => self.handle_hit_test_msg(handle, lparam), - WM_PAINT => self.handle_paint_msg(handle), - WM_CLOSE => self.handle_close_msg(), - WM_DESTROY => self.handle_destroy_msg(handle), - WM_QUERYENDSESSION => Some(1), - WM_ENDSESSION => self.handle_end_session_msg(wparam), - WM_MOUSEMOVE => self.handle_mouse_move_msg(handle, lparam, wparam), - WM_MOUSELEAVE | WM_NCMOUSELEAVE => self.handle_mouse_leave_msg(), - WM_NCMOUSEMOVE => self.handle_nc_mouse_move_msg(handle, lparam), - // Treat double click as a second single click, since we track the double clicks ourselves. - // If you don't interact with any elements, this will fall through to the windows default - // behavior of toggling whether the window is maximized. - WM_NCLBUTTONDBLCLK | WM_NCLBUTTONDOWN => { - self.handle_nc_mouse_down_msg(handle, MouseButton::Left, wparam, lparam) - } - WM_NCRBUTTONDOWN => { - self.handle_nc_mouse_down_msg(handle, MouseButton::Right, wparam, lparam) - } - WM_NCMBUTTONDOWN => { - self.handle_nc_mouse_down_msg(handle, MouseButton::Middle, wparam, lparam) - } - WM_NCLBUTTONUP => { - self.handle_nc_mouse_up_msg(handle, MouseButton::Left, wparam, lparam) - } - WM_NCRBUTTONUP => { - self.handle_nc_mouse_up_msg(handle, MouseButton::Right, wparam, lparam) - } - WM_NCMBUTTONUP => { - self.handle_nc_mouse_up_msg(handle, MouseButton::Middle, wparam, lparam) - } - WM_LBUTTONDOWN => self.handle_mouse_down_msg(handle, MouseButton::Left, lparam), - WM_RBUTTONDOWN => self.handle_mouse_down_msg(handle, MouseButton::Right, lparam), - WM_MBUTTONDOWN => self.handle_mouse_down_msg(handle, MouseButton::Middle, lparam), - WM_XBUTTONDOWN => { - self.handle_xbutton_msg(handle, wparam, lparam, Self::handle_mouse_down_msg) - } - WM_LBUTTONUP => self.handle_mouse_up_msg(handle, MouseButton::Left, lparam), - WM_RBUTTONUP => self.handle_mouse_up_msg(handle, MouseButton::Right, lparam), - WM_MBUTTONUP => self.handle_mouse_up_msg(handle, MouseButton::Middle, lparam), - WM_XBUTTONUP => { - self.handle_xbutton_msg(handle, wparam, lparam, Self::handle_mouse_up_msg) - } - WM_MOUSEWHEEL => self.handle_mouse_wheel_msg(handle, wparam, lparam), - WM_MOUSEHWHEEL => self.handle_mouse_horizontal_wheel_msg(handle, wparam, lparam), - WM_SYSKEYUP => self.handle_syskeyup_msg(wparam, lparam), - WM_KEYUP => self.handle_keyup_msg(wparam, lparam), - WM_GPUI_KEYDOWN => self.handle_keydown_msg(wparam, lparam), - WM_CHAR => self.handle_char_msg(wparam), - WM_IME_STARTCOMPOSITION => self.handle_ime_position(handle), - WM_IME_COMPOSITION => self.handle_ime_composition(handle, lparam), - WM_SETCURSOR => self.handle_set_cursor(handle, lparam), - WM_SETTINGCHANGE => self.handle_system_settings_changed(handle, wparam, lparam), - WM_INPUTLANGCHANGE => self.handle_input_language_changed(), - WM_SHOWWINDOW => self.handle_window_visibility_changed(handle, wparam), - WM_GPUI_CURSOR_STYLE_CHANGED => self.handle_cursor_changed(lparam), - WM_GPUI_FORCE_UPDATE_WINDOW => self.draw_window(handle, true), - WM_GPUI_GPU_DEVICE_LOST => self.handle_device_lost(lparam), - DM_POINTERHITTEST => self.handle_dm_pointer_hit_test(wparam), - WM_GETOBJECT => self.handle_wm_getobject(wparam, lparam), - _ => None, - }; - if let Some(n) = handled { - LRESULT(n) - } else { - unsafe { DefWindowProcW(handle, msg, wparam, lparam) } - } - } - - fn handle_end_session_msg(&self, wparam: WPARAM) -> Option { - if wparam.0 != 0 { - unsafe { - SendMessageW( - self.platform_window_handle, - WM_GPUI_END_SESSION, - Some(WPARAM(self.validation_number)), - None, - ); - } - } - Some(0) - } - - fn handle_move_msg(&self, handle: HWND, lparam: LPARAM) -> Option { - let origin = logical_point( - lparam.signed_loword() as f32, - lparam.signed_hiword() as f32, - self.state.scale_factor.get(), - ); - self.state.origin.set(origin); - let size = self.state.logical_size.get(); - let center_x = origin.x.as_f32() + size.width.as_f32() / 2.; - let center_y = origin.y.as_f32() + size.height.as_f32() / 2.; - let monitor_bounds = self.state.display.get().bounds(); - if center_x < monitor_bounds.left().as_f32() - || center_x > monitor_bounds.right().as_f32() - || center_y < monitor_bounds.top().as_f32() - || center_y > monitor_bounds.bottom().as_f32() - { - // center of the window may have moved to another monitor - let monitor = unsafe { MonitorFromWindow(handle, MONITOR_DEFAULTTONULL) }; - // minimize the window can trigger this event too, in this case, - // monitor is invalid, we do nothing. - if !monitor.is_invalid() && self.state.display.get().handle != monitor { - // we will get the same monitor if we only have one - self.state.display.set(WindowsDisplay::new( - WindowsDisplay::display_id_for_monitor(monitor), - )?); - } - } - if let Some(mut callback) = self.state.callbacks.moved.take() { - callback(); - self.state.callbacks.moved.set(Some(callback)); - } - Some(0) - } - - fn handle_get_min_max_info_msg(&self, lparam: LPARAM) -> Option { - let min_size = self.state.min_size?; - let scale_factor = self.state.scale_factor.get(); - let boarder_offset = &self.state.border_offset; - - unsafe { - let minmax_info = &mut *(lparam.0 as *mut MINMAXINFO); - minmax_info.ptMinTrackSize.x = min_size.width.scale(scale_factor).as_f32() as i32 - + boarder_offset.width_offset.get(); - minmax_info.ptMinTrackSize.y = min_size.height.scale(scale_factor).as_f32() as i32 - + boarder_offset.height_offset.get(); - } - Some(0) - } - - fn handle_size_msg(&self, wparam: WPARAM, lparam: LPARAM) -> Option { - // Don't resize the renderer when the window is minimized, but record that it was minimized so - // that on restore the swap chain can be recreated via `update_drawable_size_even_if_unchanged`. - if wparam.0 == SIZE_MINIMIZED as usize { - self.state - .restore_from_minimized - .set(self.state.callbacks.request_frame.take()); - return Some(0); - } - - let width = lparam.loword().max(1) as i32; - let height = lparam.hiword().max(1) as i32; - let new_size = size(DevicePixels(width), DevicePixels(height)); - - let scale_factor = self.state.scale_factor.get(); - let mut should_resize_renderer = false; - if let Some(restore_from_minimized) = self.state.restore_from_minimized.take() { - self.state - .callbacks - .request_frame - .set(Some(restore_from_minimized)); - } else { - should_resize_renderer = true; - } - - self.handle_size_change(new_size, scale_factor, should_resize_renderer); - Some(0) - } - - fn handle_size_change( - &self, - device_size: Size, - scale_factor: f32, - should_resize_renderer: bool, - ) { - let new_logical_size = device_size.to_pixels(scale_factor); - - self.state.logical_size.set(new_logical_size); - if should_resize_renderer - && let Err(e) = self.state.renderer.borrow_mut().resize(device_size) - { - log::error!("Failed to resize renderer, invalidating devices: {}", e); - self.state - .invalidate_devices - .store(true, std::sync::atomic::Ordering::Release); - } - if let Some(mut callback) = self.state.callbacks.resize.take() { - callback(new_logical_size, scale_factor); - self.state.callbacks.resize.set(Some(callback)); - } - } - - fn handle_size_move_loop(&self, handle: HWND) -> Option { - unsafe { - let ret = SetTimer( - Some(handle), - SIZE_MOVE_LOOP_TIMER_ID, - USER_TIMER_MINIMUM, - None, - ); - if ret == 0 { - log::error!( - "unable to create timer: {}", - std::io::Error::last_os_error() - ); - } - } - None - } - - fn handle_size_move_loop_exit(&self, handle: HWND) -> Option { - unsafe { - KillTimer(Some(handle), SIZE_MOVE_LOOP_TIMER_ID).log_err(); - } - None - } - - fn handle_timer_msg(&self, handle: HWND, wparam: WPARAM) -> Option { - if wparam.0 == SIZE_MOVE_LOOP_TIMER_ID { - let mut runnables = self.main_receiver.clone().try_iter(); - while let Some(Ok(runnable)) = runnables.next() { - WindowsDispatcher::execute_runnable(runnable); - } - self.handle_paint_msg(handle) - } else { - None - } - } - - fn handle_paint_msg(&self, handle: HWND) -> Option { - self.draw_window(handle, false) - } - - fn handle_close_msg(&self) -> Option { - let mut callback = self.state.callbacks.should_close.take()?; - let should_close = callback(); - self.state.callbacks.should_close.set(Some(callback)); - if should_close { None } else { Some(0) } - } - - fn handle_destroy_msg(&self, handle: HWND) -> Option { - let callback = { self.state.callbacks.close.take() }; - // Re-enable parent window if this was a modal dialog - if let Some(parent_hwnd) = self.parent_hwnd { - unsafe { - let _ = EnableWindow(parent_hwnd, true); - let _ = SetForegroundWindow(parent_hwnd); - } - } - - if let Some(callback) = callback { - callback(); - } - unsafe { - PostMessageW( - Some(self.platform_window_handle), - WM_GPUI_CLOSE_ONE_WINDOW, - WPARAM(self.validation_number), - LPARAM(handle.0 as isize), - ) - .log_err(); - } - Some(0) - } - - fn handle_mouse_move_msg(&self, handle: HWND, lparam: LPARAM, wparam: WPARAM) -> Option { - self.start_tracking_mouse(handle, TME_LEAVE); - self.restore_cursor_after_hide(); - - let Some(mut func) = self.state.callbacks.input.take() else { - return Some(1); - }; - let scale_factor = self.state.scale_factor.get(); - - let pressed_button = match MODIFIERKEYS_FLAGS(wparam.loword() as u32) { - flags if flags.contains(MK_LBUTTON) => Some(MouseButton::Left), - flags if flags.contains(MK_RBUTTON) => Some(MouseButton::Right), - flags if flags.contains(MK_MBUTTON) => Some(MouseButton::Middle), - flags if flags.contains(MK_XBUTTON1) => { - Some(MouseButton::Navigate(NavigationDirection::Back)) - } - flags if flags.contains(MK_XBUTTON2) => { - Some(MouseButton::Navigate(NavigationDirection::Forward)) - } - _ => None, - }; - let x = lparam.signed_loword() as f32; - let y = lparam.signed_hiword() as f32; - let input = PlatformInput::MouseMove(MouseMoveEvent { - position: logical_point(x, y, scale_factor), - pressed_button, - modifiers: current_modifiers(), - }); - let handled = !func(input).propagate; - self.state.callbacks.input.set(Some(func)); - - if handled { Some(0) } else { Some(1) } - } - - fn handle_mouse_leave_msg(&self) -> Option { - self.state.hovered.set(false); - // The next window's `WM_SETCURSOR` picks its own cursor, so we just clear - // the flag for tight `is_cursor_visible()` semantics. - self.state.cursor_visible.store(true, Ordering::Relaxed); - if let Some(mut callback) = self.state.callbacks.hovered_status_change.take() { - callback(false); - self.state - .callbacks - .hovered_status_change - .set(Some(callback)); - } - - Some(0) - } - - fn handle_syskeyup_msg(&self, wparam: WPARAM, lparam: LPARAM) -> Option { - let input = handle_key_event(wparam, lparam, &self.state, |keystroke, _| { - PlatformInput::KeyUp(KeyUpEvent { keystroke }) - })?; - let mut func = self.state.callbacks.input.take()?; - - func(input); - self.state.callbacks.input.set(Some(func)); - - // Always return 0 to indicate that the message was handled, so we could properly handle `ModifiersChanged` event. - Some(0) - } - - // It's a known bug that you can't trigger `ctrl-shift-0`. See: - // https://superuser.com/questions/1455762/ctrl-shift-number-key-combination-has-stopped-working-for-a-few-numbers - fn handle_keydown_msg(&self, wparam: WPARAM, lparam: LPARAM) -> Option { - let Some(input) = handle_key_event( - wparam, - lparam, - &self.state, - |keystroke, prefer_character_input| { - PlatformInput::KeyDown(KeyDownEvent { - keystroke, - is_held: lparam.0 & (0x1 << 30) > 0, - prefer_character_input, - }) - }, - ) else { - return Some(1); - }; - - let Some(mut func) = self.state.callbacks.input.take() else { - return Some(1); - }; - - let handled = !func(input).propagate; - - self.state.callbacks.input.set(Some(func)); - - if handled { Some(0) } else { Some(1) } - } - - fn handle_keyup_msg(&self, wparam: WPARAM, lparam: LPARAM) -> Option { - let Some(input) = handle_key_event(wparam, lparam, &self.state, |keystroke, _| { - PlatformInput::KeyUp(KeyUpEvent { keystroke }) - }) else { - return Some(1); - }; - - let Some(mut func) = self.state.callbacks.input.take() else { - return Some(1); - }; - - let handled = !func(input).propagate; - self.state.callbacks.input.set(Some(func)); - - if handled { Some(0) } else { Some(1) } - } - - fn handle_char_msg(&self, wparam: WPARAM) -> Option { - let input = self.parse_char_message(wparam)?; - self.with_input_handler(|input_handler| { - input_handler.replace_text_in_range(None, &input); - }); - - Some(0) - } - - fn handle_mouse_down_msg( - &self, - handle: HWND, - button: MouseButton, - lparam: LPARAM, - ) -> Option { - unsafe { SetCapture(handle) }; - - let Some(mut func) = self.state.callbacks.input.take() else { - return Some(1); - }; - let x = lparam.signed_loword(); - let y = lparam.signed_hiword(); - let physical_point = point(DevicePixels(x as i32), DevicePixels(y as i32)); - let click_count = self.state.click_state.update(button, physical_point); - let scale_factor = self.state.scale_factor.get(); - - let input = PlatformInput::MouseDown(MouseDownEvent { - button, - position: logical_point(x as f32, y as f32, scale_factor), - modifiers: current_modifiers(), - click_count, - first_mouse: false, - }); - let handled = !func(input).propagate; - self.state.callbacks.input.set(Some(func)); - - if handled { Some(0) } else { Some(1) } - } - - fn handle_mouse_up_msg( - &self, - _handle: HWND, - button: MouseButton, - lparam: LPARAM, - ) -> Option { - unsafe { ReleaseCapture().log_err() }; - - let Some(mut func) = self.state.callbacks.input.take() else { - return Some(1); - }; - let x = lparam.signed_loword() as f32; - let y = lparam.signed_hiword() as f32; - let click_count = self.state.click_state.current_count.get(); - let scale_factor = self.state.scale_factor.get(); - - let input = PlatformInput::MouseUp(MouseUpEvent { - button, - position: logical_point(x, y, scale_factor), - modifiers: current_modifiers(), - click_count, - }); - let handled = !func(input).propagate; - self.state.callbacks.input.set(Some(func)); - - if handled { Some(0) } else { Some(1) } - } - - fn handle_xbutton_msg( - &self, - handle: HWND, - wparam: WPARAM, - lparam: LPARAM, - handler: impl Fn(&Self, HWND, MouseButton, LPARAM) -> Option, - ) -> Option { - let nav_dir = match wparam.hiword() { - XBUTTON1 => NavigationDirection::Back, - XBUTTON2 => NavigationDirection::Forward, - _ => return Some(1), - }; - handler(self, handle, MouseButton::Navigate(nav_dir), lparam) - } - - fn handle_mouse_wheel_msg( - &self, - handle: HWND, - wparam: WPARAM, - lparam: LPARAM, - ) -> Option { - let modifiers = current_modifiers(); - - let Some(mut func) = self.state.callbacks.input.take() else { - return Some(1); - }; - let scale_factor = self.state.scale_factor.get(); - let wheel_scroll_amount = match modifiers.shift { - true => self - .system_settings() - .mouse_wheel_settings - .wheel_scroll_chars - .get(), - false => self - .system_settings() - .mouse_wheel_settings - .wheel_scroll_lines - .get(), - }; - - let wheel_distance = - (wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) * wheel_scroll_amount as f32; - let mut cursor_point = POINT { - x: lparam.signed_loword().into(), - y: lparam.signed_hiword().into(), - }; - unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() }; - let input = PlatformInput::ScrollWheel(ScrollWheelEvent { - position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor), - delta: ScrollDelta::Lines(match modifiers.shift { - true => Point { - x: wheel_distance, - y: 0.0, - }, - false => Point { - y: wheel_distance, - x: 0.0, - }, - }), - modifiers, - touch_phase: TouchPhase::Moved, - }); - let handled = !func(input).propagate; - self.state.callbacks.input.set(Some(func)); - - if handled { Some(0) } else { Some(1) } - } - - fn handle_mouse_horizontal_wheel_msg( - &self, - handle: HWND, - wparam: WPARAM, - lparam: LPARAM, - ) -> Option { - let Some(mut func) = self.state.callbacks.input.take() else { - return Some(1); - }; - let scale_factor = self.state.scale_factor.get(); - let wheel_scroll_chars = self - .system_settings() - .mouse_wheel_settings - .wheel_scroll_chars - .get(); - - let wheel_distance = - (-wparam.signed_hiword() as f32 / WHEEL_DELTA as f32) * wheel_scroll_chars as f32; - let mut cursor_point = POINT { - x: lparam.signed_loword().into(), - y: lparam.signed_hiword().into(), - }; - unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() }; - let event = PlatformInput::ScrollWheel(ScrollWheelEvent { - position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor), - delta: ScrollDelta::Lines(Point { - x: wheel_distance, - y: 0.0, - }), - modifiers: current_modifiers(), - touch_phase: TouchPhase::Moved, - }); - let handled = !func(event).propagate; - self.state.callbacks.input.set(Some(func)); - - if handled { Some(0) } else { Some(1) } - } - - fn retrieve_caret_position(&self) -> Option { - self.with_input_handler_and_scale_factor(|input_handler, scale_factor| { - let caret_range = input_handler.selected_text_range(false)?; - let caret_position = input_handler.bounds_for_range(caret_range.range)?; - Some(POINT { - // logical to physical - x: (caret_position.origin.x.as_f32() * scale_factor) as i32, - y: (caret_position.origin.y.as_f32() * scale_factor) as i32 - + ((caret_position.size.height.as_f32() * scale_factor) as i32 / 2), - }) - }) - } - - fn handle_ime_position(&self, handle: HWND) -> Option { - if let Some(caret_position) = self.retrieve_caret_position() { - self.update_ime_position(handle, caret_position); - } - Some(0) - } - - pub(crate) fn update_ime_position(&self, handle: HWND, caret_position: POINT) { - let Some(ctx) = ImeContext::get(handle) else { - return; - }; - unsafe { - ImmSetCompositionWindow( - *ctx, - &COMPOSITIONFORM { - dwStyle: CFS_POINT, - ptCurrentPos: caret_position, - ..Default::default() - }, - ) - .ok() - .log_err(); - - ImmSetCandidateWindow( - *ctx, - &CANDIDATEFORM { - dwStyle: CFS_CANDIDATEPOS, - ptCurrentPos: caret_position, - ..Default::default() - }, - ) - .ok() - .log_err(); - } - } - - fn update_ime_enabled(&self, handle: HWND) { - let ime_enabled = self - .with_input_handler(|input_handler| input_handler.query_accepts_text_input()) - .unwrap_or(false); - if ime_enabled == self.state.ime_enabled.get() { - return; - } - self.state.ime_enabled.set(ime_enabled); - unsafe { - if ime_enabled { - ImmAssociateContextEx(handle, HIMC::default(), IACE_DEFAULT) - .ok() - .log_err(); - } else { - // The IME context is per-thread, so without this check a change in this - // window's text input state could commit an IME composition happening in another window. - if GetFocus() == handle - && let Some(ctx) = ImeContext::get(handle) - { - ImmNotifyIME(*ctx, NI_COMPOSITIONSTR, CPS_COMPLETE, 0) - .ok() - .log_err(); - } - ImmAssociateContextEx(handle, HIMC::default(), 0) - .ok() - .log_err(); - } - } - } - - fn handle_ime_composition(&self, handle: HWND, lparam: LPARAM) -> Option { - let ctx = ImeContext::get(handle)?; - self.handle_ime_composition_inner(*ctx, lparam) - } - - fn handle_ime_composition_inner(&self, ctx: HIMC, lparam: LPARAM) -> Option { - let lparam = lparam.0 as u32; - if lparam == 0 { - // Japanese IME may send this message with lparam = 0, which indicates that - // there is no composition string. - self.with_input_handler(|input_handler| { - input_handler.replace_text_in_range(None, ""); - })?; - Some(0) - } else { - if lparam & GCS_RESULTSTR.0 > 0 { - let comp_result = parse_ime_composition_string(ctx, GCS_RESULTSTR)?; - self.with_input_handler(|input_handler| { - input_handler - .replace_text_in_range(None, &String::from_utf16_lossy(&comp_result)); - })?; - } - if lparam & GCS_COMPSTR.0 > 0 { - let comp_string = parse_ime_composition_string(ctx, GCS_COMPSTR)?; - let caret_pos = - (!comp_string.is_empty() && lparam & GCS_CURSORPOS.0 > 0).then(|| { - let cursor_pos = retrieve_composition_cursor_position(ctx); - let pos = if should_use_ime_cursor_position(ctx, cursor_pos) { - cursor_pos - } else { - comp_string.len() - }; - pos..pos - }); - self.with_input_handler(|input_handler| { - input_handler.replace_and_mark_text_in_range( - None, - &String::from_utf16_lossy(&comp_string), - caret_pos, - ); - })?; - } - if lparam & (GCS_RESULTSTR.0 | GCS_COMPSTR.0) > 0 { - return Some(0); - } - - // currently, we don't care other stuff - None - } - } - - fn handle_calc_client_size( - &self, - handle: HWND, - wparam: WPARAM, - lparam: LPARAM, - ) -> Option { - if !self.hide_title_bar || self.state.is_fullscreen() || wparam.0 == 0 { - return None; - } - - unsafe { - let params = lparam.0 as *mut NCCALCSIZE_PARAMS; - let saved_top = (*params).rgrc[0].top; - let result = DefWindowProcW(handle, WM_NCCALCSIZE, wparam, lparam); - (*params).rgrc[0].top = saved_top; - if self.state.is_maximized() { - let dpi = GetDpiForWindow(handle); - (*params).rgrc[0].top += get_frame_thicknessx(dpi); - } - Some(result.0 as isize) - } - } - - fn handle_activate_msg(self: &Rc, wparam: WPARAM) -> Option { - let activated = wparam.loword() > 0; - - let events = self - .state - .a11y - .try_borrow_mut() - .ok() - .and_then(|mut a11y| a11y.as_mut()?.adapter.update_window_focus_state(activated)); - if let Some(events) = events { - events.raise(); - } - - let this = self.clone(); - - if !activated { - this.state.cursor_visible.store(true, Ordering::Relaxed); - } - - // When the window is activated (gains focus), reset the modifier tracking state. - // This fixes the issue where Alt-Tab away and back leaves stale modifier state - // (especially the Alt key) because Windows doesn't always send key-up events to - // windows that have lost focus. - if activated { - this.state.last_reported_modifiers.set(None); - this.state.last_reported_capslock.set(None); - - if let Some(mut func) = this.state.callbacks.input.take() { - let input = PlatformInput::ModifiersChanged(ModifiersChangedEvent { - modifiers: current_modifiers(), - capslock: current_capslock(), - }); - func(input); - this.state.callbacks.input.set(Some(func)); - } - } - - self.executor - .spawn(async move { - if let Some(mut func) = this.state.callbacks.active_status_change.take() { - func(activated); - this.state.callbacks.active_status_change.set(Some(func)); - } - }) - .detach(); - - None - } - - fn handle_wm_getobject(&self, wparam: WPARAM, lparam: LPARAM) -> Option { - let result = { - let mut a11y = self.state.a11y.borrow_mut(); - let a11y = a11y.as_mut()?; - a11y.adapter.handle_wm_getobject( - accesskit_windows::WPARAM(wparam.0), - accesskit_windows::LPARAM(lparam.0), - &mut a11y.activation_handler, - )? - }; - // The borrow above must be dropped before calling `.into()`, because - // it calls `UiaReturnRawElementProvider` which may send a nested - // `WM_GETOBJECT` back into this window procedure. - let lresult: accesskit_windows::LRESULT = result.into(); - Some(lresult.0) - } - - fn handle_create_msg(&self, handle: HWND) -> Option { - if self.hide_title_bar { - notify_frame_changed(handle); - Some(0) - } else { - None - } - } - - fn handle_dpi_changed_msg( - &self, - handle: HWND, - wparam: WPARAM, - lparam: LPARAM, - ) -> Option { - let new_dpi = wparam.loword() as f32; - - let is_maximized = self.state.is_maximized(); - let new_scale_factor = new_dpi / USER_DEFAULT_SCREEN_DPI as f32; - self.state.scale_factor.set(new_scale_factor); - self.state.border_offset.update(handle).log_err(); - - self.state - .direct_manipulation - .set_scale_factor(new_scale_factor); - - if is_maximized { - // Get the monitor and its work area at the new DPI - let monitor = unsafe { MonitorFromWindow(handle, MONITOR_DEFAULTTONEAREST) }; - let mut monitor_info: MONITORINFO = unsafe { std::mem::zeroed() }; - monitor_info.cbSize = std::mem::size_of::() as u32; - if unsafe { GetMonitorInfoW(monitor, &mut monitor_info) }.as_bool() { - let work_area = monitor_info.rcWork; - let width = work_area.right - work_area.left; - let height = work_area.bottom - work_area.top; - - // Update the window size to match the new monitor work area - // This will trigger WM_SIZE which will handle the size change - unsafe { - SetWindowPos( - handle, - None, - work_area.left, - work_area.top, - width, - height, - SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED, - ) - .context("unable to set maximized window position after dpi has changed") - .log_err(); - } - - // SetWindowPos may not send WM_SIZE for maximized windows in some cases, - // so we manually update the size to ensure proper rendering - let device_size = size(DevicePixels(width), DevicePixels(height)); - self.handle_size_change(device_size, new_scale_factor, true); - } - } else { - // For non-maximized windows, use the suggested RECT from the system - let rect = unsafe { &*(lparam.0 as *const RECT) }; - let width = rect.right - rect.left; - let height = rect.bottom - rect.top; - // this will emit `WM_SIZE` and `WM_MOVE` right here - // even before this function returns - // the new size is handled in `WM_SIZE` - unsafe { - SetWindowPos( - handle, - None, - rect.left, - rect.top, - width, - height, - SWP_NOZORDER | SWP_NOACTIVATE, - ) - .context("unable to set window position after dpi has changed") - .log_err(); - } - } - - Some(0) - } - - fn handle_display_change_msg(&self, handle: HWND) -> Option { - let new_monitor = unsafe { MonitorFromWindow(handle, MONITOR_DEFAULTTONULL) }; - if new_monitor.is_invalid() { - log::error!("No monitor detected!"); - return None; - } - let new_display = WindowsDisplay::new(WindowsDisplay::display_id_for_monitor(new_monitor))?; - self.state.display.set(new_display); - Some(0) - } - - fn handle_hit_test_msg(&self, handle: HWND, lparam: LPARAM) -> Option { - if self.state.is_fullscreen() { - return None; - } - - let callback = self.state.callbacks.hit_test_window_control.take(); - let drag_area = if let Some(mut callback) = callback { - let area = callback(); - self.state - .callbacks - .hit_test_window_control - .set(Some(callback)); - area.and_then(|area| match area { - WindowControlArea::Drag if self.is_movable => Some(HTCAPTION as _), - WindowControlArea::Drag => None, - WindowControlArea::Close => Some(HTCLOSE as _), - WindowControlArea::Max if self.is_resizable => Some(HTMAXBUTTON as _), - WindowControlArea::Max if self.is_movable => Some(HTCAPTION as _), - WindowControlArea::Max => Some(HTNOWHERE as _), - WindowControlArea::Min if self.is_minimizable => Some(HTMINBUTTON as _), - WindowControlArea::Min if self.is_movable => Some(HTCAPTION as _), - WindowControlArea::Min => Some(HTNOWHERE as _), - }) - } else { - None - }; - - if !self.hide_title_bar { - // If the OS draws the title bar, we don't need to handle hit test messages. - return drag_area; - } - - let dpi = unsafe { GetDpiForWindow(handle) }; - // We do not use the OS title bar, so the default `DefWindowProcW` will only register a 1px edge for resizes - // We need to calculate the frame thickness ourselves and do the hit test manually. - let frame_y = get_frame_thicknessx(dpi); - let frame_x = get_frame_thicknessy(dpi); - let mut cursor_point = POINT { - x: lparam.signed_loword().into(), - y: lparam.signed_hiword().into(), - }; - - unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() }; - if self.is_resizable - && !self.state.is_maximized() - && 0 <= cursor_point.y - && cursor_point.y <= frame_y - { - // x-axis actually goes from -frame_x to 0 - return Some(if cursor_point.x <= 0 { - HTTOPLEFT - } else { - let mut rect = Default::default(); - unsafe { GetWindowRect(handle, &mut rect) }.log_err(); - // right and bottom bounds of RECT are exclusive, thus `-1` - let right = rect.right - rect.left - 1; - // the bounds include the padding frames, so accommodate for both of them - if right - 2 * frame_x <= cursor_point.x { - HTTOPRIGHT - } else { - HTTOP - } - } as _); - } - - drag_area - } - - fn handle_nc_mouse_move_msg(&self, handle: HWND, lparam: LPARAM) -> Option { - self.start_tracking_mouse(handle, TME_LEAVE | TME_NONCLIENT); - self.restore_cursor_after_hide(); - - let mut func = self.state.callbacks.input.take()?; - let scale_factor = self.state.scale_factor.get(); - - let mut cursor_point = POINT { - x: lparam.signed_loword().into(), - y: lparam.signed_hiword().into(), - }; - unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() }; - let input = PlatformInput::MouseMove(MouseMoveEvent { - position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor), - pressed_button: None, - modifiers: current_modifiers(), - }); - let handled = !func(input).propagate; - self.state.callbacks.input.set(Some(func)); - - if handled { Some(0) } else { None } - } - - fn handle_nc_mouse_down_msg( - &self, - handle: HWND, - button: MouseButton, - wparam: WPARAM, - lparam: LPARAM, - ) -> Option { - if let Some(mut func) = self.state.callbacks.input.take() { - let scale_factor = self.state.scale_factor.get(); - let mut cursor_point = POINT { - x: lparam.signed_loword().into(), - y: lparam.signed_hiword().into(), - }; - unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() }; - let physical_point = point(DevicePixels(cursor_point.x), DevicePixels(cursor_point.y)); - let click_count = self.state.click_state.update(button, physical_point); - - let input = PlatformInput::MouseDown(MouseDownEvent { - button, - position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor), - modifiers: current_modifiers(), - click_count, - first_mouse: false, - }); - let handled = !func(input).propagate; - self.state.callbacks.input.set(Some(func)); - - if handled { - return Some(0); - } - } else { - }; - - // Since these are handled in handle_nc_mouse_up_msg we must prevent the default window proc - if button == MouseButton::Left { - match wparam.0 as u32 { - HTMINBUTTON => self.state.nc_button_pressed.set(Some(HTMINBUTTON)), - HTMAXBUTTON => self.state.nc_button_pressed.set(Some(HTMAXBUTTON)), - HTCLOSE => self.state.nc_button_pressed.set(Some(HTCLOSE)), - _ => return None, - }; - Some(0) - } else { - None - } - } - - fn handle_nc_mouse_up_msg( - &self, - handle: HWND, - button: MouseButton, - wparam: WPARAM, - lparam: LPARAM, - ) -> Option { - if let Some(mut func) = self.state.callbacks.input.take() { - let scale_factor = self.state.scale_factor.get(); - - let mut cursor_point = POINT { - x: lparam.signed_loword().into(), - y: lparam.signed_hiword().into(), - }; - unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() }; - let input = PlatformInput::MouseUp(MouseUpEvent { - button, - position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor), - modifiers: current_modifiers(), - click_count: 1, - }); - let handled = !func(input).propagate; - self.state.callbacks.input.set(Some(func)); - - if handled { - return Some(0); - } - } else { - } - - let last_pressed = self.state.nc_button_pressed.take(); - if button == MouseButton::Left - && let Some(last_pressed) = last_pressed - { - let handled = match (wparam.0 as u32, last_pressed) { - (HTMINBUTTON, HTMINBUTTON) if self.is_minimizable => { - unsafe { ShowWindowAsync(handle, SW_MINIMIZE).ok().log_err() }; - true - } - (HTMINBUTTON, HTMINBUTTON) => true, - (HTMAXBUTTON, HTMAXBUTTON) if self.is_resizable => { - if self.state.is_maximized() { - unsafe { ShowWindowAsync(handle, SW_NORMAL).ok().log_err() }; - } else { - unsafe { ShowWindowAsync(handle, SW_MAXIMIZE).ok().log_err() }; - } - true - } - (HTMAXBUTTON, HTMAXBUTTON) => true, - (HTCLOSE, HTCLOSE) => { - unsafe { - PostMessageW(Some(handle), WM_CLOSE, WPARAM::default(), LPARAM::default()) - .log_err() - }; - true - } - _ => false, - }; - if handled { - return Some(0); - } - } - - None - } - - fn handle_cursor_changed(&self, lparam: LPARAM) -> Option { - let had_cursor = self.state.current_cursor.get().is_some(); - - self.state.current_cursor.set(if lparam.0 == 0 { - None - } else { - Some(HCURSOR(lparam.0 as _)) - }); - - if had_cursor != self.state.current_cursor.get().is_some() { - unsafe { SetCursor(self.state.current_cursor.get()) }; - } - - Some(0) - } - - fn handle_set_cursor(&self, handle: HWND, lparam: LPARAM) -> Option { - if unsafe { !IsWindowEnabled(handle).as_bool() } - || matches!( - lparam.loword() as u32, - HTLEFT - | HTRIGHT - | HTTOP - | HTTOPLEFT - | HTTOPRIGHT - | HTBOTTOM - | HTBOTTOMLEFT - | HTBOTTOMRIGHT - ) - { - return None; - } - let cursor = if self.state.cursor_visible.load(Ordering::Relaxed) { - self.state.current_cursor.get() - } else { - None - }; - unsafe { - SetCursor(cursor); - }; - Some(0) - } - - fn handle_system_settings_changed( - &self, - handle: HWND, - wparam: WPARAM, - lparam: LPARAM, - ) -> Option { - if wparam.0 != 0 { - self.state.click_state.system_update(wparam.0); - self.state.border_offset.update(handle).log_err(); - // system settings may emit a window message which wants to take the refcell self.state, so drop it - - self.system_settings().update(wparam.0); - } else { - self.handle_system_theme_changed(handle, lparam)?; - }; - - Some(0) - } - - fn handle_system_theme_changed(&self, handle: HWND, lparam: LPARAM) -> Option { - // lParam is a pointer to a string that indicates the area containing the system parameter - // that was changed. - let parameter = PCWSTR::from_raw(lparam.0 as _); - if unsafe { !parameter.is_null() && !parameter.is_empty() } - && let Some(parameter_string) = unsafe { parameter.to_string() }.log_err() - { - log::info!("System settings changed: {}", parameter_string); - if parameter_string.as_str() == "ImmersiveColorSet" { - let new_appearance = system_appearance() - .context("unable to get system appearance when handling ImmersiveColorSet") - .log_err()?; - - if new_appearance != self.state.appearance.get() { - self.state.appearance.set(new_appearance); - let mut callback = self.state.callbacks.appearance_changed.take()?; - - callback(); - self.state.callbacks.appearance_changed.set(Some(callback)); - configure_dwm_dark_mode(handle, new_appearance); - } - } - } - Some(0) - } - - fn handle_input_language_changed(&self) -> Option { - unsafe { - PostMessageW( - Some(self.platform_window_handle), - WM_GPUI_KEYBOARD_LAYOUT_CHANGED, - WPARAM(self.validation_number), - LPARAM(0), - ) - .log_err(); - } - Some(0) - } - - fn handle_window_visibility_changed(&self, handle: HWND, wparam: WPARAM) -> Option { - if wparam.0 == 1 { - self.draw_window(handle, false); - } - None - } - - fn handle_device_lost(&self, lparam: LPARAM) -> Option { - let devices = lparam.0 as *const DirectXDevices; - let devices = unsafe { &*devices }; - if let Err(err) = self - .state - .renderer - .borrow_mut() - .handle_device_lost(&devices) - { - panic!("Device lost: {err}"); - } - // Make sure the first `draw_window` after recovery (whether it comes - // from the forced WM_GPUI_FORCE_UPDATE_WINDOW or a stray WM_PAINT in - // between) is treated as a forced render so it both clears - // `skip_draws` and bypasses the view cache. - self.state.force_render_pending.set(true); - Some(0) - } - - fn handle_dm_pointer_hit_test(&self, wparam: WPARAM) -> Option { - self.state.direct_manipulation.on_pointer_hit_test(wparam); - None - } - - #[inline] - fn draw_window(&self, handle: HWND, force_render: bool) -> Option { - let Some(_guard) = self.state.draw_coordinator.try_begin_draw() else { - log::debug!("deferring re-entrant draw of window {handle:?}"); - if force_render { - self.state.force_render_pending.set(true); - } - // Validate the region so a nested message pump doesn't keep - // re-dispatching WM_PAINT for the still-invalid region in a busy - // loop until the in-progress draw unwinds. The vsync thread - // re-invalidates every window on each vsync (see - // `begin_vsync_thread`), so the deferred frame still gets drawn, - // at most one vsync late. - unsafe { ValidateRect(Some(handle), None).ok().log_err() }; - return Some(0); - }; - let mut request_frame = self.state.callbacks.request_frame.take()?; - - self.state.direct_manipulation.update(); - - let events = self.state.direct_manipulation.drain_events(); - if !events.is_empty() { - if let Some(mut func) = self.state.callbacks.input.take() { - for event in events { - func(event); - } - self.state.callbacks.input.set(Some(func)); - } - } - - let force_render = force_render || self.state.force_render_pending.take(); - if force_render { - // Re-enable drawing after a device loss recovery. The forced render - // will rebuild the scene with fresh atlas textures. - self.state.renderer.borrow_mut().mark_drawable(); - } - request_frame(RequestFrameOptions { - require_presentation: false, - force_render, - }); - - self.state.callbacks.request_frame.set(Some(request_frame)); - self.update_ime_enabled(handle); - unsafe { ValidateRect(Some(handle), None).ok().log_err() }; - - Some(0) - } - - #[inline] - fn parse_char_message(&self, wparam: WPARAM) -> Option { - let code_point = wparam.loword(); - - // https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G2630 - match code_point { - 0xD800..=0xDBFF => { - // High surrogate, wait for low surrogate - self.state.pending_surrogate.set(Some(code_point)); - None - } - 0xDC00..=0xDFFF => { - if let Some(high_surrogate) = self.state.pending_surrogate.take() { - // Low surrogate, combine with pending high surrogate - String::from_utf16(&[high_surrogate, code_point]).ok() - } else { - // Invalid low surrogate without a preceding high surrogate - log::warn!( - "Received low surrogate without a preceding high surrogate: {code_point:x}" - ); - None - } - } - _ => { - self.state.pending_surrogate.set(None); - char::from_u32(code_point as u32) - .filter(|c| !c.is_control()) - .map(|c| c.to_string()) - } - } - } - - /// Clear the hidden flag and restore the cursor immediately - fn restore_cursor_after_hide(&self) { - if !self.state.cursor_visible.swap(true, Ordering::Relaxed) { - unsafe { - SetCursor(self.state.current_cursor.get()); - } - } - } - - fn start_tracking_mouse(&self, handle: HWND, flags: TRACKMOUSEEVENT_FLAGS) { - if !self.state.hovered.get() { - self.state.hovered.set(true); - unsafe { - TrackMouseEvent(&mut TRACKMOUSEEVENT { - cbSize: std::mem::size_of::() as u32, - dwFlags: flags, - hwndTrack: handle, - dwHoverTime: HOVER_DEFAULT, - }) - .log_err() - }; - if let Some(mut callback) = self.state.callbacks.hovered_status_change.take() { - callback(true); - self.state - .callbacks - .hovered_status_change - .set(Some(callback)); - } - } - } - - fn with_input_handler(&self, f: F) -> Option - where - F: FnOnce(&mut PlatformInputHandler) -> R, - { - let mut input_handler = self.state.input_handler.take()?; - let result = f(&mut input_handler); - self.state.input_handler.set(Some(input_handler)); - Some(result) - } - - fn with_input_handler_and_scale_factor(&self, f: F) -> Option - where - F: FnOnce(&mut PlatformInputHandler, f32) -> Option, - { - let mut input_handler = self.state.input_handler.take()?; - let scale_factor = self.state.scale_factor.get(); - - let result = f(&mut input_handler, scale_factor); - self.state.input_handler.set(Some(input_handler)); - result - } -} - -struct ImeContext { - hwnd: HWND, - himc: HIMC, -} - -impl ImeContext { - fn get(hwnd: HWND) -> Option { - let himc = unsafe { ImmGetContext(hwnd) }; - if himc.is_invalid() { - return None; - } - Some(Self { hwnd, himc }) - } -} - -impl std::ops::Deref for ImeContext { - type Target = HIMC; - fn deref(&self) -> &HIMC { - &self.himc - } -} - -impl Drop for ImeContext { - fn drop(&mut self) { - unsafe { - ImmReleaseContext(self.hwnd, self.himc).ok().log_err(); - } - } -} - -fn handle_key_event( - wparam: WPARAM, - lparam: LPARAM, - state: &WindowsWindowState, - f: F, -) -> Option -where - F: FnOnce(Keystroke, bool) -> PlatformInput, -{ - let virtual_key = VIRTUAL_KEY(wparam.loword()); - let modifiers = current_modifiers(); - - match virtual_key { - VK_SHIFT | VK_CONTROL | VK_MENU | VK_LMENU | VK_RMENU | VK_LWIN | VK_RWIN => { - if state - .last_reported_modifiers - .get() - .is_some_and(|prev_modifiers| prev_modifiers == modifiers) - { - return None; - } - state.last_reported_modifiers.set(Some(modifiers)); - Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent { - modifiers, - capslock: current_capslock(), - })) - } - VK_PACKET => None, - VK_CAPITAL => { - let capslock = current_capslock(); - if state - .last_reported_capslock - .get() - .is_some_and(|prev_capslock| prev_capslock == capslock) - { - return None; - } - state.last_reported_capslock.set(Some(capslock)); - Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent { - modifiers, - capslock, - })) - } - vkey => { - let keystroke = parse_normal_key(vkey, lparam, modifiers)?; - Some(f(keystroke.0, keystroke.1)) - } - } -} - -fn parse_immutable(vkey: VIRTUAL_KEY) -> Option { - Some( - match vkey { - VK_SPACE => "space", - VK_BACK => "backspace", - VK_RETURN => "enter", - VK_TAB => "tab", - VK_UP => "up", - VK_DOWN => "down", - VK_RIGHT => "right", - VK_LEFT => "left", - VK_HOME => "home", - VK_END => "end", - VK_PRIOR => "pageup", - VK_NEXT => "pagedown", - VK_BROWSER_BACK => "back", - VK_BROWSER_FORWARD => "forward", - VK_ESCAPE => "escape", - VK_INSERT => "insert", - VK_DELETE => "delete", - VK_APPS => "menu", - VK_F1 => "f1", - VK_F2 => "f2", - VK_F3 => "f3", - VK_F4 => "f4", - VK_F5 => "f5", - VK_F6 => "f6", - VK_F7 => "f7", - VK_F8 => "f8", - VK_F9 => "f9", - VK_F10 => "f10", - VK_F11 => "f11", - VK_F12 => "f12", - VK_F13 => "f13", - VK_F14 => "f14", - VK_F15 => "f15", - VK_F16 => "f16", - VK_F17 => "f17", - VK_F18 => "f18", - VK_F19 => "f19", - VK_F20 => "f20", - VK_F21 => "f21", - VK_F22 => "f22", - VK_F23 => "f23", - VK_F24 => "f24", - _ => return None, - } - .to_string(), - ) -} - -fn parse_normal_key( - vkey: VIRTUAL_KEY, - lparam: LPARAM, - mut modifiers: Modifiers, -) -> Option<(Keystroke, bool)> { - let (key_char, prefer_character_input) = process_key(vkey, lparam.hiword()); - - let key = parse_immutable(vkey).or_else(|| { - let scan_code = lparam.hiword() & 0xFF; - get_keystroke_key(vkey, scan_code as u32, &mut modifiers) - })?; - - Some(( - Keystroke { - modifiers, - key, - key_char, - }, - prefer_character_input, - )) -} - -fn process_key(vkey: VIRTUAL_KEY, scan_code: u16) -> (Option, bool) { - let mut keyboard_state = [0u8; 256]; - unsafe { - if GetKeyboardState(&mut keyboard_state).is_err() { - return (None, false); - } - } - - let mut buffer_c = [0u16; 8]; - let result_c = unsafe { - ToUnicode( - vkey.0 as u32, - scan_code as u32, - Some(&keyboard_state), - &mut buffer_c, - 0x4, - ) - }; - - if result_c == 0 { - return (None, false); - } - - let c = &buffer_c[..result_c.unsigned_abs() as usize]; - let key_char = String::from_utf16(c) - .ok() - .filter(|s| !s.is_empty() && !s.chars().next().unwrap().is_control()); - - if result_c < 0 { - return (key_char, true); - } - - if key_char.is_none() { - return (None, false); - } - - // Workaround for some bug that makes the compiler think keyboard_state is still zeroed out - let keyboard_state = std::hint::black_box(keyboard_state); - let ctrl_down = (keyboard_state[VK_CONTROL.0 as usize] & 0x80) != 0; - let alt_down = (keyboard_state[VK_MENU.0 as usize] & 0x80) != 0; - let win_down = (keyboard_state[VK_LWIN.0 as usize] & 0x80) != 0 - || (keyboard_state[VK_RWIN.0 as usize] & 0x80) != 0; - - let has_modifiers = ctrl_down || alt_down || win_down; - if !has_modifiers { - return (key_char, false); - } - - let mut state_no_modifiers = keyboard_state; - state_no_modifiers[VK_CONTROL.0 as usize] = 0; - state_no_modifiers[VK_LCONTROL.0 as usize] = 0; - state_no_modifiers[VK_RCONTROL.0 as usize] = 0; - state_no_modifiers[VK_MENU.0 as usize] = 0; - state_no_modifiers[VK_LMENU.0 as usize] = 0; - state_no_modifiers[VK_RMENU.0 as usize] = 0; - state_no_modifiers[VK_LWIN.0 as usize] = 0; - state_no_modifiers[VK_RWIN.0 as usize] = 0; - - let mut buffer_c_no_modifiers = [0u16; 8]; - let result_c_no_modifiers = unsafe { - ToUnicode( - vkey.0 as u32, - scan_code as u32, - Some(&state_no_modifiers), - &mut buffer_c_no_modifiers, - 0x4, - ) - }; - - let c_no_modifiers = &buffer_c_no_modifiers[..result_c_no_modifiers.unsigned_abs() as usize]; - ( - key_char, - result_c != result_c_no_modifiers || c != c_no_modifiers, - ) -} - -fn parse_ime_composition_string(ctx: HIMC, comp_type: IME_COMPOSITION_STRING) -> Option> { - unsafe { - let string_len = ImmGetCompositionStringW(ctx, comp_type, None, 0); - if string_len >= 0 { - let mut buffer = vec![0u8; string_len as usize + 2]; - ImmGetCompositionStringW( - ctx, - comp_type, - Some(buffer.as_mut_ptr() as _), - string_len as _, - ); - let wstring = std::slice::from_raw_parts::( - buffer.as_mut_ptr().cast::(), - string_len as usize / 2, - ); - Some(wstring.to_vec()) - } else { - None - } - } -} - -#[inline] -fn retrieve_composition_cursor_position(ctx: HIMC) -> usize { - unsafe { ImmGetCompositionStringW(ctx, GCS_CURSORPOS, None, 0) as usize } -} - -fn should_use_ime_cursor_position(ctx: HIMC, cursor_pos: usize) -> bool { - let attrs_size = unsafe { ImmGetCompositionStringW(ctx, GCS_COMPATTR, None, 0) } as usize; - if attrs_size == 0 { - return false; - } - - let mut attrs = vec![0u8; attrs_size]; - let result = unsafe { - ImmGetCompositionStringW( - ctx, - GCS_COMPATTR, - Some(attrs.as_mut_ptr() as *mut _), - attrs_size as u32, - ) - }; - if result <= 0 { - return false; - } - - // Keep the cursor adjacent to the inserted text by only using the suggested position - // if it's adjacent to unconverted text. - let at_cursor_is_input = cursor_pos < attrs.len() && attrs[cursor_pos] == (ATTR_INPUT as u8); - let before_cursor_is_input = cursor_pos > 0 - && (cursor_pos - 1) < attrs.len() - && attrs[cursor_pos - 1] == (ATTR_INPUT as u8); - - at_cursor_is_input || before_cursor_is_input -} - -#[inline] -fn is_virtual_key_pressed(vkey: VIRTUAL_KEY) -> bool { - unsafe { GetKeyState(vkey.0 as i32) < 0 } -} - -#[inline] -pub(crate) fn current_modifiers() -> Modifiers { - Modifiers { - control: is_virtual_key_pressed(VK_CONTROL), - alt: is_virtual_key_pressed(VK_MENU), - shift: is_virtual_key_pressed(VK_SHIFT), - platform: is_virtual_key_pressed(VK_LWIN) || is_virtual_key_pressed(VK_RWIN), - function: false, - } -} - -#[inline] -pub(crate) fn current_capslock() -> Capslock { - let on = unsafe { GetKeyState(VK_CAPITAL.0 as i32) & 1 } > 0; - Capslock { on } -} - -// there is some additional non-visible space when talking about window -// borders on Windows: -// - SM_CXSIZEFRAME: The resize handle. -// - SM_CXPADDEDBORDER: Additional border space that isn't part of the resize handle. -fn get_frame_thicknessx(dpi: u32) -> i32 { - let resize_frame_thickness = unsafe { GetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) }; - let padding_thickness = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) }; - resize_frame_thickness + padding_thickness -} - -fn get_frame_thicknessy(dpi: u32) -> i32 { - let resize_frame_thickness = unsafe { GetSystemMetricsForDpi(SM_CYSIZEFRAME, dpi) }; - let padding_thickness = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) }; - resize_frame_thickness + padding_thickness -} - -fn notify_frame_changed(handle: HWND) { - unsafe { - SetWindowPos( - handle, - None, - 0, - 0, - 0, - 0, - SWP_FRAMECHANGED - | SWP_NOACTIVATE - | SWP_NOCOPYBITS - | SWP_NOMOVE - | SWP_NOOWNERZORDER - | SWP_NOREPOSITION - | SWP_NOSENDCHANGING - | SWP_NOSIZE - | SWP_NOZORDER, - ) - .log_err(); - } -} diff --git a/crates/gpui_pre_windows/src/gpui_windows.rs b/crates/gpui_pre_windows/src/gpui_windows.rs deleted file mode 100644 index 692d0dbb..00000000 --- a/crates/gpui_pre_windows/src/gpui_windows.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![cfg(target_os = "windows")] - -mod clipboard; -mod destination_list; -mod direct_manipulation; -mod direct_write; -mod directx_atlas; -mod directx_devices; -mod directx_renderer; -mod dispatcher; -mod display; -mod events; -mod keyboard; -mod platform; -mod system_notifications; -mod system_settings; -mod util; -mod vsync; -mod window; -mod wrapper; - -pub(crate) use clipboard::*; -pub(crate) use destination_list::*; -pub(crate) use direct_write::*; -pub(crate) use directx_atlas::*; -pub(crate) use directx_devices::*; -pub(crate) use directx_renderer::*; -pub(crate) use dispatcher::*; -pub(crate) use display::*; -pub(crate) use events::*; -pub(crate) use keyboard::*; -pub(crate) use platform::*; -pub(crate) use system_notifications::*; -pub(crate) use system_settings::*; -pub(crate) use util::*; -pub(crate) use vsync::*; -pub(crate) use window::*; -pub(crate) use wrapper::*; - -pub use platform::WindowsPlatform; - -pub(crate) use windows::Win32::Foundation::HWND; diff --git a/crates/gpui_pre_windows/src/keyboard.rs b/crates/gpui_pre_windows/src/keyboard.rs deleted file mode 100644 index 8164bc15..00000000 --- a/crates/gpui_pre_windows/src/keyboard.rs +++ /dev/null @@ -1,371 +0,0 @@ -use anyhow::Result; -use collections::HashMap; -use windows::Win32::UI::{ - Input::KeyboardAndMouse::{ - GetKeyboardLayoutNameW, MAPVK_VK_TO_CHAR, MAPVK_VK_TO_VSC, MapVirtualKeyW, ToUnicode, - VIRTUAL_KEY, VK_0, VK_1, VK_2, VK_3, VK_4, VK_5, VK_6, VK_7, VK_8, VK_9, VK_ABNT_C1, - VK_CONTROL, VK_MENU, VK_OEM_1, VK_OEM_2, VK_OEM_3, VK_OEM_4, VK_OEM_5, VK_OEM_6, VK_OEM_7, - VK_OEM_8, VK_OEM_102, VK_OEM_COMMA, VK_OEM_MINUS, VK_OEM_PERIOD, VK_OEM_PLUS, VK_SHIFT, - }, - WindowsAndMessaging::KL_NAMELENGTH, -}; - -use gpui::{ - KeybindingKeystroke, Keystroke, Modifiers, PlatformKeyboardLayout, PlatformKeyboardMapper, -}; - -pub(crate) struct WindowsKeyboardLayout { - id: String, - name: String, -} - -pub(crate) struct WindowsKeyboardMapper { - key_to_vkey: HashMap, - vkey_to_key: HashMap, - vkey_to_shifted: HashMap, -} - -impl PlatformKeyboardLayout for WindowsKeyboardLayout { - fn id(&self) -> &str { - &self.id - } - - fn name(&self) -> &str { - &self.name - } -} - -impl PlatformKeyboardMapper for WindowsKeyboardMapper { - fn map_key_equivalent( - &self, - mut keystroke: Keystroke, - use_key_equivalents: bool, - ) -> KeybindingKeystroke { - let Some((vkey, shifted_key)) = self.get_vkey_from_key(&keystroke.key, use_key_equivalents) - else { - return KeybindingKeystroke::from_keystroke(keystroke); - }; - if shifted_key && keystroke.modifiers.shift { - log::warn!( - "Keystroke '{}' has both shift and a shifted key, this is likely a bug", - keystroke.key - ); - } - - let shift = shifted_key || keystroke.modifiers.shift; - keystroke.modifiers.shift = false; - - let Some(key) = self.vkey_to_key.get(&vkey).cloned() else { - log::error!( - "Failed to map key equivalent '{:?}' to a valid key", - keystroke - ); - return KeybindingKeystroke::from_keystroke(keystroke); - }; - - keystroke.key = if shift { - let Some(shifted_key) = self.vkey_to_shifted.get(&vkey).cloned() else { - log::error!( - "Failed to map keystroke {:?} with virtual key '{:?}' to a shifted key", - keystroke, - vkey - ); - return KeybindingKeystroke::from_keystroke(keystroke); - }; - shifted_key - } else { - key.clone() - }; - - let modifiers = Modifiers { - shift, - ..keystroke.modifiers - }; - - KeybindingKeystroke::new(keystroke, modifiers, key) - } - - fn get_key_equivalents(&self) -> Option<&HashMap> { - None - } -} - -impl WindowsKeyboardLayout { - pub(crate) fn new() -> Result { - let mut buffer = [0u16; KL_NAMELENGTH as usize]; // KL_NAMELENGTH includes the null terminator - unsafe { GetKeyboardLayoutNameW(&mut buffer)? }; - let id = String::from_utf16_lossy(&buffer[..buffer.len() - 1]); // Remove the null terminator - let entry = windows_registry::LOCAL_MACHINE.open(format!( - "System\\CurrentControlSet\\Control\\Keyboard Layouts\\{id}" - ))?; - let name = entry.get_string("Layout Text")?; - Ok(Self { id, name }) - } - - pub(crate) fn unknown() -> Self { - Self { - id: "unknown".to_string(), - name: "unknown".to_string(), - } - } -} - -impl WindowsKeyboardMapper { - pub(crate) fn new() -> Self { - let mut key_to_vkey = HashMap::default(); - let mut vkey_to_key = HashMap::default(); - let mut vkey_to_shifted = HashMap::default(); - for vkey in CANDIDATE_VKEYS { - if let Some(key) = get_key_from_vkey(*vkey) { - key_to_vkey.insert(key.clone(), (vkey.0, false)); - vkey_to_key.insert(vkey.0, key); - } - let scan_code = unsafe { MapVirtualKeyW(vkey.0 as u32, MAPVK_VK_TO_VSC) }; - if scan_code == 0 { - continue; - } - if let Some(shifted_key) = get_shifted_key(*vkey, scan_code) { - key_to_vkey.insert(shifted_key.clone(), (vkey.0, true)); - vkey_to_shifted.insert(vkey.0, shifted_key); - } - } - Self { - key_to_vkey, - vkey_to_key, - vkey_to_shifted, - } - } - - fn get_vkey_from_key(&self, key: &str, use_key_equivalents: bool) -> Option<(u16, bool)> { - if use_key_equivalents { - get_vkey_from_key_with_us_layout(key) - } else { - self.key_to_vkey.get(key).cloned() - } - } -} - -pub(crate) fn get_keystroke_key( - vkey: VIRTUAL_KEY, - scan_code: u32, - modifiers: &mut Modifiers, -) -> Option { - if modifiers.shift && need_to_convert_to_shifted_key(vkey) { - get_shifted_key(vkey, scan_code).inspect(|_| { - modifiers.shift = false; - }) - } else { - get_key_from_vkey(vkey) - } -} - -fn get_key_from_vkey(vkey: VIRTUAL_KEY) -> Option { - let key_data = unsafe { MapVirtualKeyW(vkey.0 as u32, MAPVK_VK_TO_CHAR) }; - if key_data == 0 { - return None; - } - - // The high word contains dead key flag, the low word contains the character - let key = char::from_u32(key_data & 0xFFFF)?; - - Some(key.to_ascii_lowercase().to_string()) -} - -#[inline] -fn need_to_convert_to_shifted_key(vkey: VIRTUAL_KEY) -> bool { - matches!( - vkey, - VK_OEM_3 - | VK_OEM_MINUS - | VK_OEM_PLUS - | VK_OEM_4 - | VK_OEM_5 - | VK_OEM_6 - | VK_OEM_1 - | VK_OEM_7 - | VK_OEM_COMMA - | VK_OEM_PERIOD - | VK_OEM_2 - | VK_OEM_102 - | VK_OEM_8 - | VK_ABNT_C1 - | VK_0 - | VK_1 - | VK_2 - | VK_3 - | VK_4 - | VK_5 - | VK_6 - | VK_7 - | VK_8 - | VK_9 - ) -} - -fn get_shifted_key(vkey: VIRTUAL_KEY, scan_code: u32) -> Option { - generate_key_char(vkey, scan_code, false, true, false) -} - -pub(crate) fn generate_key_char( - vkey: VIRTUAL_KEY, - scan_code: u32, - control: bool, - shift: bool, - alt: bool, -) -> Option { - let mut state = [0; 256]; - if control { - state[VK_CONTROL.0 as usize] = 0x80; - } - if shift { - state[VK_SHIFT.0 as usize] = 0x80; - } - if alt { - state[VK_MENU.0 as usize] = 0x80; - } - - let mut buffer = [0; 8]; - let len = unsafe { ToUnicode(vkey.0 as u32, scan_code, Some(&state), &mut buffer, 0x5) }; - - match len { - len if len > 0 => String::from_utf16(&buffer[..len as usize]) - .ok() - .filter(|candidate| { - !candidate.is_empty() && !candidate.chars().next().unwrap().is_control() - }), - len if len < 0 => String::from_utf16(&buffer[..(-len as usize)]).ok(), - _ => None, - } -} - -fn get_vkey_from_key_with_us_layout(key: &str) -> Option<(u16, bool)> { - match key { - // ` => VK_OEM_3 - "`" => Some((VK_OEM_3.0, false)), - "~" => Some((VK_OEM_3.0, true)), - "1" => Some((VK_1.0, false)), - "!" => Some((VK_1.0, true)), - "2" => Some((VK_2.0, false)), - "@" => Some((VK_2.0, true)), - "3" => Some((VK_3.0, false)), - "#" => Some((VK_3.0, true)), - "4" => Some((VK_4.0, false)), - "$" => Some((VK_4.0, true)), - "5" => Some((VK_5.0, false)), - "%" => Some((VK_5.0, true)), - "6" => Some((VK_6.0, false)), - "^" => Some((VK_6.0, true)), - "7" => Some((VK_7.0, false)), - "&" => Some((VK_7.0, true)), - "8" => Some((VK_8.0, false)), - "*" => Some((VK_8.0, true)), - "9" => Some((VK_9.0, false)), - "(" => Some((VK_9.0, true)), - "0" => Some((VK_0.0, false)), - ")" => Some((VK_0.0, true)), - "-" => Some((VK_OEM_MINUS.0, false)), - "_" => Some((VK_OEM_MINUS.0, true)), - "=" => Some((VK_OEM_PLUS.0, false)), - "+" => Some((VK_OEM_PLUS.0, true)), - "[" => Some((VK_OEM_4.0, false)), - "{" => Some((VK_OEM_4.0, true)), - "]" => Some((VK_OEM_6.0, false)), - "}" => Some((VK_OEM_6.0, true)), - "\\" => Some((VK_OEM_5.0, false)), - "|" => Some((VK_OEM_5.0, true)), - ";" => Some((VK_OEM_1.0, false)), - ":" => Some((VK_OEM_1.0, true)), - "'" => Some((VK_OEM_7.0, false)), - "\"" => Some((VK_OEM_7.0, true)), - "," => Some((VK_OEM_COMMA.0, false)), - "<" => Some((VK_OEM_COMMA.0, true)), - "." => Some((VK_OEM_PERIOD.0, false)), - ">" => Some((VK_OEM_PERIOD.0, true)), - "/" => Some((VK_OEM_2.0, false)), - "?" => Some((VK_OEM_2.0, true)), - _ => None, - } -} - -const CANDIDATE_VKEYS: &[VIRTUAL_KEY] = &[ - VK_OEM_3, - VK_OEM_MINUS, - VK_OEM_PLUS, - VK_OEM_4, - VK_OEM_5, - VK_OEM_6, - VK_OEM_1, - VK_OEM_7, - VK_OEM_COMMA, - VK_OEM_PERIOD, - VK_OEM_2, - VK_OEM_102, - VK_OEM_8, - VK_ABNT_C1, - VK_0, - VK_1, - VK_2, - VK_3, - VK_4, - VK_5, - VK_6, - VK_7, - VK_8, - VK_9, -]; - -#[cfg(test)] -mod tests { - use crate::WindowsKeyboardMapper; - use gpui::{Keystroke, Modifiers, PlatformKeyboardMapper}; - - #[test] - fn test_keyboard_mapper() { - let mapper = WindowsKeyboardMapper::new(); - - // Normal case - let keystroke = Keystroke { - modifiers: Modifiers::control(), - key: "a".to_string(), - key_char: None, - }; - let mapped = mapper.map_key_equivalent(keystroke.clone(), true); - assert_eq!(*mapped.inner(), keystroke); - assert_eq!(mapped.key(), "a"); - assert_eq!(*mapped.modifiers(), Modifiers::control()); - - // Shifted case, ctrl-$ - let keystroke = Keystroke { - modifiers: Modifiers::control(), - key: "$".to_string(), - key_char: None, - }; - let mapped = mapper.map_key_equivalent(keystroke.clone(), true); - assert_eq!(*mapped.inner(), keystroke); - assert_eq!(mapped.key(), "4"); - assert_eq!(*mapped.modifiers(), Modifiers::control_shift()); - - // Shifted case, but shift is true - let keystroke = Keystroke { - modifiers: Modifiers::control_shift(), - key: "$".to_string(), - key_char: None, - }; - let mapped = mapper.map_key_equivalent(keystroke, true); - assert_eq!(mapped.inner().modifiers, Modifiers::control()); - assert_eq!(mapped.key(), "4"); - assert_eq!(*mapped.modifiers(), Modifiers::control_shift()); - - // Windows style - let keystroke = Keystroke { - modifiers: Modifiers::control_shift(), - key: "4".to_string(), - key_char: None, - }; - let mapped = mapper.map_key_equivalent(keystroke, true); - assert_eq!(mapped.inner().modifiers, Modifiers::control()); - assert_eq!(mapped.inner().key, "$"); - assert_eq!(mapped.key(), "4"); - assert_eq!(*mapped.modifiers(), Modifiers::control_shift()); - } -} diff --git a/crates/gpui_pre_windows/src/platform.rs b/crates/gpui_pre_windows/src/platform.rs deleted file mode 100644 index d0ae3c76..00000000 --- a/crates/gpui_pre_windows/src/platform.rs +++ /dev/null @@ -1,1605 +0,0 @@ -use std::{ - cell::{Cell, RefCell}, - ffi::{OsStr, OsString}, - os::windows::ffi::{OsStrExt as _, OsStringExt as _}, - path::{Path, PathBuf}, - rc::{Rc, Weak}, - sync::{ - Arc, - atomic::{AtomicBool, Ordering}, - }, -}; - -use anyhow::{Context as _, Result, anyhow}; -use futures::channel::oneshot::{self, Receiver}; -use gpui_util::{ResultExt, get_powershell, new_std_command}; -use itertools::Itertools; -use parking_lot::RwLock; -use smallvec::SmallVec; -use windows::{ - UI::ViewManagement::UISettings, - Win32::{ - Foundation::*, - Graphics::{Direct3D11::ID3D11Device, Gdi::*}, - Security::Credentials::*, - System::{Com::*, LibraryLoader::*, Ole::*, Power::*, SystemInformation::*}, - UI::{Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*}, - }, - core::*, -}; - -use crate::*; -use gpui::*; - -pub struct WindowsPlatform { - inner: Rc, - raw_window_handles: Arc>>, - // The below members will never change throughout the entire lifecycle of the app. - headless: bool, - icon: HICON, - background_executor: BackgroundExecutor, - foreground_executor: ForegroundExecutor, - text_system: Arc, - direct_write_text_system: Option>, - drop_target_helper: Option, - /// Flag to instruct the `VSyncProvider` thread to invalidate the directx devices - /// as resizing them has failed, causing us to have lost at least the render target. - invalidate_devices: Arc, - handle: HWND, - suspend_resume_notification: RefCell>, - disable_direct_composition: bool, - has_package_identity: bool, - app_identity: RefCell>, - system_notifications: RefCell, -} - -struct WindowsPlatformInner { - state: WindowsPlatformState, - raw_window_handles: std::sync::Weak>>, - // The below members will never change throughout the entire lifecycle of the app. - validation_number: usize, - main_receiver: PriorityQueueReceiver, - dispatcher: Arc, -} - -pub(crate) struct WindowsPlatformState { - callbacks: PlatformCallbacks, - menus: RefCell>, - jump_list: RefCell, - // NOTE: standard cursor handles don't need to close. - pub(crate) current_cursor: Cell>, - /// Shared with each window so `WM_SETCURSOR` can read it directly. - pub(crate) cursor_visible: Arc, - /// Shared with each window to coordinate draws across windows on the UI - /// thread; see [`DrawCoordinator`]. - pub(crate) draw_coordinator: Rc, - directx_devices: RefCell>, -} - -#[derive(Default)] -struct PlatformCallbacks { - open_urls: Cell)>>>, - quit: Cell bool>>>, - reopen: Cell>>, - app_menu_action: Cell>>, - will_open_app_menu: Cell>>, - validate_app_menu_command: Cell bool>>>, - keyboard_layout_change: Cell>>, - system_wake: Cell>>, -} - -impl WindowsPlatformState { - fn new(directx_devices: Option) -> Self { - let callbacks = PlatformCallbacks::default(); - let jump_list = JumpList::new(); - let current_cursor = load_cursor(CursorStyle::Arrow); - - Self { - callbacks, - jump_list: RefCell::new(jump_list), - current_cursor: Cell::new(current_cursor), - cursor_visible: Arc::new(AtomicBool::new(true)), - draw_coordinator: Rc::new(DrawCoordinator::new()), - directx_devices: RefCell::new(directx_devices), - menus: RefCell::new(Vec::new()), - } - } -} - -impl WindowsPlatform { - pub fn new(headless: bool) -> Result { - unsafe { - OleInitialize(None).context("unable to initialize Windows OLE")?; - } - let (directx_devices, text_system, direct_write_text_system) = if !headless { - let devices = DirectXDevices::new().context("Creating DirectX devices")?; - let dw_text_system = Arc::new( - DirectWriteTextSystem::new(&devices) - .context("Error creating DirectWriteTextSystem")?, - ); - ( - Some(devices), - dw_text_system.clone() as Arc, - Some(dw_text_system), - ) - } else { - ( - None, - Arc::new(gpui::NoopTextSystem::new()) as Arc, - None, - ) - }; - - let (main_sender, main_receiver) = PriorityQueueReceiver::new(); - let validation_number = if usize::BITS == 64 { - rand::random::() as usize - } else { - rand::random::() as usize - }; - let raw_window_handles = Arc::new(RwLock::new(SmallVec::new())); - - register_platform_window_class(); - let mut context = PlatformWindowCreateContext { - inner: None, - raw_window_handles: Arc::downgrade(&raw_window_handles), - validation_number, - main_sender: Some(main_sender), - main_receiver: Some(main_receiver), - directx_devices, - dispatcher: None, - }; - let result = unsafe { - CreateWindowExW( - WINDOW_EX_STYLE(0), - PLATFORM_WINDOW_CLASS_NAME, - None, - WINDOW_STYLE(0), - 0, - 0, - 0, - 0, - Some(HWND_MESSAGE), - None, - None, - Some(&raw const context as *const _), - ) - }; - let inner = context - .inner - .take() - .context("CreateWindowExW did not run correctly")??; - let dispatcher = context - .dispatcher - .take() - .context("CreateWindowExW did not run correctly")?; - let handle = result?; - - let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION) - .is_ok_and(|value| value == "true" || value == "1"); - let background_executor = BackgroundExecutor::new(dispatcher.clone()); - let foreground_executor = ForegroundExecutor::new(dispatcher); - - let drop_target_helper: Option = if !headless { - Some(unsafe { - CoCreateInstance(&CLSID_DragDropHelper, None, CLSCTX_INPROC_SERVER) - .context("Error creating drop target helper.")? - }) - } else { - None - }; - let icon = if !headless { - load_icon().unwrap_or_default() - } else { - HICON::default() - }; - - Ok(Self { - inner, - handle, - raw_window_handles, - headless, - icon, - background_executor, - foreground_executor, - text_system, - direct_write_text_system, - suspend_resume_notification: RefCell::new(None), - disable_direct_composition, - has_package_identity: has_package_identity(), - drop_target_helper, - invalidate_devices: Arc::new(AtomicBool::new(false)), - app_identity: RefCell::new(None), - system_notifications: RefCell::new(SystemNotificationState::new()), - }) - } - - pub(crate) fn window_from_hwnd(&self, hwnd: HWND) -> Option> { - self.raw_window_handles - .read() - .iter() - .find(|entry| entry.as_raw() == hwnd) - .and_then(|hwnd| window_from_hwnd(hwnd.as_raw())) - } - - #[inline] - fn post_message(&self, message: u32, wparam: WPARAM, lparam: LPARAM) { - self.raw_window_handles - .read() - .iter() - .for_each(|handle| unsafe { - PostMessageW(Some(handle.as_raw()), message, wparam, lparam).log_err(); - }); - } - - fn generate_creation_info(&self) -> WindowCreationInfo { - WindowCreationInfo { - icon: self.icon, - executor: self.foreground_executor.clone(), - current_cursor: self.inner.state.current_cursor.get(), - cursor_visible: self.inner.state.cursor_visible.clone(), - drop_target_helper: self.drop_target_helper.clone().unwrap(), - validation_number: self.inner.validation_number, - main_receiver: self.inner.main_receiver.clone(), - platform_window_handle: self.handle, - disable_direct_composition: self.disable_direct_composition, - directx_devices: self.inner.state.directx_devices.borrow().clone().unwrap(), - invalidate_devices: self.invalidate_devices.clone(), - draw_coordinator: self.inner.state.draw_coordinator.clone(), - } - } - - fn set_dock_menus(&self, menus: Vec) { - let mut actions = Vec::new(); - menus.into_iter().for_each(|menu| { - if let Some(dock_menu) = DockMenuItem::new(menu).log_err() { - actions.push(dock_menu); - } - }); - self.inner.state.jump_list.borrow_mut().dock_menus = actions; - let borrow = self.inner.state.jump_list.borrow(); - let dock_menus = borrow - .dock_menus - .iter() - .map(|menu| (menu.name.clone(), menu.description.clone())) - .collect::>(); - let recent_workspaces = borrow.recent_workspaces.clone(); - self.background_executor - .spawn(async move { - update_jump_list(&recent_workspaces, &dock_menus).log_err(); - }) - .detach(); - } - - fn update_jump_list( - &self, - menus: Vec, - entries: Vec>, - ) -> Task>> { - let mut actions = Vec::new(); - menus.into_iter().for_each(|menu| { - if let Some(dock_menu) = DockMenuItem::new(menu).log_err() { - actions.push(dock_menu); - } - }); - let mut jump_list = self.inner.state.jump_list.borrow_mut(); - jump_list.dock_menus = actions; - jump_list.recent_workspaces = entries.into(); - let dock_menus = jump_list - .dock_menus - .iter() - .map(|menu| (menu.name.clone(), menu.description.clone())) - .collect::>(); - let recent_workspaces = jump_list.recent_workspaces.clone(); - self.background_executor.spawn(async move { - update_jump_list(&recent_workspaces, &dock_menus) - .log_err() - .unwrap_or_default() - }) - } - - fn find_current_active_window(&self) -> Option { - let active_window_hwnd = unsafe { GetActiveWindow() }; - if active_window_hwnd.is_invalid() { - return None; - } - self.raw_window_handles - .read() - .iter() - .find(|hwnd| hwnd.as_raw() == active_window_hwnd) - .map(|hwnd| hwnd.as_raw()) - } - - fn begin_vsync_thread(&self) { - let Some(directx_devices) = self.inner.state.directx_devices.borrow().clone() else { - return; - }; - let Some(direct_write_text_system) = &self.direct_write_text_system else { - return; - }; - let mut directx_device = directx_devices; - let platform_window: SafeHwnd = self.handle.into(); - let validation_number = self.inner.validation_number; - let all_windows = Arc::downgrade(&self.raw_window_handles); - let text_system = Arc::downgrade(direct_write_text_system); - let invalidate_devices = self.invalidate_devices.clone(); - - std::thread::Builder::new() - .name("VSyncProvider".to_owned()) - .spawn(move || { - let vsync_provider = VSyncProvider::new(); - loop { - vsync_provider.wait_for_vsync(); - if check_device_lost(&directx_device.device) - || invalidate_devices.fetch_and(false, Ordering::Acquire) - { - if let Err(err) = handle_gpu_device_lost( - &mut directx_device, - platform_window.as_raw(), - validation_number, - &all_windows, - &text_system, - ) { - panic!("Device lost: {err}"); - } - } - let Some(all_windows) = all_windows.upgrade() else { - break; - }; - for hwnd in all_windows.read().iter() { - unsafe { - let _ = RedrawWindow(Some(hwnd.as_raw()), None, None, RDW_INVALIDATE); - } - } - } - }) - .unwrap(); - } -} - -fn translate_accelerator(msg: &MSG) -> Option<()> { - if msg.message != WM_KEYDOWN && msg.message != WM_SYSKEYDOWN { - return None; - } - - let result = unsafe { - SendMessageW( - msg.hwnd, - WM_GPUI_KEYDOWN, - Some(msg.wParam), - Some(msg.lParam), - ) - }; - (result.0 == 0).then_some(()) -} - -fn encode_restart_arguments(arguments: &[OsString]) -> OsString { - // `Start-Process` accepts a single native command line, so quote each argument according to - // the Windows argv parsing rules before passing the complete string through the environment. - let mut encoded = Vec::new(); - - for (index, argument) in arguments.iter().enumerate() { - if index > 0 { - encoded.push(b' ' as u16); - } - encoded.push(b'"' as u16); - - let mut backslash_count = 0; - for code_unit in argument.encode_wide() { - if code_unit == b'\\' as u16 { - backslash_count += 1; - } else { - if code_unit == b'"' as u16 { - encoded.extend(std::iter::repeat_n(b'\\' as u16, backslash_count * 2 + 1)); - } else { - encoded.extend(std::iter::repeat_n(b'\\' as u16, backslash_count)); - } - backslash_count = 0; - encoded.push(code_unit); - } - } - - encoded.extend(std::iter::repeat_n(b'\\' as u16, backslash_count * 2)); - encoded.push(b'"' as u16); - } - - OsString::from_wide(&encoded) -} - -impl Platform for WindowsPlatform { - fn background_executor(&self) -> BackgroundExecutor { - self.background_executor.clone() - } - - fn foreground_executor(&self) -> ForegroundExecutor { - self.foreground_executor.clone() - } - - fn text_system(&self) -> Arc { - self.text_system.clone() - } - - fn keyboard_layout(&self) -> Box { - Box::new( - WindowsKeyboardLayout::new() - .log_err() - .unwrap_or(WindowsKeyboardLayout::unknown()), - ) - } - - fn keyboard_mapper(&self) -> Rc { - Rc::new(WindowsKeyboardMapper::new()) - } - - fn on_keyboard_layout_change(&self, callback: Box) { - self.inner - .state - .callbacks - .keyboard_layout_change - .set(Some(callback)); - } - - fn on_thermal_state_change(&self, _callback: Box) {} - - fn thermal_state(&self) -> ThermalState { - ThermalState::Nominal - } - - fn run(&self, on_finish_launching: Box) { - on_finish_launching(); - if !self.headless { - self.begin_vsync_thread(); - } - - let mut msg = MSG::default(); - unsafe { - while GetMessageW(&mut msg, None, 0, 0).as_bool() { - if translate_accelerator(&msg).is_none() { - _ = TranslateMessage(&msg); - DispatchMessageW(&msg); - } - } - } - - self.inner.with_callback( - |callbacks| &callbacks.quit, - |callback| { - callback(); - }, - ); - } - - fn quit(&self) { - self.foreground_executor() - .spawn(async { unsafe { PostQuitMessage(0) } }) - .detach(); - } - - fn restart(&self, binary_path: Option, arguments: Vec) { - let pid = std::process::id(); - let Some(app_path) = binary_path.or(self.app_path().log_err()) else { - return; - }; - let script = r#" - $pidToWaitFor = $env:ZED_RESTART_PID - $exePath = $env:ZED_RESTART_EXECUTABLE - $argumentList = $env:ZED_RESTART_ARGUMENTS - - [Environment]::SetEnvironmentVariable("ZED_RESTART_PID", $null) - [Environment]::SetEnvironmentVariable("ZED_RESTART_EXECUTABLE", $null) - [Environment]::SetEnvironmentVariable("ZED_RESTART_ARGUMENTS", $null) - - while ($true) { - $process = Get-Process -Id $pidToWaitFor -ErrorAction SilentlyContinue - if (-not $process) { - if ([string]::IsNullOrEmpty($argumentList)) { - Start-Process -FilePath $exePath - } else { - Start-Process -FilePath $exePath -ArgumentList $argumentList - } - break - } - Start-Sleep -Seconds 0.1 - } - "#; - - // Defer spawning to the foreground executor so it runs after the - // current `AppCell` borrow is released. On Windows, `Command::spawn()` - // can pump the Win32 message loop (via `CreateProcessW`), which - // re-enters message handling possibly resulting in another mutable - // borrow of the `AppCell` ending up with a double borrow panic - let Some(powershell) = get_powershell() else { - log::error!("failed to restart: PowerShell is unavailable"); - return; - }; - self.foreground_executor - .spawn(async move { - let mut command = new_std_command(powershell); - let arguments = encode_restart_arguments(&arguments); - command - .arg("-command") - .arg(script) - .env("ZED_RESTART_PID", pid.to_string()) - .env("ZED_RESTART_EXECUTABLE", app_path) - .env("ZED_RESTART_ARGUMENTS", arguments); - #[allow( - clippy::disallowed_methods, - reason = "We are restarting ourselves, using std command thus is fine" - )] - let restart_process = command.spawn(); - - match restart_process { - Ok(_) => unsafe { PostQuitMessage(0) }, - Err(e) => log::error!("failed to spawn restart script: {:?}", e), - } - }) - .detach(); - } - - fn activate(&self, _ignoring_other_apps: bool) {} - - fn hide(&self) {} - - // todo(windows) - fn hide_other_apps(&self) { - unimplemented!() - } - - // todo(windows) - fn unhide_other_apps(&self) { - unimplemented!() - } - - fn displays(&self) -> Vec> { - WindowsDisplay::displays() - } - - fn primary_display(&self) -> Option> { - WindowsDisplay::primary_monitor().map(|display| Rc::new(display) as Rc) - } - - #[cfg(feature = "screen-capture")] - fn is_screen_capture_supported(&self) -> bool { - true - } - - #[cfg(feature = "screen-capture")] - fn screen_capture_sources( - &self, - ) -> oneshot::Receiver>>> { - gpui::scap_screen_capture::scap_screen_sources(&self.foreground_executor) - } - - fn active_window(&self) -> Option { - let active_window_hwnd = unsafe { GetActiveWindow() }; - self.window_from_hwnd(active_window_hwnd) - .map(|inner| inner.handle) - } - - fn open_window( - &self, - handle: AnyWindowHandle, - options: WindowParams, - ) -> Result> { - let window = WindowsWindow::new(handle, options, self.generate_creation_info())?; - let handle = window.get_raw_handle(); - self.raw_window_handles.write().push(handle.into()); - - Ok(Box::new(window)) - } - - fn window_appearance(&self) -> WindowAppearance { - system_appearance().log_err().unwrap_or_default() - } - - fn open_url(&self, url: &str) { - if url.is_empty() { - return; - } - let url_string = url.to_string(); - self.background_executor() - .spawn(async move { - open_target(&url_string) - .with_context(|| format!("Opening url: {}", url_string)) - .log_err(); - }) - .detach(); - } - - fn on_open_urls(&self, callback: Box)>) { - self.inner.state.callbacks.open_urls.set(Some(callback)); - } - - fn prompt_for_paths( - &self, - options: PathPromptOptions, - ) -> Receiver>>> { - let (tx, rx) = oneshot::channel(); - let window = self.find_current_active_window(); - self.foreground_executor() - .spawn(async move { - let _ = tx.send(file_open_dialog(options, window)); - }) - .detach(); - - rx - } - - fn prompt_for_new_path( - &self, - directory: &Path, - suggested_name: Option<&str>, - ) -> Receiver>> { - let directory = directory.to_owned(); - let suggested_name = suggested_name.map(|s| s.to_owned()); - let (tx, rx) = oneshot::channel(); - let window = self.find_current_active_window(); - self.foreground_executor() - .spawn(async move { - let _ = tx.send(file_save_dialog(directory, suggested_name, window)); - }) - .detach(); - - rx - } - - fn can_select_mixed_files_and_dirs(&self) -> bool { - // The FOS_PICKFOLDERS flag toggles between "only files" and "only folders". - false - } - - fn reveal_path(&self, path: &Path) { - if path.as_os_str().is_empty() { - return; - } - let path = path.to_path_buf(); - self.background_executor() - .spawn(async move { - open_target_in_explorer(&path) - .with_context(|| format!("Revealing path {} in explorer", path.display())) - .log_err(); - }) - .detach(); - } - - fn open_with_system(&self, path: &Path) { - if path.as_os_str().is_empty() { - return; - } - let path = path.to_path_buf(); - self.background_executor() - .spawn(async move { - open_target(&path) - .with_context(|| format!("Opening {} with system", path.display())) - .log_err(); - }) - .detach(); - } - - fn on_quit(&self, callback: Box bool>) { - self.inner.state.callbacks.quit.set(Some(callback)); - } - - fn on_reopen(&self, callback: Box) { - self.inner.state.callbacks.reopen.set(Some(callback)); - } - - fn on_system_wake(&self, callback: Box) { - self.inner.state.callbacks.system_wake.set(Some(callback)); - let mut notification = self.suspend_resume_notification.borrow_mut(); - if notification.is_none() { - *notification = unsafe { - // SAFETY: self.handle is the platform window receiving WM_POWERBROADCAST. - RegisterSuspendResumeNotification( - HANDLE(self.handle.0), - DEVICE_NOTIFY_WINDOW_HANDLE, - ) - .log_err() - }; - } - } - - fn set_app_identity(&self, identifier: &str, name: &str) { - // If the process has package identity, it's automatally granted an AUMID by the system. - if self.has_package_identity { - return; - } - - let identifier_utf16 = windows::core::HSTRING::from(identifier); - // SAFETY: `identifier_utf16` outlives the call and is null-terminated. - if let Err(error) = unsafe { - windows::Win32::UI::Shell::SetCurrentProcessExplicitAppUserModelID( - windows::core::PCWSTR(identifier_utf16.as_ptr()), - ) - } { - log::warn!("failed to set the process AppUserModelID: {error}"); - } - *self.app_identity.borrow_mut() = Some((identifier.to_string(), name.to_string())); - } - - fn show_system_notification(&self, notification: gpui::SystemNotification) { - let app_identity = self.app_identity.borrow().clone(); - self.system_notifications - .borrow_mut() - .show( - self.has_package_identity, - app_identity - .as_ref() - .map(|(identifier, name)| (identifier.as_str(), name.as_str())), - notification, - ) - .log_err(); - } - - fn dismiss_system_notification(&self, tag: &str) { - self.system_notifications.borrow_mut().dismiss(tag); - } - - fn on_system_notification_response( - &self, - callback: Box, - ) { - self.system_notifications - .borrow_mut() - .on_response(&self.foreground_executor, callback); - } - - fn set_menus(&self, menus: Vec

, - /// One-based scene clip-chain index. Zero uses the inline bounds and radii. - pub clip_index: u32, - /// Explicit GPU record padding. - pub clip_padding: u32, -} - -impl ContentMask { - /// Scale the content mask's pixel units by the given scaling factor. - pub fn scale(&self, factor: f32) -> ContentMask { - ContentMask { - bounds: self.bounds.scale(factor), - corner_radii: self.corner_radii.scale(factor), - clip_index: self.clip_index, - clip_padding: 0, - } - } - - /// Intersect without changing either mask's rounded geometry. - pub fn intersect(&self, other: &Self) -> crate::ClipRegion { - crate::ClipRegion::from(*self).intersect(&crate::ClipRegion::from(*other)) - } -} - -impl Window { - fn mark_view_dirty(&mut self, view_id: EntityId) { - // Mark ancestor views as dirty. If already in the `dirty_views` set, then all its ancestors - // should already be dirty. - for view_id in self - .rendered_frame - .dispatch_tree - .view_path_reversed(view_id) - { - if !self.dirty_views.insert(view_id) { - break; - } - } - } - - /// Registers a callback to be invoked when the window appearance changes. - pub fn observe_window_appearance( - &self, - mut callback: impl FnMut(&mut Window, &mut App) + 'static, - ) -> Subscription { - let (subscription, activate) = self.appearance_observers.insert( - (), - Box::new(move |window, cx| { - callback(window, cx); - true - }), - ); - activate(); - subscription - } - - /// Registers a callback to be invoked when the window button layout changes. - pub fn observe_button_layout_changed( - &self, - mut callback: impl FnMut(&mut Window, &mut App) + 'static, - ) -> Subscription { - let (subscription, activate) = self.button_layout_observers.insert( - (), - Box::new(move |window, cx| { - callback(window, cx); - true - }), - ); - activate(); - subscription - } - - /// Replaces the root entity of the window with a new one. - pub fn replace_root( - &mut self, - cx: &mut App, - build_view: impl FnOnce(&mut Window, &mut Context) -> E, - ) -> Entity - where - E: 'static + Render, - { - let view = cx.new(|cx| build_view(self, cx)); - self.root = Some(view.clone().into()); - self.refresh(); - view - } - - /// Returns the root entity of the window, if it has one. - pub fn root(&self) -> Option>> - where - E: 'static + Render, - { - self.root - .as_ref() - .map(|view| view.clone().downcast::().ok()) - } - - /// Obtain a handle to the window that belongs to this context. - pub fn window_handle(&self) -> AnyWindowHandle { - self.handle - } - - /// Mark the window as dirty, scheduling it to be redrawn on the next frame. - pub fn refresh(&mut self) { - if self.invalidator.not_drawing() { - self.refreshing = true; - self.invalidator.set_dirty(true); - } - } - - /// Close this window. - pub fn remove_window(&mut self) { - self.removed = true; - } - - /// Obtain the currently focused [`FocusHandle`]. If no elements are focused, returns `None`. - pub fn focused(&self, cx: &App) -> Option { - self.focus - .and_then(|id| FocusHandle::for_id(id, &cx.focus_handles)) - } - - /// While focus-lost listeners are being dispatched, returns the closest ancestor of the - /// previously focused element that can still receive focus, making it a suitable target - /// for focus restoration. Returns `None` at all other times, or when no such ancestor exists. - pub fn focus_lost_restore_target(&self, cx: &App) -> Option { - let (_leaf, ancestors) = self.focus_lost_path.split_last()?; - ancestors.iter().rev().find_map(|id| { - self.rendered_frame.dispatch_tree.focusable_node_id(*id)?; - FocusHandle::for_id(*id, &cx.focus_handles) - }) - } - - /// Move focus to the element associated with the given [`FocusHandle`]. - pub fn focus(&mut self, handle: &FocusHandle, cx: &mut App) { - if !self.focus_enabled || self.focus == Some(handle.id) { - return; - } - - self.focus = Some(handle.id); - self.focus_generation = self.focus_generation.wrapping_add(1); - self.clear_pending_keystrokes(cx); - - self.refresh(); - } - - /// Remove focus from all elements within this context's window. - pub fn blur(&mut self, cx: &mut App) { - self.clear_pending_keystrokes(cx); - - if !self.focus_enabled { - return; - } - - if self.focus.is_some() { - self.focus_generation = self.focus_generation.wrapping_add(1); - } - self.focus = None; - self.refresh(); - } - - /// Blur the window and don't allow anything in it to be focused again. - pub fn disable_focus(&mut self, cx: &mut App) { - self.blur(cx); - self.focus_enabled = false; - } - - /// Move focus to next tab stop. - pub fn focus_next(&mut self, cx: &mut App) { - if !self.focus_enabled { - return; - } - - if let Some(handle) = self.rendered_frame.tab_stops.next(self.focus.as_ref()) { - self.focus(&handle, cx) - } - } - - /// Move focus to previous tab stop. - pub fn focus_prev(&mut self, cx: &mut App) { - if !self.focus_enabled { - return; - } - - if let Some(handle) = self.rendered_frame.tab_stops.prev(self.focus.as_ref()) { - self.focus(&handle, cx) - } - } - - /// Accessor for the text system. - pub fn text_system(&self) -> &Arc { - &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 if the platform window is maximized. - /// - /// On some platforms (namely Windows) this is different than the bounds being the size of the display - pub fn is_maximized(&self) -> bool { - self.platform_window.is_maximized() - } - - /// request a certain window decoration (Wayland) - pub fn request_decorations(&self, decorations: WindowDecorations) { - self.platform_window.request_decorations(decorations); - } - - /// Set the exclusive zone for a layer-shell surface: how much screen space it - /// reserves so other surfaces avoid occluding it (e.g. a panel reserving space). - /// Positive values reserve that distance from the anchored edge, 0 lets the - /// surface be moved out of others' exclusive zones, and -1 ignores reserved - /// space and may extend under other surfaces. (Wayland layer-shell windows only) - pub fn set_exclusive_zone(&self, zone: Pixels) { - self.platform_window.set_exclusive_zone(zone); - } - - /// Set which anchored edge a layer-shell surface's exclusive zone applies to. - /// This is only needed to disambiguate a corner-anchored surface; otherwise the - /// edge is deduced from the anchor. The edge must be a single edge the surface - /// is anchored to, or it is ignored. (Wayland layer-shell windows only) - #[cfg(all(target_os = "linux", feature = "wayland"))] - pub fn set_exclusive_edge(&self, edge: crate::layer_shell::Anchor) { - self.platform_window.set_exclusive_edge(edge); - } - - /// Start an interactive window resize operation if this window is resizable. - pub fn start_window_resize(&self, edge: ResizeEdge) { - if self.is_resizable { - self.platform_window.start_window_resize(edge); - } - } - - /// Linux (wayland) only: Set the window's input region, the area that receives pointer - /// and touch input. Events outside it pass through to whatever is below the window. - /// - /// - `Some(rects)` restricts input to the union of `rects`, in window coordinates. - /// - `Some(&[])` is an empty region, so the window receives no pointer or touch input. - /// - `None` resets the region to the default, so the whole window receives input again. - pub fn set_input_region(&self, region: Option<&[Bounds]>) { - self.platform_window.set_input_region(region); - } - - /// Return the `WindowBounds` to indicate that how a window should be opened - /// after it has been closed - pub fn window_bounds(&self) -> WindowBounds { - self.platform_window.window_bounds() - } - - /// Return the `WindowBounds` excluding insets (Wayland and X11) - pub fn inner_window_bounds(&self) -> WindowBounds { - self.platform_window.inner_window_bounds() - } - - /// Dispatch the given action on the currently focused element. - pub fn dispatch_action(&mut self, action: Box, cx: &mut App) { - let focus_id = self.focused(cx).map(|handle| handle.id); - - let window = self.handle; - cx.defer(move |cx| { - window - .update(cx, |_, window, cx| { - let node_id = window.focus_node_id_in_rendered_frame(focus_id); - window.dispatch_action_on_node(node_id, action.as_ref(), cx); - }) - .log_err(); - }) - } - - pub(crate) fn dispatch_keystroke_observers( - &mut self, - event: &dyn Any, - action: Option>, - context_stack: Vec, - cx: &mut App, - ) { - let Some(key_down_event) = event.downcast_ref::() else { - return; - }; - - cx.keystroke_observers.clone().retain(&(), move |callback| { - (callback)( - &KeystrokeEvent { - keystroke: key_down_event.keystroke.clone(), - action: action.as_ref().map(|action| action.boxed_clone()), - context_stack: context_stack.clone(), - }, - self, - cx, - ) - }); - } - - pub(crate) fn dispatch_keystroke_interceptors( - &mut self, - event: &dyn Any, - context_stack: Vec, - cx: &mut App, - ) { - let Some(key_down_event) = event.downcast_ref::() else { - return; - }; - - cx.keystroke_interceptors - .clone() - .retain(&(), move |callback| { - (callback)( - &KeystrokeEvent { - keystroke: key_down_event.keystroke.clone(), - action: None, - context_stack: context_stack.clone(), - }, - self, - cx, - ) - }); - } - - /// 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(&self, cx: &mut App, f: impl FnOnce(&mut Window, &mut App) + 'static) { - let handle = self.handle; - cx.defer(move |cx| { - handle.update(cx, |_, window, cx| f(window, cx)).ok(); - }); - } - - /// Subscribe to events emitted by a entity. - /// The entity to which you're subscribing must implement the [`EventEmitter`] trait. - /// The callback will be invoked a handle to the emitting entity, the event, and a window context for the current window. - pub fn observe( - &mut self, - observed: &Entity, - cx: &mut App, - mut on_notify: impl FnMut(Entity, &mut Window, &mut App) + 'static, - ) -> Subscription { - let entity_id = observed.entity_id(); - let observed = observed.downgrade(); - let window_handle = self.handle; - cx.new_observer( - entity_id, - Box::new(move |cx| { - window_handle - .update(cx, |_, window, cx| { - if let Some(handle) = observed.upgrade() { - on_notify(handle, window, cx); - true - } else { - false - } - }) - .unwrap_or(false) - }), - ) - } - - /// Subscribe to events emitted by a entity. - /// The entity to which you're subscribing must implement the [`EventEmitter`] trait. - /// The callback will be invoked a handle to the emitting entity, the event, and a window context for the current window. - pub fn subscribe( - &mut self, - entity: &Entity, - cx: &mut App, - mut on_event: impl FnMut(Entity, &Evt, &mut Window, &mut App) + 'static, - ) -> Subscription - where - Emitter: EventEmitter, - Evt: 'static, - { - let entity_id = entity.entity_id(); - let handle = entity.downgrade(); - let window_handle = self.handle; - cx.new_subscription( - entity_id, - ( - TypeId::of::(), - Box::new(move |event, cx| { - window_handle - .update(cx, |_, window, cx| { - if let Some(entity) = handle.upgrade() { - let event = event.downcast_ref().expect("invalid event type"); - on_event(entity, event, window, cx); - true - } else { - false - } - }) - .unwrap_or(false) - }), - ), - ) - } - - /// Register a callback to be invoked when the given `Entity` is released. - pub fn observe_release( - &self, - entity: &Entity, - cx: &mut App, - mut on_release: impl FnOnce(&mut T, &mut Window, &mut App) + 'static, - ) -> Subscription - where - T: 'static, - { - let entity_id = entity.entity_id(); - let window_handle = self.handle; - let (subscription, activate) = cx.release_listeners.insert( - entity_id, - Box::new(move |entity, cx| { - let entity = entity.downcast_mut().expect("invalid entity type"); - let _ = window_handle.update(cx, |_, window, cx| on_release(entity, window, cx)); - }), - ); - activate(); - subscription - } - - /// Creates an [`AsyncWindowContext`], which has a static lifetime and can be held across - /// await points in async code. - pub fn to_async(&self, cx: &App) -> AsyncWindowContext { - AsyncWindowContext::new_context(cx.to_async(), self.handle) - } - - /// Schedule the given closure to be run directly after the current frame is rendered. - pub fn on_next_frame(&self, callback: impl FnOnce(&mut Window, &mut App) + 'static) { - RefCell::borrow_mut(&self.next_frame_callbacks).push(Box::new(callback)); - self.platform_window.schedule_frame(); - // Next-frame callbacks create frame demand without dirtying the - // window, so the platform's frame source must be woken explicitly. - self.invalidator.wake_platform(); - } - - /// Schedule a frame to be drawn on the next animation frame. - /// - /// This is useful for elements that need to animate continuously, such as a video player or an animated GIF. - /// It will cause the window to redraw on the next frame, even if no other changes have occurred. - /// - /// If called from within a view, it will notify that view on the next frame. Otherwise, it will refresh the entire window. - /// - /// Callers driving purely decorative animations (spinners, pulses, and the - /// like) should prefer [`AnimationExt::with_animation`](crate::AnimationExt::with_animation), - /// which automatically respects [`App::reduce_motion`]. When using this - /// method directly for decorative motion, check [`App::reduce_motion`] - /// and skip the frame request when it is set. - pub fn request_animation_frame(&self) { - let entity = self.current_view(); - self.on_next_frame(move |_, cx| cx.notify(entity)); - } - - /// Runs all callbacks scheduled via [`Self::on_next_frame`], returning how many ran. - /// - /// Tests have no platform frame loop, so this simulates the delivery of the - /// next frame. - #[cfg(any(test, feature = "test-support"))] - pub fn simulate_next_frame(&mut self, cx: &mut App) -> usize { - let callbacks = self.next_frame_callbacks.take(); - let count = callbacks.len(); - for callback in callbacks { - callback(self, cx); - } - count - } - - /// Spawn the future returned by the given closure on the application thread pool. - /// The closure is provided a handle to the current window and an `AsyncWindowContext` for - /// use within your future. - #[track_caller] - pub fn spawn(&self, cx: &App, f: AsyncFn) -> Task - where - R: 'static, - AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static, - { - let handle = self.handle; - cx.spawn(async move |app| { - let mut async_window_cx = AsyncWindowContext::new_context(app.clone(), handle); - f(&mut async_window_cx).await - }) - } - - /// Spawn the future returned by the given closure on the application thread - /// pool, with the given priority. The closure is provided a handle to the - /// current window and an `AsyncWindowContext` for use within your future. - #[track_caller] - pub fn spawn_with_priority( - &self, - priority: Priority, - cx: &App, - f: AsyncFn, - ) -> Task - where - R: 'static, - AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static, - { - let handle = self.handle; - cx.spawn_with_priority(priority, async move |app| { - let mut async_window_cx = AsyncWindowContext::new_context(app.clone(), handle); - f(&mut async_window_cx).await - }) - } - - /// Notify the window that its bounds have changed. - /// - /// This updates internal state like `viewport_size` and `scale_factor` from - /// the platform window, then notifies observers. Normally called automatically - /// by the platform's resize callback, but exposed publicly for test infrastructure. - pub fn bounds_changed(&mut self, cx: &mut App) { - self.scale_factor = self.platform_window.scale_factor(); - self.viewport_size = self.platform_window.content_size(); - self.display_id = self.platform_window.display().map(|display| display.id()); - self.mouse_position = self.platform_window.mouse_position(); - - self.refresh(); - - self.bounds_observers - .clone() - .retain(&(), |callback| callback(self, cx)); - } - - /// Returns the bounds of the current window in the global coordinate space, which could span across multiple displays. - pub fn bounds(&self) -> Bounds { - self.platform_window.bounds() - } - - /// Renders the current frame's scene to a texture and returns the pixel data as an RGBA image. - /// This does not present the frame to screen - useful for visual testing where we want - /// to capture what would be rendered without displaying it or requiring the window to be visible. - #[cfg(any(test, feature = "test-support"))] - pub fn render_to_image(&self) -> anyhow::Result { - self.platform_window - .render_to_image(&self.rendered_frame.scene) - } - - /// Returns the quads in the most recently rendered frame's scene, so tests can assert on - /// painted output without rasterizing the frame. Quad bounds are in scaled pixels and are - /// not clipped; each quad carries the content mask it will be clipped to when drawn. Quads - /// whose bounds don't intersect their content mask are culled at paint time and won't appear. - #[cfg(any(test, feature = "test-support"))] - pub fn painted_quads(&self) -> Vec { - self.rendered_frame.scene.quads.clone() - } - - /// Returns the immutable rounded clip nodes referenced by painted primitives. - #[cfg(any(test, feature = "test-support"))] - pub fn painted_clips(&self) -> Vec> { - self.rendered_frame.scene.rounded_clips.clone() - } - - /// Returns painted shadows for renderer regression tests. - #[cfg(any(test, feature = "test-support"))] - pub fn painted_shadows(&self) -> Vec { - self.rendered_frame.scene.shadows.clone() - } - - /// Set the content size of the window. - pub fn resize(&mut self, size: Size) { - self.platform_window.resize(size); - } - - /// Returns whether or not the window is currently fullscreen - pub fn is_fullscreen(&self) -> bool { - self.platform_window.is_fullscreen() - } - - /// Returns whether the window is currently in simple (borderless) fullscreen, - /// where it covers the entire screen including the menu bar and notch area. - /// Always `false` on platforms other than macOS. - pub fn is_simple_fullscreen(&self) -> bool { - self.platform_window.is_simple_fullscreen() - } - - pub(crate) fn appearance_changed(&mut self, cx: &mut App) { - self.appearance = self.platform_window.appearance(); - - self.appearance_observers - .clone() - .retain(&(), |callback| callback(self, cx)); - } - - pub(crate) fn button_layout_changed(&mut self, cx: &mut App) { - self.button_layout_observers - .clone() - .retain(&(), |callback| callback(self, cx)); - } - - /// Returns the appearance of the current window. - pub fn appearance(&self) -> WindowAppearance { - self.appearance - } - - /// Returns the size of the drawable area within the window. - pub fn viewport_size(&self) -> Size { - self.viewport_size - } - - /// Returns whether this window is focused by the operating system (receiving key events). - pub fn is_window_active(&self) -> bool { - self.active.get() - } - - /// Returns whether this window is considered to be the window - /// that currently owns the mouse cursor. - /// On mac, this is equivalent to `is_window_active`. - pub fn is_window_hovered(&self) -> bool { - if cfg!(any( - target_os = "windows", - target_os = "linux", - target_os = "freebsd" - )) { - self.hovered.get() - } else { - self.is_window_active() - } - } - - /// Toggle zoom on the window. - pub fn zoom_window(&self) { - self.platform_window.zoom(); - } - - /// Opens the native title bar context menu, useful when implementing client side decorations (Wayland and X11) - pub fn show_window_menu(&self, position: Point) { - self.platform_window.show_window_menu(position) - } - - /// Handle window movement for Linux and macOS. - /// Tells the compositor to take control of window movement (Wayland and X11) - /// - /// Events may not be received during a move operation. - pub fn start_window_move(&self) { - self.platform_window.start_window_move() - } - - /// When using client side decorations, set this to the width of the invisible decorations (Wayland and X11) - pub fn set_client_inset(&mut self, inset: Pixels) { - self.client_inset = Some(inset); - self.platform_window.set_client_inset(inset); - } - - /// Returns the client_inset value by [`Self::set_client_inset`]. - pub fn client_inset(&self) -> Option { - self.client_inset - } - - /// Returns whether the title bar window controls need to be rendered by the application (Wayland and X11) - pub fn window_decorations(&self) -> Decorations { - self.platform_window.window_decorations() - } - - /// Returns whether this window is resizable. - pub fn is_resizable(&self) -> bool { - self.is_resizable - } - - /// Returns whether this window is minimizable. - pub fn is_minimizable(&self) -> bool { - self.is_minimizable - } - - /// Returns the controls supported by the platform. - pub fn window_controls(&self) -> WindowControls { - self.platform_window.window_controls() - } - - /// Updates the window's title at the platform level. - pub fn set_window_title(&mut self, title: &str) { - self.platform_window.set_title(title); - self.a11y.set_window_title(title.to_string()); - } - - /// Sets the position of the macOS traffic light buttons. - #[cfg(target_os = "macos")] - pub fn set_traffic_light_position(&self, position: Point) { - self.platform_window.set_traffic_light_position(position); - } - - /// Sets the application identifier. - pub fn set_app_id(&mut self, app_id: &str) { - self.platform_window.set_app_id(app_id); - } - - /// Sets the window background appearance. - pub fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) { - self.platform_window - .set_background_appearance(background_appearance); - } - - /// Mark the window as dirty at the platform level. - pub fn set_window_edited(&mut self, edited: bool) { - self.platform_window.set_edited(edited); - } - - /// Set the path of the file this window represents. - /// On macOS, this sets the window's accessibility document property (AXDocument). - pub fn set_document_path(&self, path: Option<&std::path::Path>) { - self.platform_window.set_document_path(path); - } - - /// Determine the display on which the window is visible. - pub fn display(&self, cx: &App) -> Option> { - cx.platform - .displays() - .into_iter() - .find(|display| Some(display.id()) == self.display_id) - } - - /// Show the platform character palette. - pub fn show_character_palette(&self) { - self.platform_window.show_character_palette(); - } - - /// The scale factor of the display associated with the window. For example, it could - /// return 2.0 for a "retina" display, indicating that each logical pixel should actually - /// be rendered as two pixels on screen. - pub fn scale_factor(&self) -> f32 { - self.scale_factor - } - - /// Overrides the display scale factor for tests. - #[cfg(any(test, feature = "test-support"))] - pub fn set_scale_factor(&mut self, scale_factor: f32) { - self.scale_factor = scale_factor; - self.refresh(); - } - - /// The size of an em for the base font of the application. Adjusting this value allows the - /// UI to scale, just like zooming a web page. - pub fn rem_size(&self) -> Pixels { - self.rem_size_override_stack - .last() - .copied() - .unwrap_or(self.rem_size) - } - - /// Sets the size of an em for the base font of the application. Adjusting this value allows the - /// UI to scale, just like zooming a web page. - pub fn set_rem_size(&mut self, rem_size: impl Into) { - self.rem_size = rem_size.into(); - } - - /// Acquire a globally unique identifier for the given ElementId. - /// Only valid for the duration of the provided closure. - pub fn with_global_id( - &mut self, - element_id: ElementId, - f: impl FnOnce(&GlobalElementId, &mut Self) -> R, - ) -> R { - self.with_id(element_id, |this| { - let global_id = GlobalElementId(Arc::from(&*this.element_id_stack)); - - f(&global_id, this) - }) - } - - /// Calls the provided closure with the element ID pushed on the stack. - #[inline] - pub fn with_id( - &mut self, - element_id: impl Into, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.element_id_stack.push(element_id.into()); - let result = f(self); - self.element_id_stack.pop(); - result - } - - /// Executes the provided function with the specified rem size. - /// - /// This method must only be called as part of element drawing. - // This function is called in a highly recursive manner in editor - // prepainting, make sure its inlined to reduce the stack burden - #[inline] - pub fn with_rem_size(&mut self, rem_size: Option>, f: F) -> R - where - F: FnOnce(&mut Self) -> R, - { - self.invalidator.debug_assert_paint_or_prepaint(); - - if let Some(rem_size) = rem_size { - self.rem_size_override_stack.push(rem_size.into()); - let result = f(self); - self.rem_size_override_stack.pop(); - result - } else { - f(self) - } - } - - /// The line height associated with the current text style. - pub fn line_height(&self) -> Pixels { - self.text_style().line_height_in_pixels(self.rem_size()) - } - - /// Rounds a logical value to the nearest device pixel. - #[inline] - pub fn pixel_snap(&self, value: Pixels) -> Pixels { - px(round_to_device_pixel(value.0, self.scale_factor()) / self.scale_factor()) - } - - /// f64 variant of [`Self::pixel_snap`]. - #[inline] - pub fn pixel_snap_f64(&self, value: f64) -> f64 { - let scale_factor = f64::from(self.scale_factor()); - round_half_toward_zero_f64(value * scale_factor) / scale_factor - } - - /// Snaps a bounds' origin and size to the nearest device pixel. - #[inline] - pub fn pixel_snap_bounds(&self, bounds: Bounds) -> Bounds { - bounds.map(|c| self.pixel_snap(c)) - } - - /// Snaps a point's coordinates to the nearest device pixel. - #[inline] - pub fn pixel_snap_point(&self, position: Point) -> Point { - position.map(|c| self.pixel_snap(c)) - } - - #[inline] - fn snap_bounds(&self, bounds: Bounds) -> Bounds { - let scale_factor = self.scale_factor(); - let left = round_to_device_pixel(bounds.left().0, scale_factor); - let top = round_to_device_pixel(bounds.top().0, scale_factor); - let right = round_to_device_pixel(bounds.right().0, scale_factor).max(left); - let bottom = round_to_device_pixel(bounds.bottom().0, scale_factor).max(top); - Bounds::from_corners( - point(ScaledPixels(left), ScaledPixels(top)), - point(ScaledPixels(right), ScaledPixels(bottom)), - ) - } - - /// Rounds half-to-zero but clamps any non-zero input up to 1 dp so thin strokes do not disappear. - #[inline] - fn snap_stroke(&self, value: Pixels) -> ScaledPixels { - ScaledPixels(round_stroke_to_device_pixel(value.0, self.scale_factor())) - } - - #[inline] - fn snap_border_widths(&self, edges: Edges) -> Edges { - edges.map(|e| self.snap_stroke(*e)) - } - - /// Floors the near edge and ceils the far edge, producing a strict superset of the raw region. - #[inline] - fn cover_bounds(&self, bounds: Bounds) -> Bounds { - let scale_factor = self.scale_factor(); - let left = floor_to_device_pixel(bounds.left().0, scale_factor); - let top = floor_to_device_pixel(bounds.top().0, scale_factor); - let right = ceil_to_device_pixel(bounds.right().0, scale_factor).max(left); - let bottom = ceil_to_device_pixel(bounds.bottom().0, scale_factor).max(top); - Bounds::from_corners( - point(ScaledPixels(left), ScaledPixels(top)), - point(ScaledPixels(right), ScaledPixels(bottom)), - ) - } - - #[inline] - fn snapped_content_mask(&mut self) -> ContentMask { - let region = self.content_mask(); - let mut clip_index = 0; - for shape in ®ion.rounded_clips { - let mut clip = shape.scale(self.scale_factor()); - // Use the original element's snapped shape, as paint_quad does. - // A descendant's culling bounds must never relocate these curves. - clip.bounds = self.snap_bounds(shape.bounds); - clip.parent = clip_index; - clip_index = self.next_frame.scene.insert_clip(clip); - } - ContentMask { - bounds: self.cover_bounds(region.bounds), - corner_radii: Corners::default(), - clip_index, - clip_padding: 0, - } - } - - /// Call to prevent the default action of an event. Currently only used to prevent - /// parent elements from becoming focused on mouse down. - pub fn prevent_default(&mut self) { - self.default_prevented = true; - } - - /// Obtain whether default has been prevented for the event currently being dispatched. - pub fn default_prevented(&self) -> bool { - self.default_prevented - } - - /// Determine whether the given action is available along the dispatch path to the currently focused element. - pub fn is_action_available(&self, action: &dyn Action, cx: &App) -> bool { - let node_id = - self.focus_node_id_in_rendered_frame(self.focused(cx).map(|handle| handle.id)); - self.rendered_frame - .dispatch_tree - .is_action_available(action, node_id) - } - - /// Determine whether the given action is available along the dispatch path to the given focus_handle. - pub fn is_action_available_in(&self, action: &dyn Action, focus_handle: &FocusHandle) -> bool { - let node_id = self.focus_node_id_in_rendered_frame(Some(focus_handle.id)); - self.rendered_frame - .dispatch_tree - .is_action_available(action, node_id) - } - - /// The position of the mouse relative to the window. - pub fn mouse_position(&self) -> Point { - self.mouse_position - } - - /// Captures the pointer for the given hitbox. While captured, all mouse move and mouse up - /// events will be routed to listeners that check this hitbox's `is_hovered` status, - /// regardless of actual hit testing. This enables drag operations that continue - /// even when the pointer moves outside the element's bounds. - /// - /// The capture is automatically released on mouse up. - pub fn capture_pointer(&mut self, hitbox_id: HitboxId) { - self.captured_hitbox = Some(hitbox_id); - } - - /// Releases any active pointer capture. - pub fn release_pointer(&mut self) { - self.captured_hitbox = None; - } - - /// Returns the hitbox that has captured the pointer, if any. - pub fn captured_hitbox(&self) -> Option { - self.captured_hitbox - } - - /// Captures the current long press for the given entity. - /// - /// The capture is released when the gesture ends or is cancelled, or when - /// a replacement touch begins. A listener must also call - /// [`Self::prevent_default`] on the started event to claim the gesture. - pub fn capture_long_press(&mut self, entity: &Entity) { - self.long_press_capture = Some(entity.entity_id()); - } - - /// Returns whether the given entity has captured the current long press. - pub fn has_long_press_capture(&self, entity: &Entity) -> bool { - self.long_press_capture == Some(entity.entity_id()) - } - - /// The current state of the keyboard's modifiers - pub fn modifiers(&self) -> Modifiers { - self.modifiers - } - - /// Returns true if the last input event was keyboard-based (key press, tab navigation, etc.) - /// This is used for focus-visible styling to show focus indicators only for keyboard navigation. - pub fn last_input_was_keyboard(&self) -> bool { - self.last_input_modality == InputModality::Keyboard - } - - pub(crate) fn last_input_was_touch(&self) -> bool { - self.last_input_modality == InputModality::Touch - } - - /// The current state of the keyboard's capslock - pub fn capslock(&self) -> Capslock { - self.capslock - } - - /// Produces a new frame and assigns it to `rendered_frame`. To actually show - /// the contents of the new [`Scene`], use [`Self::present`]. - #[profiling::function] - pub fn draw(&mut self, cx: &mut App) -> ArenaClearNeeded { - // Drain every draw in profiler builds so a previous frame's - // first-invalidation timestamp can't be attributed to this one. - #[cfg(feature = "profiler")] - let frame_dirty = self.invalidator.take_frame_dirty(); - #[cfg(feature = "profiler")] - self.window_profiler.begin_draw(); - - // Set up the per-App arena for element allocation during this draw. - // This ensures that multiple test Apps have isolated arenas. - let arena_scope = ElementArenaScope::enter(&cx.element_arena); - - self.invalidate_entities(); - cx.entities.clear_accessed(); - debug_assert!(self.rendered_entity_stack.is_empty()); - self.invalidator.set_dirty(false); - self.requested_autoscroll = None; - - // Restore the previously-used input handler. - // Place it back into a None slot (left by a previous .take()) so that - // cached paint_range indices in reuse_paint find the handler at the - // expected position. - if let Some(input_handler) = self.platform_window.take_input_handler() { - if let Some(slot) = self - .rendered_frame - .input_handlers - .iter_mut() - .rev() - .find(|h| h.is_none()) - { - *slot = Some(input_handler); - } else { - self.rendered_frame.input_handlers.push(Some(input_handler)); - } - } - if !cx.mode.skip_drawing() { - self.draw_roots(cx); - #[cfg(feature = "profiler")] - { - let viewport_size = self.viewport_size; - let scale_factor = self.scale_factor(); - self.debug_frame_overlay.paint( - &mut self.next_frame.scene, - viewport_size, - scale_factor, - ); - } - } - self.dirty_views.clear(); - self.next_frame.window_active = self.active.get(); - - // Register requested input handler with the platform window. - // Use .take() instead of .pop() to preserve Vec length, so that cached - // paint_range indices remain valid for reuse_paint on the next frame. - // Search backwards to find the last Some entry, since reuse_paint may - // have copied None slots from the previous frame. (Fixes #50456) - let focused_text_input_active = if let Some(mut input_handler) = self - .next_frame - .input_handlers - .iter_mut() - .rev() - .find_map(|h| h.take()) - { - let accepts_text_input = input_handler.accepts_text_input(self, cx); - self.platform_window.set_input_handler(input_handler); - accepts_text_input - } else { - false - }; - self.apply_text_input_configuration(cx); - if focused_text_input_active != self.focused_text_input_active { - self.focused_text_input_active = focused_text_input_active; - self.platform_window - .text_input_state_changed(if focused_text_input_active { - TextInputStateChange::FocusGained - } else { - TextInputStateChange::FocusLost - }); - } - - self.layout_engine.as_mut().unwrap().clear(); - self.text_system().finish_frame(); - self.next_frame.finish(&mut self.rendered_frame); - - self.invalidator.set_phase(DrawPhase::Focus); - let previous_focus_path = self.rendered_frame.focus_path(); - let previous_window_active = self.rendered_frame.window_active; - mem::swap(&mut self.rendered_frame, &mut self.next_frame); - self.next_frame.clear(); - let current_focus_path = self.rendered_frame.focus_path(); - let current_window_active = self.rendered_frame.window_active; - let mut focus_before_listeners = self.focus; - - if previous_focus_path != current_focus_path - || previous_window_active != current_window_active - { - if !previous_focus_path.is_empty() && current_focus_path.is_empty() { - self.focus_lost_path = previous_focus_path.clone(); - self.focus_lost_listeners - .clone() - .retain(&(), |listener| listener(self, cx)); - self.focus_lost_path = SmallVec::new(); - // The focus-lost fallback (e.g. a workspace refocusing itself) may target - // an element that isn't part of the element tree, in which case scheduling - // a redraw below would dispatch focus-lost again, looping forever. Only - // track focus movement caused by the focus listeners. - focus_before_listeners = self.focus; - } - - let event = WindowFocusEvent { - previous_focus_path: if previous_window_active { - previous_focus_path - } else { - Default::default() - }, - current_focus_path: if current_window_active { - current_focus_path - } else { - Default::default() - }, - }; - self.focus_listeners - .clone() - .retain(&(), |listener| listener(&event, self, cx)); - } - - debug_assert!(self.rendered_entity_stack.is_empty()); - self.record_entities_accessed(cx); - self.reset_cursor_style(cx); - self.refreshing = false; - self.invalidator.set_phase(DrawPhase::None); - // Focus listeners may move focus (e.g. a dock forwarding focus to its active - // panel). `Window::focus` suppresses `refresh` while a draw is in progress, so - // schedule another frame here to render the new focus state and dispatch the - // resulting focus events. - if self.focus != focus_before_listeners { - self.refresh(); - } - self.needs_present.set(true); - - #[cfg(feature = "profiler")] - { - let draw_duration = self - .window_profiler - .end_draw(frame_dirty.dirty_at, frame_dirty.invalidations); - self.debug_frame_overlay.record_frame(draw_duration); - } - - // Exit the scope to obtain the arena-clear token this draw owes; the - // scope's teardown itself happens in `ElementArenaScope::drop`. - arena_scope.exit(&cx.element_arena) - } - - fn record_entities_accessed(&mut self, cx: &mut App) { - let mut entities_ref = cx.entities.accessed_entities.get_mut(); - let mut entities = mem::take(entities_ref.deref_mut()); - let handle = self.handle; - cx.record_entities_accessed( - handle, - // Try moving window invalidator into the Window - self.invalidator.clone(), - &entities, - ); - let mut entities_ref = cx.entities.accessed_entities.get_mut(); - mem::swap(&mut entities, entities_ref.deref_mut()); - } - - fn invalidate_entities(&mut self) { - let mut views = self.invalidator.take_views(); - for entity in views.drain() { - self.mark_view_dirty(entity); - } - self.invalidator.replace_views(views); - } - - #[profiling::function] - fn present(&mut self) { - #[cfg(feature = "profiler")] - let _foreground_turn = profiler::journal::foreground_turn(); - #[cfg(feature = "profiler")] - let present_start = Instant::now(); - self.platform_window.draw(&self.rendered_frame.scene); - #[cfg(feature = "profiler")] - self.window_profiler.record_present( - present_start, - Instant::now(), - self.active.get(), - !self.next_frame_callbacks.borrow().is_empty(), - ); - self.needs_present.set(false); - profiling::finish_frame!(); - } - - /// Presents the most recently drawn frame if it hasn't been presented yet. - /// - /// Benchmarks drive drawing synchronously rather than through a platform - /// frame-request loop, so they call this after each measured update to - /// submit the frame like production presentation would. - #[cfg(any(feature = "bench-support", all(test, feature = "profiler")))] - pub fn present_if_needed(&mut self) { - if self.needs_present.get() { - self.present(); - } - } - - /// Returns a snapshot of the current input-latency histograms. - #[cfg(feature = "profiler")] - pub fn input_latency_snapshot(&self) -> profiler::InputLatencySnapshot { - self.window_profiler.input_latency_snapshot() - } - - /// Returns a snapshot of the current frame-duration histograms. - #[cfg(feature = "profiler")] - pub fn frame_duration_snapshot(&self) -> profiler::FrameDurationSnapshot { - self.window_profiler.frame_duration_snapshot() - } - - /// Returns the current mode of the debug frame overlay. - #[cfg(feature = "profiler")] - pub fn debug_frame_overlay_mode(&self) -> DebugFrameOverlayMode { - self.debug_frame_overlay.mode() - } - - /// Sets the mode of the debug frame overlay and schedules a redraw. - #[cfg(feature = "profiler")] - pub fn set_debug_frame_overlay_mode(&mut self, mode: DebugFrameOverlayMode) { - self.debug_frame_overlay.set_mode(mode); - self.refresh(); - } - - /// Advances the debug frame overlay through its hidden, frame-time-only, - /// and detailed modes. - #[cfg(feature = "profiler")] - pub fn cycle_debug_frame_overlay_mode(&mut self) { - self.set_debug_frame_overlay_mode(self.debug_frame_overlay.mode().next()); - } - - /// Clears the debug frame overlay's frame-time statistics, except for the - /// total frame count, and schedules a redraw. - #[cfg(feature = "profiler")] - pub fn reset_debug_frame_overlay_stats(&mut self) { - self.debug_frame_overlay.reset_stats(); - self.refresh(); - } - - fn draw_roots(&mut self, cx: &mut App) { - self.invalidator.set_phase(DrawPhase::Prepaint); - self.tooltip_bounds.take(); - - self.a11y.sync_active_flag(); - if self.a11y.is_active() { - self.a11y.begin_frame(); - } - - let _inspector_width: Pixels = rems(30.0).to_pixels(self.rem_size()); - let root_size = { - #[cfg(any(feature = "inspector", debug_assertions))] - { - if self.inspector.is_some() { - let mut size = self.viewport_size; - size.width = (size.width - _inspector_width).max(px(0.0)); - size - } else { - self.viewport_size - } - } - #[cfg(not(any(feature = "inspector", debug_assertions)))] - { - self.viewport_size - } - }; - - // Layout all root elements. Like the root element on the web, which - // stretches to fill the viewport unless explicitly sized, window roots - // fill the window when their size is `auto`. - let scale_factor = self.scale_factor(); - let mut root_element = self.root.as_ref().unwrap().clone().into_any_element(); - let root_layout_id = root_element.request_layout(self, cx); - self.layout_engine - .as_mut() - .unwrap() - .stretch_auto_size_to_fill(root_layout_id, root_size, scale_factor); - root_element.prepaint_as_root(Point::default(), root_size.into(), self, cx); - - #[cfg(any(feature = "inspector", debug_assertions))] - let inspector_element = self.prepaint_inspector(_inspector_width, cx); - - self.prepaint_deferred_draws(cx); - - let mut prompt_element = None; - let mut active_drag_element = None; - let mut tooltip_element = None; - if let Some(prompt) = self.prompt.take() { - let mut element = prompt.view.any_view().into_any_element(); - let prompt_layout_id = element.request_layout(self, cx); - self.layout_engine - .as_mut() - .unwrap() - .stretch_auto_size_to_fill(prompt_layout_id, root_size, scale_factor); - element.prepaint_as_root(Point::default(), root_size.into(), self, cx); - prompt_element = Some(element); - self.prompt = Some(prompt); - } else if let Some(active_drag) = cx.active_drag.take() { - let mut element = active_drag.view.clone().into_any_element(); - let offset = self.mouse_position() - active_drag.cursor_offset; - element.prepaint_as_root(offset, AvailableSpace::min_size(), self, cx); - active_drag_element = Some(element); - cx.active_drag = Some(active_drag); - } else { - tooltip_element = self.prepaint_tooltip(cx); - } - - self.mouse_hit_test = self.next_frame.hit_test(self.mouse_position); - - // Now actually paint the elements. - self.invalidator.set_phase(DrawPhase::Paint); - root_element.paint(self, cx); - - #[cfg(any(feature = "inspector", debug_assertions))] - self.paint_inspector(inspector_element, cx); - - self.paint_deferred_draws(cx); - - if let Some(mut prompt_element) = prompt_element { - prompt_element.paint(self, cx); - } else if let Some(mut drag_element) = active_drag_element { - drag_element.paint(self, cx); - } else if let Some(mut tooltip_element) = tooltip_element { - tooltip_element.paint(self, cx); - } - - #[cfg(any(feature = "inspector", debug_assertions))] - self.paint_inspector_hitbox(cx); - - // a11y may have been activated/deactivated halfway through the frame - let a11y_active_start_of_frame = self.a11y.is_active(); - self.a11y.sync_active_flag(); - let a11y_active_end_of_frame = self.a11y.is_active(); - - let should_send_a11y_update = a11y_active_start_of_frame && a11y_active_end_of_frame; - - if a11y_active_start_of_frame { - // Harvest frame metadata for the debug dump while the live window - // and frame are still in scope. - let frame_info = crate::window::a11y::debug::FrameDebugInfo { - viewport_size: self.viewport_size, - scale_factor: self.scale_factor, - tab_stop_count: self.next_frame.tab_stops.tab_stop_count(), - }; - // clear the builder state regardless - let tree_update = self.a11y.end_frame(frame_info); - - if should_send_a11y_update { - log::debug!( - "Sending a11y tree update: {} nodes", - tree_update.nodes.len() - ); - self.platform_window.a11y_tree_update(tree_update); - } - } - } - - fn prepaint_tooltip(&mut self, cx: &mut App) -> Option { - // Use indexing instead of iteration to avoid borrowing self for the duration of the loop. - for tooltip_request_index in (0..self.next_frame.tooltip_requests.len()).rev() { - let Some(Some(tooltip_request)) = self - .next_frame - .tooltip_requests - .get(tooltip_request_index) - .cloned() - else { - log::error!("Unexpectedly absent TooltipRequest"); - continue; - }; - let mut element = tooltip_request.tooltip.view.clone().into_any_element(); - let mouse_position = tooltip_request.tooltip.mouse_position; - let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self, cx); - - let mut tooltip_bounds = - Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size); - let window_bounds = Bounds { - origin: Point::default(), - size: self.viewport_size(), - }; - - if tooltip_bounds.right() > window_bounds.right() { - let new_x = mouse_position.x - tooltip_bounds.size.width - px(1.); - if new_x >= Pixels::ZERO { - tooltip_bounds.origin.x = new_x; - } else { - tooltip_bounds.origin.x = cmp::max( - Pixels::ZERO, - tooltip_bounds.origin.x - tooltip_bounds.right() - window_bounds.right(), - ); - } - } - - if tooltip_bounds.bottom() > window_bounds.bottom() { - let new_y = mouse_position.y - tooltip_bounds.size.height - px(1.); - if new_y >= Pixels::ZERO { - tooltip_bounds.origin.y = new_y; - } else { - tooltip_bounds.origin.y = cmp::max( - Pixels::ZERO, - tooltip_bounds.origin.y - tooltip_bounds.bottom() - window_bounds.bottom(), - ); - } - } - - // It's possible for an element to have an active tooltip while not being painted (e.g. - // via the `visible_on_hover` method). Since mouse listeners are not active in this - // case, instead update the tooltip's visibility here. - let is_visible = - (tooltip_request.tooltip.check_visible_and_update)(tooltip_bounds, self, cx); - if !is_visible { - continue; - } - - self.with_absolute_element_offset(tooltip_bounds.origin, |window| { - element.prepaint(window, cx) - }); - - self.tooltip_bounds = Some(TooltipBounds { - id: tooltip_request.id, - bounds: tooltip_bounds, - }); - return Some(element); - } - None - } - - fn prepaint_deferred_draws(&mut self, cx: &mut App) { - assert_eq!(self.element_id_stack.len(), 0); - - // Process deferred draws in multiple rounds to support nesting. - // Each round processes all current deferred draws, which may push new ones. - // - // The draws are processed in place rather than being moved out of - // `next_frame.deferred_draws`: `prepaint_index` snapshots that vector's - // length, so any prepaint range recorded during a round (view caches, - // nested deferred draws) must index the same vector `reuse_prepaint` - // slices on the next frame. Moving the draws out and re-appending them - // shifts the indices of nested draws, causing reused subtrees to graft - // the wrong deferred draws and panic in the dispatch tree. - let mut round_start = 0; - let mut depth = 0; - loop { - let round_end = self.next_frame.deferred_draws.len(); - if round_start == round_end { - break; - } - // Limit maximum nesting depth to prevent infinite loops. - assert!(depth < 10, "Exceeded maximum (10) deferred depth"); - depth += 1; - - // Sort this round by priority. - let mut traversal_order = (round_start..round_end).collect::>(); - traversal_order.sort_by_key(|ix| self.next_frame.deferred_draws[*ix].priority); - - for deferred_draw_ix in traversal_order { - let (element, parent_node, current_view, rem_size, absolute_offset, prepaint_range) = { - let deferred_draw = &mut self.next_frame.deferred_draws[deferred_draw_ix]; - self.element_id_stack - .clone_from(&deferred_draw.element_id_stack); - self.text_style_stack - .clone_from(&deferred_draw.text_style_stack); - ( - deferred_draw.element.take(), - deferred_draw.parent_node, - deferred_draw.current_view, - deferred_draw.rem_size, - deferred_draw.absolute_offset, - deferred_draw.prepaint_range.clone(), - ) - }; - self.next_frame.dispatch_tree.set_active_node(parent_node); - - let prepaint_start = self.prepaint_index(); - if let Some(mut element) = element { - self.with_rendered_view(current_view, |window| { - window.with_rem_size(Some(rem_size), |window| { - window.with_absolute_element_offset(absolute_offset, |window| { - element.prepaint(window, cx); - }); - }); - }); - self.next_frame.deferred_draws[deferred_draw_ix].element = Some(element); - } else { - self.reuse_prepaint(prepaint_range); - } - let prepaint_end = self.prepaint_index(); - self.next_frame.deferred_draws[deferred_draw_ix].prepaint_range = - prepaint_start..prepaint_end; - } - - self.element_id_stack.clear(); - self.text_style_stack.clear(); - round_start = round_end; - } - } - - fn paint_deferred_draws(&mut self, cx: &mut App) { - assert_eq!(self.element_id_stack.len(), 0); - - // Paint all deferred draws in priority order. - // Since prepaint has already processed nested deferreds, we just paint them all. - if self.next_frame.deferred_draws.len() == 0 { - return; - } - - let traversal_order = self.deferred_draw_traversal_order(); - let mut deferred_draws = mem::take(&mut self.next_frame.deferred_draws); - for deferred_draw_ix in traversal_order { - let mut deferred_draw = &mut deferred_draws[deferred_draw_ix]; - self.element_id_stack - .clone_from(&deferred_draw.element_id_stack); - self.next_frame - .dispatch_tree - .set_active_node(deferred_draw.parent_node); - - let paint_start = self.paint_index(); - let content_mask = deferred_draw.content_mask.clone(); - if let Some(element) = deferred_draw.element.as_mut() { - self.with_rendered_view(deferred_draw.current_view, |window| { - window.with_content_mask(content_mask, |window| { - window.with_rem_size(Some(deferred_draw.rem_size), |window| { - element.paint(window, cx); - }); - }) - }) - } else { - self.reuse_paint(deferred_draw.paint_range.clone()); - } - let paint_end = self.paint_index(); - deferred_draw.paint_range = paint_start..paint_end; - } - self.next_frame.deferred_draws = deferred_draws; - self.element_id_stack.clear(); - } - - fn deferred_draw_traversal_order(&mut self) -> SmallVec<[usize; 8]> { - let deferred_count = self.next_frame.deferred_draws.len(); - let mut sorted_indices = (0..deferred_count).collect::>(); - sorted_indices.sort_by_key(|ix| self.next_frame.deferred_draws[*ix].priority); - sorted_indices - } - - pub(crate) fn prepaint_index(&self) -> PrepaintStateIndex { - PrepaintStateIndex { - hitboxes_index: self.next_frame.hitboxes.len(), - tooltips_index: self.next_frame.tooltip_requests.len(), - deferred_draws_index: self.next_frame.deferred_draws.len(), - dispatch_tree_index: self.next_frame.dispatch_tree.len(), - accessed_element_states_index: self.next_frame.accessed_element_states.len(), - line_layout_index: self.text_system.layout_index(), - } - } - - pub(crate) fn reuse_prepaint(&mut self, range: Range) { - self.next_frame.hitboxes.extend( - self.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index] - .iter() - .cloned(), - ); - self.next_frame.tooltip_requests.extend( - self.rendered_frame.tooltip_requests - [range.start.tooltips_index..range.end.tooltips_index] - .iter_mut() - .map(|request| request.take()), - ); - self.next_frame.accessed_element_states.extend( - self.rendered_frame.accessed_element_states[range.start.accessed_element_states_index - ..range.end.accessed_element_states_index] - .iter() - .map(|(id, type_id)| (id.clone(), *type_id)), - ); - self.text_system - .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index); - - let reused_subtree = self.next_frame.dispatch_tree.reuse_subtree( - range.start.dispatch_tree_index..range.end.dispatch_tree_index, - &mut self.rendered_frame.dispatch_tree, - self.focus, - ); - - if reused_subtree.contains_focus() { - self.next_frame.focus = self.focus; - } - - self.next_frame.deferred_draws.extend( - self.rendered_frame.deferred_draws - [range.start.deferred_draws_index..range.end.deferred_draws_index] - .iter() - .map(|deferred_draw| DeferredDraw { - current_view: deferred_draw.current_view, - parent_node: reused_subtree.refresh_node_id(deferred_draw.parent_node), - element_id_stack: deferred_draw.element_id_stack.clone(), - text_style_stack: deferred_draw.text_style_stack.clone(), - content_mask: deferred_draw.content_mask.clone(), - rem_size: deferred_draw.rem_size, - priority: deferred_draw.priority, - element: None, - absolute_offset: deferred_draw.absolute_offset, - prepaint_range: deferred_draw.prepaint_range.clone(), - paint_range: deferred_draw.paint_range.clone(), - }), - ); - } - - pub(crate) fn paint_index(&self) -> PaintIndex { - PaintIndex { - scene_index: self.next_frame.scene.len(), - mouse_listeners_index: self.next_frame.mouse_listeners.len(), - input_handlers_index: self.next_frame.input_handlers.len(), - cursor_styles_index: self.next_frame.cursor_styles.len(), - accessed_element_states_index: self.next_frame.accessed_element_states.len(), - tab_handle_index: self.next_frame.tab_stops.paint_index(), - line_layout_index: self.text_system.layout_index(), - } - } - - pub(crate) fn reuse_paint(&mut self, range: Range) { - self.next_frame.cursor_styles.extend( - self.rendered_frame.cursor_styles - [range.start.cursor_styles_index..range.end.cursor_styles_index] - .iter() - .cloned(), - ); - self.next_frame.input_handlers.extend( - self.rendered_frame.input_handlers - [range.start.input_handlers_index..range.end.input_handlers_index] - .iter_mut() - .map(|handler| handler.take()), - ); - self.next_frame.mouse_listeners.extend( - self.rendered_frame.mouse_listeners - [range.start.mouse_listeners_index..range.end.mouse_listeners_index] - .iter_mut() - .map(|listener| listener.take()), - ); - self.next_frame.accessed_element_states.extend( - self.rendered_frame.accessed_element_states[range.start.accessed_element_states_index - ..range.end.accessed_element_states_index] - .iter() - .map(|(id, type_id)| (id.clone(), *type_id)), - ); - self.next_frame.tab_stops.replay( - &self.rendered_frame.tab_stops.insertion_history - [range.start.tab_handle_index..range.end.tab_handle_index], - ); - - self.text_system - .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index); - self.next_frame.scene.replay( - range.start.scene_index..range.end.scene_index, - &self.rendered_frame.scene, - ); - } - - /// Push a text style onto the stack, and call a function with that style active. - /// Use [`Window::text_style`] to get the current, combined text style. This method - /// should only be called as part of element drawing. - pub fn with_text_style(&mut self, style: Option, f: F) -> R - where - F: FnOnce(&mut Self) -> R, - { - self.invalidator.debug_assert_paint_or_prepaint(); - if let Some(style) = style { - self.text_style_stack.push(style); - let result = f(self); - self.text_style_stack.pop(); - result - } else { - f(self) - } - } - - /// Updates the cursor style at the platform level. This method should only be called - /// during the paint phase of element drawing. - pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) { - self.invalidator.debug_assert_paint(); - self.next_frame.cursor_styles.push(CursorStyleRequest { - hitbox_id: Some(hitbox.id), - style, - }); - } - - /// Updates the cursor style for the entire window at the platform level. A cursor - /// style using this method will have precedence over any cursor style set using - /// `set_cursor_style`. This method should only be called during the paint - /// phase of element drawing. - pub fn set_window_cursor_style(&mut self, style: CursorStyle) { - self.invalidator.debug_assert_paint(); - self.next_frame.cursor_styles.push(CursorStyleRequest { - hitbox_id: None, - style, - }) - } - - /// Sets a tooltip to be rendered for the upcoming frame. This method should only be called - /// during the paint phase of element drawing. - pub fn set_tooltip(&mut self, tooltip: AnyTooltip) -> TooltipId { - self.invalidator.debug_assert_prepaint(); - let id = TooltipId(post_inc(&mut self.next_tooltip_id.0)); - self.next_frame - .tooltip_requests - .push(Some(TooltipRequest { id, tooltip })); - id - } - - /// Invoke the given function with the given content mask after intersecting it - /// with the current mask. This method should only be called during element drawing. - // This function is called in a highly recursive manner in editor - // prepainting, make sure its inlined to reduce the stack burden - #[inline] - pub fn with_content_mask( - &mut self, - mask: Option>, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.invalidator.debug_assert_paint_or_prepaint(); - if let Some(mask) = mask { - let mask = mask.into().intersect(&self.content_mask()); - self.content_mask_stack.push(mask); - let result = f(self); - self.content_mask_stack.pop(); - result - } else { - f(self) - } - } - - /// Updates the global element offset relative to the current offset. This is used to implement - /// scrolling. This method should only be called during the prepaint phase of element drawing. - pub fn with_element_offset( - &mut self, - offset: Point, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.invalidator.debug_assert_prepaint(); - - if offset.is_zero() { - return f(self); - }; - - let abs_offset = self.element_offset() + offset; - self.with_absolute_element_offset(abs_offset, f) - } - - /// Updates the global element offset based on the given offset. This is used to implement - /// drag handles and other manual painting of elements. This method should only be called during - /// the prepaint phase of element drawing. - pub fn with_absolute_element_offset( - &mut self, - offset: Point, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.invalidator.debug_assert_prepaint(); - self.element_offset_stack.push(offset); - let result = f(self); - self.element_offset_stack.pop(); - result - } - - pub(crate) fn with_element_opacity( - &mut self, - opacity: Option, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.invalidator.debug_assert_paint_or_prepaint(); - - let Some(opacity) = opacity else { - return f(self); - }; - - let previous_opacity = self.element_opacity; - self.element_opacity = previous_opacity * opacity; - let result = f(self); - self.element_opacity = previous_opacity; - result - } - - /// Perform prepaint on child elements in a "retryable" manner, so that any side effects - /// of prepaints can be discarded before prepainting again. This is used to support autoscroll - /// where we need to prepaint children to detect the autoscroll bounds, then adjust the - /// element offset and prepaint again. See [`crate::List`] for an example. This method should only be - /// called during the prepaint phase of element drawing. - pub fn transact(&mut self, f: impl FnOnce(&mut Self) -> Result) -> Result { - self.invalidator.debug_assert_prepaint(); - let index = self.prepaint_index(); - let result = f(self); - if result.is_err() { - self.next_frame.hitboxes.truncate(index.hitboxes_index); - self.next_frame - .tooltip_requests - .truncate(index.tooltips_index); - self.next_frame - .deferred_draws - .truncate(index.deferred_draws_index); - self.next_frame - .dispatch_tree - .truncate(index.dispatch_tree_index); - self.next_frame - .accessed_element_states - .truncate(index.accessed_element_states_index); - self.text_system.truncate_layouts(index.line_layout_index); - } - result - } - - /// When you call this method during [`Element::prepaint`], containing elements will attempt to - /// scroll to cause the specified bounds to become visible. When they decide to autoscroll, they will call - /// [`Element::prepaint`] again with a new set of bounds. See [`crate::List`] for an example of an element - /// that supports this method being called on the elements it contains. This method should only be - /// called during the prepaint phase of element drawing. - pub fn request_autoscroll(&mut self, bounds: Bounds) { - self.invalidator.debug_assert_prepaint(); - self.requested_autoscroll = Some(bounds); - } - - /// This method can be called from a containing element such as [`crate::List`] to support the autoscroll behavior - /// described in [`Self::request_autoscroll`]. - pub fn take_autoscroll(&mut self) -> Option> { - self.invalidator.debug_assert_prepaint(); - self.requested_autoscroll.take() - } - - /// Asynchronously load an asset, if the asset hasn't finished loading this will return None. - /// Your view will be re-drawn once the asset has finished loading. - /// - /// Note that the multiple calls to this method will only result in one `Asset::load` call at a - /// time. - pub fn use_asset(&mut self, source: &A::Source, cx: &mut App) -> Option { - let (task, is_first) = cx.fetch_asset::(source); - task.clone().now_or_never().or_else(|| { - if is_first { - let entity_id = self.current_view(); - self.spawn(cx, { - let task = task.clone(); - async move |cx| { - task.await; - - cx.on_next_frame(move |_, cx| { - cx.notify(entity_id); - }); - } - }) - .detach(); - } - - None - }) - } - - /// Asynchronously load an asset, if the asset hasn't finished loading or doesn't exist this will return None. - /// Your view will not be re-drawn once the asset has finished loading. - /// - /// Note that the multiple calls to this method will only result in one `Asset::load` call at a - /// time. - pub fn get_asset(&mut self, source: &A::Source, cx: &mut App) -> Option { - let (task, _) = cx.fetch_asset::(source); - task.now_or_never() - } - /// Obtain the current element offset. This method should only be called during the - /// prepaint phase of element drawing. - pub fn element_offset(&self) -> Point { - self.invalidator.debug_assert_prepaint(); - self.element_offset_stack - .last() - .copied() - .unwrap_or_default() - } - - /// Obtain the current element opacity. This method should only be called during the - /// prepaint phase of element drawing. - #[inline] - pub(crate) fn element_opacity(&self) -> f32 { - self.invalidator.debug_assert_paint_or_prepaint(); - self.element_opacity - } - - /// Obtain the current content mask. This method should only be called during element drawing. - pub fn content_mask(&self) -> crate::ClipRegion { - self.invalidator.debug_assert_paint_or_prepaint(); - self.content_mask_stack.last().cloned().unwrap_or_else(|| { - ContentMask { - bounds: Bounds { - origin: Point::default(), - size: self.viewport_size, - }, - ..Default::default() - } - .into() - }) - } - - /// Provide elements in the called function with a new namespace in which their identifiers must be unique. - /// This can be used within a custom element to distinguish multiple sets of child elements. - pub fn with_element_namespace( - &mut self, - element_id: impl Into, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.element_id_stack.push(element_id.into()); - let result = f(self); - self.element_id_stack.pop(); - result - } - - /// Use a piece of state that exists as long this element is being rendered in consecutive frames. - pub fn use_keyed_state( - &mut self, - key: impl Into, - cx: &mut App, - init: impl FnOnce(&mut Self, &mut Context) -> S, - ) -> Entity { - let current_view = self.current_view(); - self.with_global_id(key.into(), |global_id, window| { - window.with_element_state(global_id, |state: Option>, window| { - if let Some(state) = state { - (state.clone(), state) - } else { - let new_state = cx.new(|cx| init(window, cx)); - cx.observe(&new_state, move |_, cx| { - cx.notify(current_view); - }) - .detach(); - (new_state.clone(), new_state) - } - }) - }) - } - - /// Use a piece of state that exists as long this element is being rendered in consecutive frames, without needing to specify a key - /// - /// NOTE: This method uses the location of the caller to generate an ID for this state. - /// If this is not sufficient to identify your state (e.g. you're rendering a list item), - /// you can provide a custom ElementID using the `use_keyed_state` method. - #[track_caller] - pub fn use_state( - &mut self, - cx: &mut App, - init: impl FnOnce(&mut Self, &mut Context) -> S, - ) -> Entity { - self.use_keyed_state( - ElementId::CodeLocation(*core::panic::Location::caller()), - cx, - init, - ) - } - - /// Updates or initializes state for an element with the given id that lives across multiple - /// frames. If an element with this ID existed in the rendered frame, its state will be passed - /// to the given closure. The state returned by the closure will be stored so it can be referenced - /// when drawing the next frame. This method should only be called as part of element drawing. - pub fn with_element_state( - &mut self, - global_id: &GlobalElementId, - f: impl FnOnce(Option, &mut Self) -> (R, S), - ) -> R - where - S: 'static, - { - self.invalidator.debug_assert_paint_or_prepaint(); - - let key = (global_id.clone(), TypeId::of::()); - self.next_frame.accessed_element_states.push(key.clone()); - - if let Some(any) = self - .next_frame - .element_states - .remove(&key) - .or_else(|| self.rendered_frame.element_states.remove(&key)) - { - let ElementStateBox { - inner, - #[cfg(debug_assertions)] - type_name, - } = any; - // Using the extra inner option to avoid needing to reallocate a new box. - let mut state_box = inner - .downcast::>() - .map_err(|_| { - #[cfg(debug_assertions)] - { - anyhow::anyhow!( - "invalid element state type for id, requested {:?}, actual: {:?}", - std::any::type_name::(), - type_name - ) - } - - #[cfg(not(debug_assertions))] - { - anyhow::anyhow!( - "invalid element state type for id, requested {:?}", - std::any::type_name::(), - ) - } - }) - .unwrap(); - - let state = state_box.take().expect( - "reentrant call to with_element_state for the same state type and element id", - ); - let (result, state) = f(Some(state), self); - state_box.replace(state); - self.next_frame.element_states.insert( - key, - ElementStateBox { - inner: state_box, - #[cfg(debug_assertions)] - type_name, - }, - ); - result - } else { - let (result, state) = f(None, self); - self.next_frame.element_states.insert( - key, - ElementStateBox { - inner: Box::new(Some(state)), - #[cfg(debug_assertions)] - type_name: std::any::type_name::(), - }, - ); - result - } - } - - /// A variant of `with_element_state` that allows the element's id to be optional. This is a convenience - /// method for elements where the element id may or may not be assigned. Prefer using `with_element_state` - /// when the element is guaranteed to have an id. - /// - /// The first option means 'no ID provided' - /// The second option means 'not yet initialized' - pub fn with_optional_element_state( - &mut self, - global_id: Option<&GlobalElementId>, - f: impl FnOnce(Option>, &mut Self) -> (R, Option), - ) -> R - where - S: 'static, - { - self.invalidator.debug_assert_paint_or_prepaint(); - - if let Some(global_id) = global_id { - self.with_element_state(global_id, |state, cx| { - let (result, state) = f(Some(state), cx); - let state = - state.expect("you must return some state when you pass some element id"); - (result, state) - }) - } else { - let (result, state) = f(None, self); - debug_assert!( - state.is_none(), - "you must not return an element state when passing None for the global id" - ); - result - } - } - - /// Executes the given closure within the context of a tab group. - #[inline] - pub fn with_tab_group(&mut self, index: Option, f: impl FnOnce(&mut Self) -> R) -> R { - if let Some(index) = index { - self.next_frame.tab_stops.begin_group(index); - let result = f(self); - self.next_frame.tab_stops.end_group(); - result - } else { - f(self) - } - } - - /// Defers the drawing of the given element, scheduling it to be painted on top of the currently-drawn tree - /// at a later time. The `priority` parameter determines the drawing order relative to other deferred elements, - /// with higher values being drawn on top. - /// - /// When `content_mask` is provided, the deferred element will be clipped to that region during - /// both prepaint and paint. When `None`, no additional clipping is applied. - /// - /// This method should only be called as part of the prepaint phase of element drawing. - pub fn defer_draw( - &mut self, - element: AnyElement, - absolute_offset: Point, - priority: usize, - content_mask: Option, - ) { - self.invalidator.debug_assert_prepaint(); - let parent_node = self.next_frame.dispatch_tree.active_node_id().unwrap(); - self.next_frame.deferred_draws.push(DeferredDraw { - current_view: self.current_view(), - parent_node, - element_id_stack: self.element_id_stack.clone(), - text_style_stack: self.text_style_stack.clone(), - content_mask, - rem_size: self.rem_size(), - priority, - element: Some(element), - absolute_offset, - prepaint_range: PrepaintStateIndex::default()..PrepaintStateIndex::default(), - paint_range: PaintIndex::default()..PaintIndex::default(), - }); - } - - /// Creates a new painting layer for the specified bounds. A "layer" is a batch - /// of geometry that are non-overlapping and have the same draw order. This is typically used - /// for performance reasons. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_layer(&mut self, bounds: Bounds, f: impl FnOnce(&mut Self) -> R) -> R { - self.invalidator.debug_assert_paint(); - - let content_mask = self.content_mask(); - let clipped_bounds = bounds.intersect(&content_mask.bounds); - if !clipped_bounds.is_empty() { - self.next_frame - .scene - .push_layer(self.cover_bounds(clipped_bounds)); - } - - let result = f(self); - - if !clipped_bounds.is_empty() { - self.next_frame.scene.pop_layer(); - } - - result - } - - /// Paint the drop (non-inset) shadows from `shadows` into the scene at the current - /// z-index. Inset shadows are skipped; paint those with [`Self::paint_inset_shadows`] - /// after the element's background so they layer on top of the fill. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_drop_shadows( - &mut self, - bounds: Bounds, - corner_radii: Corners, - shadows: &[BoxShadow], - ) { - self.invalidator.debug_assert_paint(); - - let scale_factor = self.scale_factor(); - let content_mask = self.snapped_content_mask(); - let opacity = self.element_opacity(); - let element_bounds = self.cover_bounds(bounds); - let element_corner_radii = corner_radii.scale(scale_factor); - for shadow in shadows { - if shadow.inset { - continue; - } - let shadow_bounds = (bounds + shadow.offset).dilate(shadow.spread_radius); - // CSS expands a rounded shadow's curve together with its border - // box. Keeping the element radius on a spread shadow makes the - // outside edge visibly squarer than the control (most obvious on - // focus rings, whose two spread layers are the control's outline - // and offset gap). The shadow quad is larger by `spread` on every - // side, so the corresponding corner radius grows by the same - // amount. Negative spreads shrink the curve and clamp at zero. - let shadow_corner_radii = corner_radii.map(|radius| { - (*radius + shadow.spread_radius).max(Pixels::ZERO) - }); - self.next_frame.scene.insert_primitive(Shadow { - order: 0, - blur_radius: shadow.blur_radius.scale(scale_factor), - bounds: self.cover_bounds(shadow_bounds), - content_mask, - corner_radii: shadow_corner_radii.scale(scale_factor), - color: shadow.color.opacity(opacity), - element_bounds, - element_corner_radii, - inset: 0, - pad: 0, - }); - } - } - - /// Paint the inset shadows from `shadows` into the scene at the current z-index. Should - /// be called after the element's background so the shadow layers on top of the fill. - /// Drop shadows are skipped; paint those with [`Self::paint_drop_shadows`] before the background. - pub fn paint_inset_shadows( - &mut self, - bounds: Bounds, - corner_radii: Corners, - shadows: &[BoxShadow], - ) { - self.invalidator.debug_assert_paint(); - - let scale_factor = self.scale_factor(); - let content_mask = self.snapped_content_mask(); - let opacity = self.element_opacity(); - let element_bounds = self.cover_bounds(bounds); - let element_corner_radii = corner_radii.scale(scale_factor); - for shadow in shadows { - if !shadow.inset { - continue; - } - let hole = (bounds + shadow.offset).dilate(-shadow.spread_radius); - // Clamp at zero so a large spread can't produce negative radii, which would - // break the SDF in the shader. - let zero = Pixels::ZERO; - let hole_corner_radii = Corners { - top_left: (corner_radii.top_left - shadow.spread_radius).max(zero), - top_right: (corner_radii.top_right - shadow.spread_radius).max(zero), - bottom_right: (corner_radii.bottom_right - shadow.spread_radius).max(zero), - bottom_left: (corner_radii.bottom_left - shadow.spread_radius).max(zero), - }; - self.next_frame.scene.insert_primitive(Shadow { - order: 0, - blur_radius: shadow.blur_radius.scale(scale_factor), - bounds: self.cover_bounds(hole), - content_mask, - corner_radii: hole_corner_radii.scale(scale_factor), - color: shadow.color.opacity(opacity), - element_bounds, - element_corner_radii, - inset: 1, - pad: 0, - }); - } - } - - fn largest_border_interior(quad: &Quad) -> Bounds { - let radii = &quad.corner_radii; - let widths = &quad.border_widths; - let edge_radii = Edges { - top: radii.top_left.max(radii.top_right), - right: radii.top_right.max(radii.bottom_right), - bottom: radii.bottom_left.max(radii.bottom_right), - left: radii.top_left.max(radii.bottom_left), - }; - - let antialias_inset = point(ScaledPixels(1.0), ScaledPixels(1.0)); - let inset_bounds = |top_left_inset, bottom_right_inset| { - Bounds::from_corners( - quad.bounds.origin + top_left_inset + antialias_inset, - quad.bounds.bottom_right() - bottom_right_inset - antialias_inset, - ) - }; - - // Rounded corners need only be excluded on one axis. Either candidate - // is empty of border pixels, so use the larger interior. - let horizontal_band = inset_bounds( - point(widths.left, widths.top.max(edge_radii.top)), - point(widths.right, widths.bottom.max(edge_radii.bottom)), - ); - let vertical_band = inset_bounds( - point(widths.left.max(edge_radii.left), widths.top), - point(widths.right.max(edge_radii.right), widths.bottom), - ); - - let area = |bounds: &Bounds| { - bounds.size.width.0.max(0.) * bounds.size.height.0.max(0.) - }; - if area(&horizontal_band) >= area(&vertical_band) { - horizontal_band - } else { - vertical_band - } - } - - /// Paint one or more quads into the scene for the next frame at the current stacking context. - /// Quads are colored rectangular regions with an optional background, border, and corner radius. - /// see [`fill`], [`outline`], and [`quad`] to construct this type. - /// - /// This method should only be called as part of the paint phase of element drawing. - /// - /// Note that the `quad.corner_radii` are allowed to exceed the bounds, creating sharp corners - /// where the circular arcs meet. This will not display well when combined with dashed borders. - /// Use `Corners::clamp_radii_for_quad_size` if the radii should fit within the bounds. - pub fn paint_quad(&mut self, quad: PaintQuad) { - self.invalidator.debug_assert_paint(); - - let opacity = self.element_opacity(); - let snapped_bounds = self.snap_bounds(quad.bounds); - let snapped_border_widths = self.snap_border_widths(quad.border_widths); - let quad = Quad { - order: 0, - bounds: snapped_bounds, - content_mask: self.snapped_content_mask(), - background: quad.background.opacity(opacity), - border_color: quad.border_color.opacity(opacity), - corner_radii: quad.corner_radii.scale(self.scale_factor()), - border_widths: snapped_border_widths, - border_style: quad.border_style, - }; - - if !quad.background.is_transparent() { - self.next_frame.scene.insert_primitive(quad); - return; - } - - // Splitting a border-only quad around its empty interior avoids shading - // every transparent pixel inside large outlines. - let outer_bounds = quad.bounds; - let inner_bounds = Self::largest_border_interior(&quad); - - if inner_bounds.is_empty() { - self.next_frame.scene.insert_primitive(quad); - return; - } - - let strips = [ - // Top - Bounds::from_corners( - outer_bounds.origin, - point(outer_bounds.right(), inner_bounds.top()), - ), - // Bottom - Bounds::from_corners( - point(outer_bounds.left(), inner_bounds.bottom()), - outer_bounds.bottom_right(), - ), - // Left - Bounds::from_corners( - point(outer_bounds.left(), inner_bounds.top()), - inner_bounds.bottom_left(), - ), - // Right - Bounds::from_corners( - inner_bounds.top_right(), - point(outer_bounds.right(), inner_bounds.bottom()), - ), - ]; - - for strip in strips { - let content_mask_bounds = quad.content_mask.bounds.intersect(&strip); - if !content_mask_bounds.is_empty() { - self.next_frame.scene.insert_primitive(Quad { - content_mask: ContentMask { - bounds: content_mask_bounds, - ..quad.content_mask - }, - ..quad - }); - } - } - } - - /// Paint the given `Path` into the scene for the next frame at the current z-index. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_path(&mut self, mut path: Path, color: impl Into) { - self.invalidator.debug_assert_paint(); - - let scale_factor = self.scale_factor(); - let content_mask = self.snapped_content_mask(); - let opacity = self.element_opacity(); - let color: Background = color.into(); - path.color = color.opacity(opacity); - let mut path = path.scale(scale_factor); - path.content_mask = content_mask; - self.next_frame.scene.insert_primitive(path); - } - - /// Paint an underline into the scene for the next frame at the current z-index. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_underline( - &mut self, - origin: Point, - width: Pixels, - style: &UnderlineStyle, - ) { - self.invalidator.debug_assert_paint(); - - let scale_factor = self.scale_factor(); - let thickness = self.snap_stroke(style.thickness); - let height = if style.wavy { - ScaledPixels(thickness.0 * 3.) - } else { - thickness - }; - let bounds = Bounds { - origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))), - size: size(self.snap_stroke(width), height), - }; - let element_opacity = self.element_opacity(); - - let content_mask = self.snapped_content_mask(); - self.next_frame.scene.insert_primitive(Underline { - order: 0, - pad: 0, - bounds, - content_mask, - color: style.color.unwrap_or_default().opacity(element_opacity), - thickness, - wavy: style.wavy.into(), - }); - } - - /// Paint a strikethrough into the scene for the next frame at the current z-index. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_strikethrough( - &mut self, - origin: Point, - width: Pixels, - style: &StrikethroughStyle, - ) { - self.invalidator.debug_assert_paint(); - - let scale_factor = self.scale_factor(); - let height = style.thickness; - let bounds = Bounds { - origin: origin.map(|c| ScaledPixels(round_to_device_pixel(c.0, scale_factor))), - size: size(self.snap_stroke(width), self.snap_stroke(height)), - }; - let opacity = self.element_opacity(); - - let content_mask = self.snapped_content_mask(); - self.next_frame.scene.insert_primitive(Underline { - order: 0, - pad: 0, - bounds, - content_mask, - thickness: self.snap_stroke(style.thickness), - color: style.color.unwrap_or_default().opacity(opacity), - wavy: false.into(), - }); - } - - /// Paints a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index. - /// - /// The y component of the origin is the baseline of the glyph. - /// You should generally prefer to use the [`ShapedLine::paint`](crate::ShapedLine::paint) or - /// [`WrappedLine::paint`](crate::WrappedLine::paint) methods in the [`TextSystem`](crate::TextSystem). - /// This method is only useful if you need to paint a single glyph that has already been shaped. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_glyph( - &mut self, - origin: Point, - font_id: FontId, - glyph_id: GlyphId, - font_size: Pixels, - color: Hsla, - ) -> Result<()> { - self.invalidator.debug_assert_paint(); - - let element_opacity = self.element_opacity(); - let scale_factor = self.scale_factor(); - let glyph_origin = origin.scale(scale_factor); - - let quantized_origin = Point::new( - round_half_toward_zero(glyph_origin.x.0 * SUBPIXEL_VARIANTS_X as f32) - / SUBPIXEL_VARIANTS_X as f32, - round_half_toward_zero(glyph_origin.y.0 * SUBPIXEL_VARIANTS_Y as f32) - / SUBPIXEL_VARIANTS_Y as f32, - ); - let subpixel_variant = Point::new( - (quantized_origin.x.fract() * SUBPIXEL_VARIANTS_X as f32) as u8, - (quantized_origin.y.fract() * SUBPIXEL_VARIANTS_Y as f32) as u8, - ); - let integer_origin = quantized_origin.map(|c| ScaledPixels(c.trunc())); - let subpixel_rendering = self.should_use_subpixel_rendering(font_id, font_size); - let dilation = self.text_system().glyph_dilation_for_color(color); - let params = RenderGlyphParams { - font_id, - glyph_id, - font_size, - subpixel_variant, - scale_factor, - is_emoji: false, - subpixel_rendering, - dilation, - }; - - let raster_bounds = self.text_system().raster_bounds(¶ms)?; - if !raster_bounds.is_zero() { - let tile = self - .sprite_atlas - .get_or_insert_with(¶ms.clone().into(), &mut || { - let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?; - Ok(Some((size, Cow::Owned(bytes)))) - })? - .expect("Callback above only errors or returns Some"); - let bounds = Bounds { - origin: integer_origin + raster_bounds.origin.map(Into::into), - size: tile.bounds.size.map(Into::into), - }; - let content_mask = self.snapped_content_mask(); - - if subpixel_rendering { - self.next_frame.scene.insert_primitive(SubpixelSprite { - order: 0, - pad: 0, - bounds, - content_mask, - color: color.opacity(element_opacity), - tile, - transformation: TransformationMatrix::unit(), - }); - } else { - self.next_frame.scene.insert_primitive(MonochromeSprite { - order: 0, - pad: 0, - bounds, - content_mask, - color: color.opacity(element_opacity), - tile, - transformation: TransformationMatrix::unit(), - }); - } - } - Ok(()) - } - - fn should_use_subpixel_rendering(&self, font_id: FontId, font_size: Pixels) -> bool { - if self.platform_window.background_appearance() != WindowBackgroundAppearance::Opaque { - return false; - } - - if !self.platform_window.is_subpixel_rendering_supported() { - return false; - } - - let mode = match self.text_rendering_mode.get() { - TextRenderingMode::PlatformDefault => self - .text_system() - .recommended_rendering_mode(font_id, font_size), - mode => mode, - }; - - mode == TextRenderingMode::Subpixel - } - - /// Paints an emoji glyph into the scene for the next frame at the current z-index. - /// - /// The y component of the origin is the baseline of the glyph. - /// You should generally prefer to use the [`ShapedLine::paint`](crate::ShapedLine::paint) or - /// [`WrappedLine::paint`](crate::WrappedLine::paint) methods in the [`TextSystem`](crate::TextSystem). - /// This method is only useful if you need to paint a single emoji that has already been shaped. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_emoji( - &mut self, - origin: Point, - font_id: FontId, - glyph_id: GlyphId, - font_size: Pixels, - ) -> Result<()> { - self.invalidator.debug_assert_paint(); - - let scale_factor = self.scale_factor(); - let glyph_origin = origin.scale(scale_factor); - let integer_origin = glyph_origin.map(|c| ScaledPixels(round_half_toward_zero(c.0))); - let params = RenderGlyphParams { - font_id, - glyph_id, - font_size, - subpixel_variant: Default::default(), - scale_factor, - is_emoji: true, - subpixel_rendering: false, - dilation: 0, - }; - - let raster_bounds = self.text_system().raster_bounds(¶ms)?; - if !raster_bounds.is_zero() { - let tile = self - .sprite_atlas - .get_or_insert_with(¶ms.clone().into(), &mut || { - let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?; - Ok(Some((size, Cow::Owned(bytes)))) - })? - .expect("Callback above only errors or returns Some"); - - let bounds = Bounds { - origin: integer_origin + raster_bounds.origin.map(Into::into), - size: tile.bounds.size.map(Into::into), - }; - let content_mask = self.snapped_content_mask(); - let opacity = self.element_opacity(); - - self.next_frame.scene.insert_primitive(PolychromeSprite { - order: 0, - pad: 0, - grayscale: false.into(), - bounds, - corner_radii: Default::default(), - content_mask, - tile, - opacity, - }); - } - Ok(()) - } - - /// Paint a monochrome SVG into the scene for the next frame at the current stacking context. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn paint_svg( - &mut self, - bounds: Bounds, - path: SharedString, - mut data: Option<&[u8]>, - transformation: TransformationMatrix, - color: Hsla, - cx: &App, - ) -> Result<()> { - self.invalidator.debug_assert_paint(); - - let element_opacity = self.element_opacity(); - let bounds = self.snap_bounds(bounds); - - let params = RenderSvgParams { - path, - size: bounds.size.map(|pixels| { - DevicePixels::from((pixels.0 * SMOOTH_SVG_SCALE_FACTOR).ceil() as i32) - }), - }; - - let Some(tile) = - self.sprite_atlas - .get_or_insert_with(¶ms.clone().into(), &mut || { - let Some((size, bytes)) = cx.svg_renderer.render_alpha_mask(¶ms, data)? - else { - return Ok(None); - }; - Ok(Some((size, Cow::Owned(bytes)))) - })? - else { - return Ok(()); - }; - let content_mask = self.snapped_content_mask(); - let svg_bounds = Bounds { - origin: bounds.center() - - Point::new( - ScaledPixels(tile.bounds.size.width.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.), - ScaledPixels(tile.bounds.size.height.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.), - ), - size: tile - .bounds - .size - .map(|value| ScaledPixels(value.0 as f32 / SMOOTH_SVG_SCALE_FACTOR)), - }; - let final_bounds = svg_bounds - .map_origin(|value| ScaledPixels(round_half_toward_zero(value.0))) - .map_size(|size| size.ceil()); - - self.next_frame.scene.insert_primitive(MonochromeSprite { - order: 0, - pad: 0, - bounds: final_bounds, - content_mask, - color: color.opacity(element_opacity), - tile, - transformation, - }); - - Ok(()) - } - - /// Paint an image into the scene for the next frame at the current z-index. - /// This method will panic if the frame_index is not valid - /// - /// This method should only be called as part of the paint phase of element drawing. - /// Paint an image into `bounds`, positioning and scaling it according to `image_bounds`. - /// - /// The visible region rendered is `bounds.intersect(&image_bounds)`, with `corner_radii` - /// applied to `bounds`. - pub fn paint_image( - &mut self, - bounds: Bounds, - image_bounds: Bounds, - corner_radii: Corners, - data: Arc, - frame_index: usize, - grayscale: bool, - ) -> Result<()> { - self.invalidator.debug_assert_paint(); - - let visible_bounds = bounds.intersect(&image_bounds); - if visible_bounds.size.width <= Pixels::ZERO || visible_bounds.size.height <= Pixels::ZERO { - return Ok(()); - } - if image_bounds.size.width <= Pixels::ZERO || image_bounds.size.height <= Pixels::ZERO { - return Ok(()); - } - - let params = RenderImageParams { - image_id: data.id, - frame_index, - }; - - let tile = self - .sprite_atlas - .get_or_insert_with(¶ms.into(), &mut || { - Ok(Some(( - data.size(frame_index), - Cow::Borrowed( - data.as_bytes(frame_index) - .expect("It's the caller's job to pass a valid frame index"), - ), - ))) - })? - .expect("Callback above only returns Some"); - - let visible_bounds_snapped = self.snap_bounds(visible_bounds); - - let sub_tile = if visible_bounds == image_bounds { - tile - } else { - let x_offset_ratio = - (visible_bounds.origin.x - image_bounds.origin.x) / image_bounds.size.width; - let y_offset_ratio = - (visible_bounds.origin.y - image_bounds.origin.y) / image_bounds.size.height; - let width_ratio = visible_bounds.size.width / image_bounds.size.width; - let height_ratio = visible_bounds.size.height / image_bounds.size.height; - - let tile_origin_x = tile.bounds.origin.x.0; - let tile_origin_y = tile.bounds.origin.y.0; - let tile_width = tile.bounds.size.width.0; - let tile_height = tile.bounds.size.height.0; - - let sub_origin_x = tile_origin_x + (x_offset_ratio * tile_width as f32).round() as i32; - let sub_origin_y = tile_origin_y + (y_offset_ratio * tile_height as f32).round() as i32; - let sub_width = (width_ratio * tile_width as f32).round() as i32; - let sub_height = (height_ratio * tile_height as f32).round() as i32; - - let max_x = tile_origin_x + tile_width; - let max_y = tile_origin_y + tile_height; - - let clamped_origin_x = sub_origin_x.clamp(tile_origin_x, max_x); - let clamped_origin_y = sub_origin_y.clamp(tile_origin_y, max_y); - let clamped_width = sub_width.min(max_x - clamped_origin_x).max(0); - let clamped_height = sub_height.min(max_y - clamped_origin_y).max(0); - - AtlasTile { - bounds: Bounds { - origin: point( - DevicePixels(clamped_origin_x), - DevicePixels(clamped_origin_y), - ), - size: size(DevicePixels(clamped_width), DevicePixels(clamped_height)), - }, - ..tile - } - }; - - let content_mask = self.snapped_content_mask(); - let corner_radii = corner_radii - .clamp_radii_for_quad_size(visible_bounds.size) - .scale(self.scale_factor()); - let opacity = self.element_opacity(); - - self.next_frame.scene.insert_primitive(PolychromeSprite { - order: 0, - pad: 0, - grayscale: grayscale.into(), - bounds: visible_bounds_snapped, - content_mask, - corner_radii, - tile: sub_tile, - opacity, - }); - Ok(()) - } - - /// Paint a surface into the scene for the next frame at the current z-index. - /// - /// This method should only be called as part of the paint phase of element drawing. - #[cfg(target_os = "macos")] - pub fn paint_surface(&mut self, bounds: Bounds, image_buffer: CVPixelBuffer) { - use crate::PaintSurface; - - self.invalidator.debug_assert_paint(); - - let bounds = self.snap_bounds(bounds); - let content_mask = self.snapped_content_mask(); - self.next_frame.scene.insert_primitive(PaintSurface { - order: 0, - bounds, - content_mask, - image_buffer, - }); - } - - /// Removes an image from the sprite atlas. - pub fn drop_image(&mut self, data: Arc) -> Result<()> { - for frame_index in 0..data.frame_count() { - let params = RenderImageParams { - image_id: data.id, - frame_index, - }; - - self.sprite_atlas.remove(¶ms.clone().into()); - } - - Ok(()) - } - - /// Returns whether every frame of an image is present in the sprite atlas. - #[cfg(any(test, feature = "test-support"))] - pub fn has_image_atlas_entry(&self, data: &RenderImage) -> bool { - data.frame_count() > 0 - && (0..data.frame_count()).all(|frame_index| { - self.sprite_atlas.contains( - &RenderImageParams { - image_id: data.id, - frame_index, - } - .into(), - ) - }) - } - - /// Add a node to the layout tree for the current frame. Takes the `Style` of the element for which - /// layout is being requested, along with the layout ids of any children. This method is called during - /// calls to the [`Element::request_layout`] trait method and enables any element to participate in layout. - /// - /// This method should only be called as part of the request_layout or prepaint phase of element drawing. - #[must_use] - pub fn request_layout( - &mut self, - style: Style, - children: impl IntoIterator, - cx: &mut App, - ) -> LayoutId { - self.invalidator.debug_assert_prepaint(); - - cx.layout_id_buffer.clear(); - cx.layout_id_buffer.extend(children); - let rem_size = self.rem_size(); - let scale_factor = self.scale_factor(); - - self.layout_engine.as_mut().unwrap().request_layout( - style, - rem_size, - scale_factor, - &cx.layout_id_buffer, - ) - } - - /// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children, - /// this variant takes a function that is invoked during layout so you can use arbitrary logic to - /// determine the element's size. One place this is used internally is when measuring text. - /// - /// The given closure is invoked at layout time with the known dimensions and available space and - /// returns a `Size`. - /// - /// This method should only be called as part of the request_layout or prepaint phase of element drawing. - pub fn request_measured_layout(&mut self, style: Style, measure: F) -> LayoutId - where - F: Fn(Size>, Size, &mut Window, &mut App) -> Size - + 'static, - { - self.invalidator.debug_assert_prepaint(); - - let rem_size = self.rem_size(); - let scale_factor = self.scale_factor(); - self.layout_engine - .as_mut() - .unwrap() - .request_measured_layout(style, rem_size, scale_factor, measure) - } - - /// Compute the layout for the given id within the given available space. - /// This method is called for its side effect, typically by the framework prior to painting. - /// After calling it, you can request the bounds of the given layout node id or any descendant. - /// - /// This method should only be called as part of the prepaint phase of element drawing. - pub fn compute_layout( - &mut self, - layout_id: LayoutId, - available_space: Size, - cx: &mut App, - ) { - self.invalidator.debug_assert_prepaint(); - - let mut layout_engine = self.layout_engine.take().unwrap(); - layout_engine.compute_layout(layout_id, available_space, self, cx); - self.layout_engine = Some(layout_engine); - } - - /// Obtain the bounds computed for the given LayoutId relative to the window. This method will usually be invoked by - /// GPUI itself automatically in order to pass your element its `Bounds` automatically. - /// - /// This method should only be called as part of element drawing. - pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds { - self.invalidator.debug_assert_prepaint(); - - let scale_factor = self.scale_factor(); - let mut bounds = self - .layout_engine - .as_mut() - .unwrap() - .layout_bounds(layout_id, scale_factor) - .map(Into::into); - let snapped_offset = self.pixel_snap_point(self.element_offset()); - bounds.origin += snapped_offset; - bounds - } - - /// This method should be called during `prepaint`. You can use - /// the returned [Hitbox] during `paint` or in an event handler - /// to determine whether the inserted hitbox was the topmost. - /// - /// This method should only be called as part of the prepaint phase of element drawing. - pub fn insert_hitbox(&mut self, bounds: Bounds, behavior: HitboxBehavior) -> Hitbox { - self.invalidator.debug_assert_prepaint(); - - let content_mask = self.content_mask(); - let mut id = self.next_hitbox_id; - self.next_hitbox_id = self.next_hitbox_id.next(); - let hitbox = Hitbox { - id, - bounds, - content_mask, - behavior, - }; - self.next_frame.hitboxes.push(hitbox.clone()); - hitbox - } - - /// Set a hitbox which will act as a control area of the platform window. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn insert_window_control_hitbox(&mut self, area: WindowControlArea, hitbox: Hitbox) { - self.invalidator.debug_assert_paint(); - self.next_frame.window_control_hitboxes.push((area, hitbox)); - } - - /// Sets the key context for the current element. This context will be used to translate - /// keybindings into actions. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn set_key_context(&mut self, context: KeyContext) { - self.invalidator.debug_assert_paint(); - self.next_frame.dispatch_tree.set_key_context(context); - } - - /// Sets the focus handle for the current element. This handle will be used to manage focus state - /// and keyboard event dispatch for the element. - /// - /// This method should only be called as part of the prepaint phase of element drawing. - pub fn set_focus_handle(&mut self, focus_handle: &FocusHandle, _: &App) { - self.invalidator.debug_assert_prepaint(); - if focus_handle.is_focused(self) { - self.next_frame.focus = Some(focus_handle.id); - } - self.next_frame.dispatch_tree.set_focus_id(focus_handle.id); - } - - /// Sets the view id for the current element, which will be used to manage view caching. - /// - /// This method should only be called as part of element prepaint. We plan on removing this - /// method eventually when we solve some issues that require us to construct editor elements - /// directly instead of always using editors via views. - pub fn set_view_id(&mut self, view_id: EntityId) { - self.invalidator.debug_assert_prepaint(); - self.next_frame.dispatch_tree.set_view_id(view_id); - } - - /// Get the entity ID for the currently rendering view - pub fn current_view(&self) -> EntityId { - self.invalidator.debug_assert_paint_or_prepaint(); - self.rendered_entity_stack.last().copied().unwrap() - } - - #[inline] - pub(crate) fn with_rendered_view( - &mut self, - id: EntityId, - f: impl FnOnce(&mut Self) -> R, - ) -> R { - self.rendered_entity_stack.push(id); - let result = f(self); - self.rendered_entity_stack.pop(); - result - } - - /// Executes the provided function with the specified image cache. - pub fn with_image_cache(&mut self, image_cache: Option, f: F) -> R - where - F: FnOnce(&mut Self) -> R, - { - if let Some(image_cache) = image_cache { - self.image_cache_stack.push(image_cache); - let result = f(self); - self.image_cache_stack.pop(); - result - } else { - f(self) - } - } - - /// Sets an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the - /// platform to receive textual input with proper integration with concerns such - /// as IME interactions. This handler will be active for the upcoming frame until the following frame is - /// rendered. - /// - /// This method should only be called as part of the paint phase of element drawing. - /// - /// [element_input_handler]: crate::ElementInputHandler - pub fn handle_input( - &mut self, - focus_handle: &FocusHandle, - input_handler: impl InputHandler, - cx: &App, - ) { - self.invalidator.debug_assert_paint(); - - if focus_handle.is_focused(self) { - let cx = self.to_async(cx); - self.next_frame - .input_handlers - .push(Some(PlatformInputHandler::new(cx, Box::new(input_handler)))); - } - } - - /// Forwards the focused input handler's [`TextInputConfiguration`] to the - /// platform window when it differs from the last forwarded value. With no - /// input handler the default configuration applies, so a field's - /// preferences don't outlive its focus. - fn apply_text_input_configuration(&mut self, cx: &mut App) { - let configuration = match self.platform_window.take_input_handler() { - Some(mut input_handler) => { - let configuration = input_handler.text_input_configuration(self, cx); - self.platform_window.set_input_handler(input_handler); - configuration - } - None => TextInputConfiguration::default(), - }; - if self.last_text_input_configuration.as_ref() != Some(&configuration) { - self.platform_window - .set_text_input_configuration(configuration.clone()); - self.last_text_input_configuration = Some(configuration); - } - } - - /// Register a mouse event listener on the window for the next frame. The type of event - /// is determined by the first parameter of the given listener. When the next frame is rendered - /// the listener will be cleared. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn on_mouse_event( - &mut self, - mut listener: impl FnMut(&Event, DispatchPhase, &mut Window, &mut App) + 'static, - ) { - self.invalidator.debug_assert_paint(); - - self.next_frame.mouse_listeners.push(Some(Box::new( - move |event: &dyn Any, phase: DispatchPhase, window: &mut Window, cx: &mut App| { - if let Some(event) = event.downcast_ref() { - listener(event, phase, window, cx) - } - }, - ))); - } - - /// Register a key event listener on this node for the next frame. The type of event - /// is determined by the first parameter of the given listener. When the next frame is rendered - /// the listener will be cleared. - /// - /// This is a fairly low-level method, so prefer using event handlers on elements unless you have - /// a specific need to register a listener yourself. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn on_key_event( - &mut self, - listener: impl Fn(&Event, DispatchPhase, &mut Window, &mut App) + 'static, - ) { - self.invalidator.debug_assert_paint(); - - self.next_frame.dispatch_tree.on_key_event(Rc::new( - move |event: &dyn Any, phase, window: &mut Window, cx: &mut App| { - if let Some(event) = event.downcast_ref::() { - listener(event, phase, window, cx) - } - }, - )); - } - - /// Register a modifiers changed event listener on the window for the next frame. - /// - /// This is a fairly low-level method, so prefer using event handlers on elements unless you have - /// a specific need to register a global listener. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn on_modifiers_changed( - &mut self, - listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static, - ) { - self.invalidator.debug_assert_paint(); - - self.next_frame.dispatch_tree.on_modifiers_changed(Rc::new( - move |event: &ModifiersChangedEvent, window: &mut Window, cx: &mut App| { - listener(event, window, cx) - }, - )); - } - - /// Register a listener to be called when the given focus handle or one of its descendants receives focus. - /// This does not fire if the given focus handle - or one of its descendants - was previously focused. - /// Returns a subscription and persists until the subscription is dropped. - pub fn on_focus_in( - &mut self, - handle: &FocusHandle, - cx: &mut App, - mut listener: impl FnMut(&mut Window, &mut App) + 'static, - ) -> Subscription { - let focus_id = handle.id; - let (subscription, activate) = - self.new_focus_listener(Box::new(move |event, window, cx| { - if event.is_focus_in(focus_id) { - listener(window, cx); - } - true - })); - cx.defer(move |_| activate()); - subscription - } - - /// Register a listener to be called when the given focus handle or one of its descendants loses focus. - /// Returns a subscription and persists until the subscription is dropped. - pub fn on_focus_out( - &mut self, - handle: &FocusHandle, - cx: &mut App, - mut listener: impl FnMut(FocusOutEvent, &mut Window, &mut App) + 'static, - ) -> Subscription { - let focus_id = handle.id; - let (subscription, activate) = - self.new_focus_listener(Box::new(move |event, window, cx| { - if let Some(blurred_id) = event.previous_focus_path.last().copied() - && event.is_focus_out(focus_id) - { - let event = FocusOutEvent { - blurred: WeakFocusHandle { - id: blurred_id, - handles: Arc::downgrade(&cx.focus_handles), - }, - }; - listener(event, window, cx) - } - true - })); - cx.defer(move |_| activate()); - subscription - } - - fn reset_cursor_style(&self, cx: &mut App) { - // Set the cursor only if we're the active window. - if self.is_window_hovered() { - let style = self - .rendered_frame - .cursor_style(self) - .unwrap_or(CursorStyle::Arrow); - cx.platform.set_cursor_style(style); - } - } - - /// Dispatch a given keystroke as though the user had typed it. - /// You can create a keystroke with Keystroke::parse(""). - pub fn dispatch_keystroke(&mut self, keystroke: Keystroke, cx: &mut App) -> bool { - let keystroke = keystroke.with_simulated_ime(); - let result = self.dispatch_event( - PlatformInput::KeyDown(KeyDownEvent { - keystroke: keystroke.clone(), - is_held: false, - prefer_character_input: false, - }), - cx, - ); - if !result.propagate { - return true; - } - - if let Some(input) = keystroke.key_char - && let Some(mut input_handler) = self.platform_window.take_input_handler() - { - input_handler.dispatch_input(&input, self, cx); - self.platform_window.set_input_handler(input_handler); - return true; - } - - false - } - - /// Return a key binding string for an action, to display in the UI. Uses the highest precedence - /// binding for the action (last binding added to the keymap). - pub fn keystroke_text_for(&self, action: &dyn Action) -> String { - self.highest_precedence_binding_for_action(action) - .map(|binding| { - binding - .keystrokes() - .iter() - .map(ToString::to_string) - .collect::>() - .join(" ") - }) - .unwrap_or_else(|| action.name().to_string()) - } - - /// Dispatch a mouse, keyboard, or touch event on the window. - #[profiling::function] - pub fn dispatch_event(&mut self, event: PlatformInput, cx: &mut App) -> DispatchEventResult { - #[cfg(feature = "profiler")] - self.window_profiler.begin_input(event.kind_name()); - let update_count_before = self.invalidator.update_count(); - // Track input modality for focus-visible styling and hover suppression. - // Hover is suppressed during keyboard modality so that keyboard navigation - // doesn't show hover highlights on the item under the mouse cursor. - let old_modality = self.last_input_modality; - self.last_input_modality = match &event { - PlatformInput::KeyDown(_) => InputModality::Keyboard, - PlatformInput::MouseMove(_) | PlatformInput::MouseDown(_) => InputModality::Mouse, - PlatformInput::Touch(_) => InputModality::Touch, - _ => self.last_input_modality, - }; - if self.last_input_modality != old_modality { - self.refresh(); - } - - // Handlers may set this to false by calling `stop_propagation`. - cx.propagate_event = true; - // Handlers may set this to true by calling `prevent_default`. - self.default_prevented = false; - - let event = match event { - // Track the mouse position with our own state, since accessing the platform - // API for the mouse position can only occur on the main thread. - PlatformInput::MouseMove(mouse_move) => { - self.mouse_position = mouse_move.position; - self.modifiers = mouse_move.modifiers; - PlatformInput::MouseMove(mouse_move) - } - PlatformInput::MouseDown(mouse_down) => { - self.mouse_position = mouse_down.position; - self.modifiers = mouse_down.modifiers; - PlatformInput::MouseDown(mouse_down) - } - PlatformInput::MouseUp(mouse_up) => { - self.mouse_position = mouse_up.position; - self.modifiers = mouse_up.modifiers; - PlatformInput::MouseUp(mouse_up) - } - PlatformInput::MousePressure(mouse_pressure) => { - PlatformInput::MousePressure(mouse_pressure) - } - PlatformInput::MouseExited(mouse_exited) => { - self.modifiers = mouse_exited.modifiers; - PlatformInput::MouseExited(mouse_exited) - } - PlatformInput::ModifiersChanged(modifiers_changed) => { - self.modifiers = modifiers_changed.modifiers; - self.capslock = modifiers_changed.capslock; - PlatformInput::ModifiersChanged(modifiers_changed) - } - PlatformInput::ScrollWheel(scroll_wheel) => { - self.mouse_position = scroll_wheel.position; - self.modifiers = scroll_wheel.modifiers; - PlatformInput::ScrollWheel(scroll_wheel) - } - PlatformInput::Pinch(pinch) => { - self.mouse_position = pinch.position; - self.modifiers = pinch.modifiers; - PlatformInput::Pinch(pinch) - } - // Translate dragging and dropping of external files from the operating system - // to internal drag and drop events. - PlatformInput::FileDrop(file_drop) => match file_drop { - FileDropEvent::Entered { position, paths } => { - self.mouse_position = position; - let source_window = self.handle.window_id(); - if !cx.restore_platform_drag(source_window) && cx.active_drag.is_none() { - cx.active_drag = Some(AnyDrag { - value: Arc::new(paths.clone()), - view: cx.new(|_| paths).into(), - cursor_offset: position, - cursor_style: None, - external_payload_source: None, - }); - } - PlatformInput::MouseMove(MouseMoveEvent { - position, - pressed_button: Some(MouseButton::Left), - modifiers: Modifiers::default(), - }) - } - FileDropEvent::Pending { position } => { - self.mouse_position = position; - PlatformInput::MouseMove(MouseMoveEvent { - position, - pressed_button: Some(MouseButton::Left), - modifiers: Modifiers::default(), - }) - } - FileDropEvent::Submit { position } => { - cx.activate(true); - self.mouse_position = position; - PlatformInput::MouseUp(MouseUpEvent { - button: MouseButton::Left, - position, - modifiers: Modifiers::default(), - click_count: 1, - }) - } - FileDropEvent::Exited => { - if !cx.hand_restored_drag_to_platform(self.handle.window_id()) { - cx.active_drag.take(); - } - self.refresh(); - PlatformInput::FileDrop(FileDropEvent::Exited) - } - FileDropEvent::Ended => { - cx.end_platform_drag(self.handle.window_id()); - self.refresh(); - PlatformInput::FileDrop(FileDropEvent::Ended) - } - }, - PlatformInput::Touch(touch) => PlatformInput::Touch(touch), - PlatformInput::LongPress(long_press) => { - self.mouse_position = if long_press.phase == crate::TouchPhase::Started { - long_press.start_position - } else { - long_press.position - }; - if long_press.phase == crate::TouchPhase::Started { - self.long_press_capture = None; - } - PlatformInput::LongPress(long_press) - } - PlatformInput::TouchDrag(touch_drag) => { - self.mouse_position = touch_drag.start_position; - PlatformInput::TouchDrag(touch_drag) - } - PlatformInput::KeyDown(_) | PlatformInput::KeyUp(_) => event, - }; - - if let Some(any_mouse_event) = event.mouse_event() { - self.dispatch_mouse_event(any_mouse_event, cx); - } else if let Some(any_key_event) = event.keyboard_event() { - self.dispatch_key_event(any_key_event, cx); - } else if let Some(touch_event) = event.touch_event() { - self.dispatch_touch_event(touch_event, cx); - } - if let PlatformInput::LongPress(long_press) = &event { - match long_press.phase { - crate::TouchPhase::Started if !self.default_prevented => { - self.long_press_capture = None; - } - crate::TouchPhase::Ended | crate::TouchPhase::Cancelled => { - self.long_press_capture = None; - } - crate::TouchPhase::Started | crate::TouchPhase::Moved => {} - } - } - - // Must run after the move is dispatched: the platform owns the gesture afterwards, so this - // is the last chance for drag listeners to see the pointer leave and reset their state. - self.promote_external_drag_to_platform(&event, cx); - - let caused_invalidation = self.invalidator.update_count() > update_count_before; - if caused_invalidation { - self.input_rate_tracker.borrow_mut().record_input(); - } - #[cfg(feature = "profiler")] - self.window_profiler.end_input(caused_invalidation); - - DispatchEventResult { - propagate: cx.propagate_event, - default_prevented: self.default_prevented, - } - } - - fn promote_external_drag_to_platform(&mut self, event: &PlatformInput, cx: &mut App) { - let PlatformInput::MouseMove(mouse_move) = event else { - return; - }; - if mouse_move.pressed_button != Some(MouseButton::Left) { - return; - } - if Bounds::new(Point::default(), self.viewport_size).contains(&mouse_move.position) { - return; - } - if !self.platform_window.can_start_external_drag() { - return; - } - let Some(payload_source) = cx - .active_drag - .as_mut() - .and_then(|drag| drag.external_payload_source.take()) - else { - return; - }; - let Some(payload) = payload_source(self, cx) else { - return; - }; - if self.platform_window.start_external_drag(&payload) - && cx.hand_active_drag_to_platform(self.handle.window_id()) - { - self.refresh(); - } - } - - /// Whether recognized touch pans may use the platform's predicted touch - /// positions ([`TouchEvent::predicted_position`]) to compensate for input - /// latency. Defaults to true. - pub fn touch_prediction_enabled(&self) -> bool { - self.touch_prediction_enabled - } - - /// Sets whether recognized touch pans may use the platform's predicted - /// touch positions. Disabling drops [`TouchEvent::predicted_position`] - /// before gesture recognition, so pans track only raw touch positions. - pub fn set_touch_prediction_enabled(&mut self, enabled: bool) { - self.touch_prediction_enabled = enabled; - } - - /// Runs the portable gesture recognizer over a raw touch event and - /// dispatches whatever it resolves (scroll steps, synthesized taps) - /// through the ordinary mouse-event path. - fn dispatch_touch_event(&mut self, event: &TouchEvent, cx: &mut App) { - let mut event = event.clone(); - if !self.touch_prediction_enabled { - event.predicted_position = None; - } - let recognized_gestures = self.touch_gestures.handle_event(&event); - if event.phase == crate::TouchPhase::Started - && let Some(touch_drag) = self.touch_gestures.offer_touch_drag(event.id) - { - self.dispatch_recognized_touch_gesture(touch_drag, cx); - } - if event.phase == crate::TouchPhase::Started - && self.touch_gestures.pending_long_press().is_some() - { - self.long_press_capture = None; - } - let mut tapped = false; - for gesture in recognized_gestures { - tapped |= matches!(gesture, RecognizedTouchGesture::Tap { .. }); - self.dispatch_recognized_touch_gesture(gesture, cx); - } - if event.phase == crate::TouchPhase::Started { - self.schedule_long_press_timer(cx); - } else if self.touch_gestures.pending_long_press().is_none() { - self.long_press_timer.take(); - } - // The platform's touch-release handler may inspect the input handler - // as soon as this dispatch returns (the web platform decides virtual - // keyboard visibility there, inside the user gesture). Input handlers - // are registered during draw, so draw now to make them reflect any - // focus change the tap just caused. - if tapped && self.invalidator.is_dirty() { - self.draw(cx).clear(cx); - } - if self.touch_gestures.has_momentum() { - self.schedule_touch_momentum_tick(); - } - } - - fn dispatch_recognized_touch_gesture(&mut self, gesture: RecognizedTouchGesture, cx: &mut App) { - match gesture { - RecognizedTouchGesture::Scroll(scroll_wheel) => { - self.mouse_position = scroll_wheel.position; - cx.propagate_event = true; - self.dispatch_mouse_event(&scroll_wheel, cx); - } - RecognizedTouchGesture::Tap { down, up } => { - self.mouse_position = up.position; - cx.propagate_event = true; - self.dispatch_mouse_event(&down, cx); - cx.propagate_event = true; - self.dispatch_mouse_event(&up, cx); - } - RecognizedTouchGesture::TouchDrag(touch_drag) => { - self.mouse_position = touch_drag.start_position; - cx.propagate_event = true; - self.default_prevented = false; - let started = touch_drag.phase == crate::TouchPhase::Started; - self.dispatch_mouse_event(&touch_drag, cx); - if started { - self.touch_gestures - .resolve_touch_drag(self.default_prevented); - } - } - RecognizedTouchGesture::LongPress(long_press) => { - self.mouse_position = if long_press.phase == crate::TouchPhase::Started { - long_press.start_position - } else { - long_press.position - }; - cx.propagate_event = true; - self.default_prevented = false; - let started = long_press.phase == crate::TouchPhase::Started; - let ended = matches!( - long_press.phase, - crate::TouchPhase::Ended | crate::TouchPhase::Cancelled - ); - self.dispatch_mouse_event(&long_press, cx); - if started { - let claimed = self.default_prevented; - self.touch_gestures.resolve_long_press(claimed); - if !claimed { - self.long_press_capture = None; - } - } - if ended { - self.long_press_capture = None; - } - } - } - } - - fn schedule_long_press_timer(&mut self, cx: &mut App) { - self.long_press_timer.take(); - let Some((touch_id, duration)) = self.touch_gestures.pending_long_press() else { - return; - }; - self.long_press_timer = Some(self.spawn(cx, async move |cx| { - cx.background_executor.timer(duration).await; - cx.update(move |window, cx| { - window.long_press_timer.take(); - if let Some(gesture) = window.touch_gestures.offer_long_press(touch_id) { - window.dispatch_recognized_touch_gesture(gesture, cx); - } - }) - .log_err(); - })); - } - - fn schedule_touch_momentum_tick(&mut self) { - self.on_next_frame(|window, cx| { - if let Some(gesture) = window.touch_gestures.tick_momentum() { - window.dispatch_recognized_touch_gesture(gesture, cx); - } - if window.touch_gestures.has_momentum() { - window.schedule_touch_momentum_tick(); - } - }); - } - - fn dispatch_mouse_event(&mut self, event: &dyn Any, cx: &mut App) { - let hit_test = self.rendered_frame.hit_test(self.mouse_position()); - if hit_test != self.mouse_hit_test { - self.mouse_hit_test = hit_test; - self.reset_cursor_style(cx); - } - - #[cfg(any(feature = "inspector", debug_assertions))] - if self.is_inspector_picking(cx) { - self.handle_inspector_mouse_event(event, cx); - // When inspector is picking, all other mouse handling is skipped. - return; - } - - let mut mouse_listeners = mem::take(&mut self.rendered_frame.mouse_listeners); - - // Capture phase, events bubble from back to front. Handlers for this phase are used for - // special purposes, such as detecting events outside of a given Bounds. - for listener in &mut mouse_listeners { - let listener = listener.as_mut().unwrap(); - listener(event, DispatchPhase::Capture, self, cx); - if !cx.propagate_event { - break; - } - } - - // Bubble phase, where most normal handlers do their work. - if cx.propagate_event { - for listener in mouse_listeners.iter_mut().rev() { - let listener = listener.as_mut().unwrap(); - listener(event, DispatchPhase::Bubble, self, cx); - if !cx.propagate_event { - break; - } - } - } - - self.rendered_frame.mouse_listeners = mouse_listeners; - - if cx.has_active_drag() { - if event.is::() { - // If this was a mouse move event, redraw the window so that the - // active drag can follow the mouse cursor. - self.refresh(); - } else if event.is::() { - // If this was a mouse up event, cancel the active drag and redraw - // the window. - cx.active_drag = None; - self.refresh(); - } - } - - // Auto-release pointer capture on mouse up - if event.is::() && self.captured_hitbox.is_some() { - self.captured_hitbox = None; - } - } - - fn dispatch_key_event(&mut self, event: &dyn Any, cx: &mut App) { - if self.invalidator.is_dirty() { - self.draw(cx).clear(cx); - } - - let node_id = self.focus_node_id_in_rendered_frame(self.focus); - let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id); - - let mut keystroke: Option = None; - - if let Some(event) = event.downcast_ref::() { - if event.modifiers.number_of_modifiers() == 0 - && self.pending_modifier.modifiers.number_of_modifiers() == 1 - && !self.pending_modifier.saw_other_input - { - let key = match self.pending_modifier.modifiers { - modifiers if modifiers.shift => Some("shift"), - modifiers if modifiers.control => Some("control"), - modifiers if modifiers.alt => Some("alt"), - modifiers if modifiers.platform => Some("platform"), - modifiers if modifiers.function => Some("function"), - _ => None, - }; - if let Some(key) = key { - keystroke = Some(Keystroke { - key: key.to_string(), - key_char: None, - modifiers: Modifiers::default(), - }); - } - } - - if self.pending_modifier.modifiers.number_of_modifiers() == 0 - && event.modifiers.number_of_modifiers() == 1 - { - self.pending_modifier.saw_other_input = false - } else if event.modifiers.number_of_modifiers() > 1 { - self.pending_modifier.saw_other_input = true - } - self.pending_modifier.modifiers = event.modifiers - } else if let Some(key_down_event) = event.downcast_ref::() { - self.pending_modifier.saw_other_input = true; - keystroke = Some(key_down_event.keystroke.clone()); - if key_down_event.keystroke.key_char.is_some() - && matches!( - cx.cursor_hide_mode, - CursorHideMode::OnTyping | CursorHideMode::OnTypingAndAction - ) - { - cx.platform.hide_cursor_until_mouse_moves(); - } - } - - let Some(keystroke) = keystroke else { - self.finish_dispatch_key_event(event, dispatch_path, self.context_stack(), cx); - return; - }; - - cx.propagate_event = true; - self.dispatch_keystroke_interceptors(event, self.context_stack(), cx); - if !cx.propagate_event { - self.finish_dispatch_key_event(event, dispatch_path, self.context_stack(), cx); - return; - } - - let mut currently_pending = self.pending_input.take().unwrap_or_default(); - if currently_pending.focus.is_some() && currently_pending.focus != self.focus { - currently_pending = PendingInput::default(); - } - - let match_result = self.rendered_frame.dispatch_tree.dispatch_key( - currently_pending.keystrokes, - keystroke, - &dispatch_path, - ); - - if !match_result.to_replay.is_empty() { - self.replay_pending_input(match_result.to_replay, cx); - cx.propagate_event = true; - } - - if !match_result.pending.is_empty() { - let previous_timeout = currently_pending.timeout.take(); - currently_pending.keystrokes = match_result.pending; - currently_pending.focus = self.focus; - - let text_input_requires_timeout = event - .downcast_ref::() - .filter(|key_down| key_down.keystroke.key_char.is_some()) - .and_then(|_| self.platform_window.take_input_handler()) - .map_or(false, |mut input_handler| { - let accepts = input_handler.accepts_text_input(self, cx); - self.platform_window.set_input_handler(input_handler); - accepts - }); - - let needs_timeout = previous_timeout.is_some() - || match_result.pending_has_binding - || text_input_requires_timeout; - currently_pending.timeout = if needs_timeout { - match previous_timeout { - Some(mut timeout) if timeout.is_paused() => { - timeout.reset_duration(PENDING_INPUT_TIMEOUT); - Some(timeout) - } - previous_timeout => { - drop(previous_timeout); - Some(self.new_pending_input_timeout(PENDING_INPUT_TIMEOUT, cx)) - } - } - } else { - None - }; - self.pending_input = Some(currently_pending); - self.pending_input_changed(cx); - cx.propagate_event = false; - return; - } - - let skip_bindings = event - .downcast_ref::() - .filter(|key_down_event| key_down_event.prefer_character_input) - .map(|_| { - self.platform_window - .take_input_handler() - .map_or(false, |mut input_handler| { - let accepts = input_handler.accepts_text_input(self, cx); - self.platform_window.set_input_handler(input_handler); - // If modifiers are not excessive (e.g. AltGr), and the input handler is accepting text input, - // we prefer the text input over bindings. - accepts - }) - }) - .unwrap_or(false); - - if !skip_bindings { - for binding in match_result.bindings { - self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx); - if !cx.propagate_event { - self.dispatch_keystroke_observers( - event, - Some(binding.action), - match_result.context_stack, - cx, - ); - self.pending_input_changed(cx); - return; - } - } - } - - self.finish_dispatch_key_event(event, dispatch_path, match_result.context_stack, cx); - self.pending_input_changed(cx); - } - - fn new_pending_input_timeout(&self, duration: Duration, cx: &App) -> PendingInputTimeout { - let (started_at, task) = self.start_pending_input_timeout(duration, cx); - PendingInputTimeout { - duration, - remaining: duration, - state: PendingInputTimeoutState::Running { started_at, task }, - } - } - - fn start_pending_input_timeout(&self, remaining: Duration, cx: &App) -> (Instant, Task<()>) { - let started_at = cx.background_executor().now(); - let task = self.spawn(cx, async move |cx| { - cx.background_executor.timer(remaining).await; - cx.update(move |window, cx| { - let Some(currently_pending) = window - .pending_input - .take() - .filter(|pending| pending.focus == window.focus) - else { - return; - }; - - let node_id = window.focus_node_id_in_rendered_frame(window.focus); - let dispatch_path = window.rendered_frame.dispatch_tree.dispatch_path(node_id); - - let to_replay = window - .rendered_frame - .dispatch_tree - .flush_dispatch(currently_pending.keystrokes, &dispatch_path); - - window.pending_input_changed(cx); - window.replay_pending_input(to_replay, cx) - }) - .log_err(); - }); - (started_at, task) - } - - fn finish_dispatch_key_event( - &mut self, - event: &dyn Any, - dispatch_path: SmallVec<[DispatchNodeId; 32]>, - context_stack: Vec, - cx: &mut App, - ) { - self.dispatch_key_down_up_event(event, &dispatch_path, cx); - if !cx.propagate_event { - return; - } - - self.dispatch_modifiers_changed_event(event, &dispatch_path, cx); - if !cx.propagate_event { - return; - } - - self.dispatch_keystroke_observers(event, None, context_stack, cx); - } - - pub(crate) fn pending_input_changed(&mut self, cx: &mut App) { - self.pending_input_observers - .clone() - .retain(&(), |callback| callback(self, cx)); - } - - fn defer_pending_input_changed(&self, cx: &mut App) { - // Avoid re-entrant entity updates by deferring observer notifications to the end of the - // current effect cycle, and only for this window. - let window_handle = self.handle; - cx.defer(move |cx| { - window_handle - .update(cx, |_, window, cx| { - window.pending_input_changed(cx); - }) - .ok(); - }); - } - - fn dispatch_key_down_up_event( - &mut self, - event: &dyn Any, - dispatch_path: &SmallVec<[DispatchNodeId; 32]>, - cx: &mut App, - ) { - // Capture phase - for node_id in dispatch_path { - let node = self.rendered_frame.dispatch_tree.node(*node_id); - - for key_listener in node.key_listeners.clone() { - key_listener(event, DispatchPhase::Capture, self, cx); - if !cx.propagate_event { - return; - } - } - } - - // Bubble phase - for node_id in dispatch_path.iter().rev() { - // Handle low level key events - let node = self.rendered_frame.dispatch_tree.node(*node_id); - for key_listener in node.key_listeners.clone() { - key_listener(event, DispatchPhase::Bubble, self, cx); - if !cx.propagate_event { - return; - } - } - } - } - - fn dispatch_modifiers_changed_event( - &mut self, - event: &dyn Any, - dispatch_path: &SmallVec<[DispatchNodeId; 32]>, - cx: &mut App, - ) { - let Some(event) = event.downcast_ref::() else { - return; - }; - for node_id in dispatch_path.iter().rev() { - let node = self.rendered_frame.dispatch_tree.node(*node_id); - for listener in node.modifiers_changed_listeners.clone() { - listener(event, self, cx); - if !cx.propagate_event { - return; - } - } - } - } - - /// Determine whether a potential multi-stroke key binding is in progress on this window. - pub fn has_pending_keystrokes(&self) -> bool { - self.pending_input().is_some() - } - - #[cfg(test)] - pub(crate) fn pending_input_is_none(&self) -> bool { - self.pending_input.is_none() - } - - pub(crate) fn clear_pending_keystrokes(&mut self, cx: &mut App) { - if self.pending_input.take().is_some() { - self.defer_pending_input_changed(cx); - } - } - - /// Returns pending input that can still complete a multi-stroke key binding. Input left over - /// from a previous focus can never complete one. - pub fn pending_input(&self) -> Option> { - self.pending_input - .as_ref() - .filter(|pending_input| pending_input.focus == self.focus) - .map(|pending_input| PendingInputStatus { - keystrokes: pending_input.keystrokes.as_slice(), - timeout: pending_input - .timeout - .as_ref() - .map(PendingInputTimeout::status), - }) - } - - /// Pauses or resumes the current pending input timeout on behalf of `owner`. - /// - /// A paused timeout resumes automatically if `owner` is released. Returns whether the timeout - /// state changed. A timeout paused by one owner cannot be resumed by another. - pub fn set_pending_input_timeout_paused( - &mut self, - owner: &Entity, - paused: bool, - cx: &mut App, - ) -> bool { - let owner_id = owner.entity_id(); - if !paused { - return self.resume_pending_input_timeout(owner_id, cx); - } - - let timeout = self - .pending_input - .as_ref() - .filter(|pending_input| pending_input.focus == self.focus) - .and_then(|pending_input| pending_input.timeout.as_ref()); - let Some(timeout) = timeout else { - return false; - }; - if timeout.is_paused() { - return false; - } - - let release_subscription = self.observe_release(owner, cx, move |_, window, cx| { - window.resume_pending_input_timeout(owner_id, cx); - }); - let now = cx.background_executor().now(); - let changed = self - .pending_input - .as_mut() - .filter(|pending_input| pending_input.focus == self.focus) - .and_then(|pending_input| pending_input.timeout.as_mut()) - .is_some_and(|timeout| { - timeout.pause( - PendingInputTimeoutPause { - owner_id, - _release_subscription: release_subscription, - }, - now, - ) - }); - - if changed { - self.defer_pending_input_changed(cx); - } - changed - } - - fn resume_pending_input_timeout(&mut self, owner_id: EntityId, cx: &mut App) -> bool { - let Some(remaining) = self - .pending_input - .as_ref() - .and_then(|pending_input| pending_input.timeout.as_ref()) - .filter(|timeout| timeout.pause_owner_id() == Some(owner_id)) - .map(|timeout| timeout.remaining) - else { - return false; - }; - - let (started_at, task) = self.start_pending_input_timeout(remaining, cx); - let changed = self - .pending_input - .as_mut() - .and_then(|pending_input| pending_input.timeout.as_mut()) - .is_some_and(|timeout| timeout.resume(owner_id, started_at, task)); - - if changed { - self.defer_pending_input_changed(cx); - } - changed - } - - /// Returns the currently pending input keystrokes that might result in a multi-stroke key binding. - pub fn pending_input_keystrokes(&self) -> Option<&[Keystroke]> { - self.pending_input() - .map(|pending_input| pending_input.keystrokes()) - } - - fn replay_pending_input(&mut self, replays: SmallVec<[Replay; 1]>, cx: &mut App) { - let node_id = self.focus_node_id_in_rendered_frame(self.focus); - let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id); - - 'replay: for replay in replays { - let event = KeyDownEvent { - keystroke: replay.keystroke.clone(), - is_held: false, - prefer_character_input: true, - }; - - cx.propagate_event = true; - for binding in replay.bindings { - self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx); - if !cx.propagate_event { - self.dispatch_keystroke_observers( - &event, - Some(binding.action), - Vec::default(), - cx, - ); - continue 'replay; - } - } - - self.dispatch_key_down_up_event(&event, &dispatch_path, cx); - if !cx.propagate_event { - continue 'replay; - } - if let Some(input) = replay.keystroke.key_char.as_ref().cloned() - && let Some(mut input_handler) = self.platform_window.take_input_handler() - { - input_handler.dispatch_input(&input, self, cx); - self.platform_window.set_input_handler(input_handler) - } - } - } - - fn focus_node_id_in_rendered_frame(&self, focus_id: Option) -> DispatchNodeId { - focus_id - .and_then(|focus_id| { - self.rendered_frame - .dispatch_tree - .focusable_node_id(focus_id) - }) - .unwrap_or_else(|| self.rendered_frame.dispatch_tree.root_node_id()) - } - - fn dispatch_action_on_node( - &mut self, - node_id: DispatchNodeId, - action: &dyn Action, - cx: &mut App, - ) { - self.dispatch_action_on_node_inner(node_id, action, cx); - - if !cx.propagate_event - && cx.cursor_hide_mode == CursorHideMode::OnTypingAndAction - && self.last_input_was_keyboard() - { - cx.platform.hide_cursor_until_mouse_moves(); - } - } - - fn dispatch_action_on_node_inner( - &mut self, - node_id: DispatchNodeId, - action: &dyn Action, - cx: &mut App, - ) { - let dispatch_path = self.rendered_frame.dispatch_tree.dispatch_path(node_id); - - // Capture phase for global actions. - cx.propagate_event = true; - if let Some(mut global_listeners) = cx - .global_action_listeners - .remove(&action.as_any().type_id()) - { - for listener in &global_listeners { - #[cfg(feature = "profiler")] - self.window_profiler.begin_action_handler(action, cx); - listener(action.as_any(), DispatchPhase::Capture, cx); - #[cfg(feature = "profiler")] - self.window_profiler.end_action_handler(); - if !cx.propagate_event { - break; - } - } - - global_listeners.extend( - cx.global_action_listeners - .remove(&action.as_any().type_id()) - .unwrap_or_default(), - ); - - cx.global_action_listeners - .insert(action.as_any().type_id(), global_listeners); - } - - if !cx.propagate_event { - return; - } - - // Capture phase for window actions. - for node_id in &dispatch_path { - let node = self.rendered_frame.dispatch_tree.node(*node_id); - for DispatchActionListener { - action_type, - listener, - } in node.action_listeners.clone() - { - let any_action = action.as_any(); - if action_type == any_action.type_id() { - #[cfg(feature = "profiler")] - self.window_profiler.begin_action_handler(action, cx); - listener(any_action, DispatchPhase::Capture, self, cx); - #[cfg(feature = "profiler")] - self.window_profiler.end_action_handler(); - - if !cx.propagate_event { - return; - } - } - } - } - - // Bubble phase for window actions. - for node_id in dispatch_path.iter().rev() { - let node = self.rendered_frame.dispatch_tree.node(*node_id); - for DispatchActionListener { - action_type, - listener, - } in node.action_listeners.clone() - { - let any_action = action.as_any(); - if action_type == any_action.type_id() { - cx.propagate_event = false; // Actions stop propagation by default during the bubble phase - #[cfg(feature = "profiler")] - self.window_profiler.begin_action_handler(action, cx); - listener(any_action, DispatchPhase::Bubble, self, cx); - #[cfg(feature = "profiler")] - self.window_profiler.end_action_handler(); - - if !cx.propagate_event { - return; - } - } - } - } - - // Bubble phase for global actions. - if let Some(mut global_listeners) = cx - .global_action_listeners - .remove(&action.as_any().type_id()) - { - for listener in global_listeners.iter().rev() { - cx.propagate_event = false; // Actions stop propagation by default during the bubble phase - - #[cfg(feature = "profiler")] - self.window_profiler.begin_action_handler(action, cx); - listener(action.as_any(), DispatchPhase::Bubble, cx); - #[cfg(feature = "profiler")] - self.window_profiler.end_action_handler(); - if !cx.propagate_event { - break; - } - } - - global_listeners.extend( - cx.global_action_listeners - .remove(&action.as_any().type_id()) - .unwrap_or_default(), - ); - - cx.global_action_listeners - .insert(action.as_any().type_id(), global_listeners); - } - } - - /// Register the given handler to be invoked whenever the global of the given type - /// is updated. - pub fn observe_global( - &mut self, - cx: &mut App, - f: impl Fn(&mut Window, &mut App) + 'static, - ) -> Subscription { - let window_handle = self.handle; - let (subscription, activate) = cx.global_observers.insert( - TypeId::of::(), - Box::new(move |cx| { - window_handle - .update(cx, |_, window, cx| f(window, cx)) - .is_ok() - }), - ); - cx.defer(move |_| activate()); - subscription - } - - /// Focus the current window and bring it to the foreground at the platform level. - pub fn activate_window(&self) { - self.platform_window.activate(); - } - - /// Requests that the operating system draw attention to this window. - pub fn request_attention(&self) { - self.platform_window.request_attention(); - } - - /// Minimize the current window at the platform level. - pub fn minimize_window(&self) { - self.platform_window.minimize(); - } - - /// Toggle full screen status on the current window at the platform level. - pub fn toggle_fullscreen(&self) { - self.platform_window.toggle_fullscreen(); - } - - /// Toggle simple (borderless) fullscreen, where the window covers the entire - /// screen including the menu bar and, on notched displays, the area around the - /// notch. Unlike [`Window::toggle_fullscreen`], this does not move the window - /// into its own Mission Control space. Only has an effect on macOS. - pub fn toggle_simple_fullscreen(&self) { - self.platform_window.toggle_simple_fullscreen(); - } - - /// Updates the IME panel position suggestions for languages like japanese, chinese. - pub fn invalidate_character_coordinates(&self) { - self.on_next_frame(|window, cx| { - if let Some(mut input_handler) = window.platform_window.take_input_handler() { - if let Some(bounds) = input_handler.selected_bounds(window, cx) { - window.platform_window.update_ime_position(bounds); - } - window.platform_window.set_input_handler(input_handler); - } - }); - } - - /// Present a platform dialog. - /// The provided message will be presented, along with buttons for each answer. - /// When a button is clicked, the returned Receiver will receive the index of the clicked button. - pub fn prompt( - &mut self, - level: PromptLevel, - message: &str, - detail: Option<&str>, - answers: &[T], - cx: &mut App, - ) -> oneshot::Receiver - where - T: Clone + Into, - { - let prompt_builder = cx.prompt_builder.take(); - let Some(prompt_builder) = prompt_builder else { - unreachable!("Re-entrant window prompting is not supported by GPUI"); - }; - - let answers = answers - .iter() - .map(|answer| answer.clone().into()) - .collect::>(); - - let receiver = match &prompt_builder { - PromptBuilder::Default => self - .platform_window - .prompt(level, message, detail, &answers) - .unwrap_or_else(|| { - self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx) - }), - PromptBuilder::Custom(_) => { - self.build_custom_prompt(&prompt_builder, level, message, detail, &answers, cx) - } - }; - - cx.prompt_builder = Some(prompt_builder); - - receiver - } - - fn build_custom_prompt( - &mut self, - prompt_builder: &PromptBuilder, - level: PromptLevel, - message: &str, - detail: Option<&str>, - answers: &[PromptButton], - cx: &mut App, - ) -> oneshot::Receiver { - let (sender, receiver) = oneshot::channel(); - let handle = PromptHandle::new(sender); - let handle = (prompt_builder)(level, message, detail, answers, handle, self, cx); - self.prompt = Some(handle); - receiver - } - - /// Returns whether a prompt rendered by GPUI is currently active in this window. - /// - /// This is only true for prompts rendered in the window (see - /// [`App::set_prompt_builder`]), not for platform-native prompt dialogs. - pub fn has_active_prompt(&self) -> bool { - self.prompt.is_some() - } - - /// Returns the current context stack. - pub fn context_stack(&self) -> Vec { - let node_id = self.focus_node_id_in_rendered_frame(self.focus); - let dispatch_tree = &self.rendered_frame.dispatch_tree; - dispatch_tree - .dispatch_path(node_id) - .iter() - .filter_map(move |&node_id| dispatch_tree.node(node_id).context.clone()) - .collect() - } - - /// Returns all available actions for the focused element. - pub fn available_actions(&self, cx: &App) -> Vec> { - let node_id = self.focus_node_id_in_rendered_frame(self.focus); - let mut actions = self.rendered_frame.dispatch_tree.available_actions(node_id); - for action_type in cx.global_action_listeners.keys() { - if let Err(ix) = actions.binary_search_by_key(action_type, |a| a.as_any().type_id()) { - let action = cx.actions.build_action_type(action_type).ok(); - if let Some(action) = action { - actions.insert(ix, action); - } - } - } - actions - } - - /// Returns key bindings that invoke an action on the currently focused element. Bindings are - /// returned in the order they were added. For display, the last binding should take precedence. - pub fn bindings_for_action(&self, action: &dyn Action) -> Vec { - self.rendered_frame - .dispatch_tree - .bindings_for_action(action, &self.rendered_frame.dispatch_tree.context_stack) - } - - /// Returns the highest precedence key binding that invokes an action on the currently focused - /// element. This is more efficient than getting the last result of `bindings_for_action`. - pub fn highest_precedence_binding_for_action(&self, action: &dyn Action) -> Option { - self.rendered_frame - .dispatch_tree - .highest_precedence_binding_for_action( - action, - &self.rendered_frame.dispatch_tree.context_stack, - ) - } - - /// Returns the key bindings for an action in a context. - pub fn bindings_for_action_in_context( - &self, - action: &dyn Action, - context: KeyContext, - ) -> Vec { - let dispatch_tree = &self.rendered_frame.dispatch_tree; - dispatch_tree.bindings_for_action(action, &[context]) - } - - /// Returns the highest precedence key binding for an action in a context. This is more - /// efficient than getting the last result of `bindings_for_action_in_context`. - pub fn highest_precedence_binding_for_action_in_context( - &self, - action: &dyn Action, - context: KeyContext, - ) -> Option { - let dispatch_tree = &self.rendered_frame.dispatch_tree; - dispatch_tree.highest_precedence_binding_for_action(action, &[context]) - } - - /// Returns any bindings that would invoke an action on the given focus handle if it were - /// focused. Bindings are returned in the order they were added. For display, the last binding - /// should take precedence. - pub fn bindings_for_action_in( - &self, - action: &dyn Action, - focus_handle: &FocusHandle, - ) -> Vec { - let dispatch_tree = &self.rendered_frame.dispatch_tree; - let Some(context_stack) = self.context_stack_for_focus_handle(focus_handle) else { - return vec![]; - }; - dispatch_tree.bindings_for_action(action, &context_stack) - } - - /// Returns the highest precedence key binding that would invoke an action on the given focus - /// handle if it were focused. This is more efficient than getting the last result of - /// `bindings_for_action_in`. - pub fn highest_precedence_binding_for_action_in( - &self, - action: &dyn Action, - focus_handle: &FocusHandle, - ) -> Option { - let dispatch_tree = &self.rendered_frame.dispatch_tree; - let context_stack = self.context_stack_for_focus_handle(focus_handle)?; - dispatch_tree.highest_precedence_binding_for_action(action, &context_stack) - } - - /// Find the bindings that can follow the current input sequence for the current context stack. - pub fn possible_bindings_for_input(&self, input: &[Keystroke]) -> Vec { - self.rendered_frame - .dispatch_tree - .possible_next_bindings_for_input(input, &self.context_stack()) - } - - fn context_stack_for_focus_handle( - &self, - focus_handle: &FocusHandle, - ) -> Option> { - let dispatch_tree = &self.rendered_frame.dispatch_tree; - let node_id = dispatch_tree.focusable_node_id(focus_handle.id)?; - let context_stack: Vec<_> = dispatch_tree - .dispatch_path(node_id) - .into_iter() - .filter_map(|node_id| dispatch_tree.node(node_id).context.clone()) - .collect(); - Some(context_stack) - } - - /// Returns a generic event listener that invokes the given listener with the view and context associated with the given view handle. - pub fn listener_for( - &self, - view: &Entity, - f: impl Fn(&mut T, &E, &mut Window, &mut Context) + 'static, - ) -> impl Fn(&E, &mut Window, &mut App) + 'static { - let view = view.downgrade(); - move |e: &E, window: &mut Window, cx: &mut App| { - view.update(cx, |view, cx| f(view, e, window, cx)).ok(); - } - } - - /// Returns a generic handler that invokes the given handler with the view and context associated with the given view handle. - pub fn handler_for) + 'static>( - &self, - entity: &Entity, - f: Callback, - ) -> impl Fn(&mut Window, &mut App) + 'static { - let entity = entity.downgrade(); - move |window: &mut Window, cx: &mut App| { - entity.update(cx, |entity, cx| f(entity, window, cx)).ok(); - } - } - - /// Register a callback that can interrupt the closing of the current window based the returned boolean. - /// If the callback returns false, the window won't be closed. - pub fn on_window_should_close( - &self, - cx: &App, - f: impl Fn(&mut Window, &mut App) -> bool + 'static, - ) { - let mut cx = self.to_async(cx); - self.platform_window.on_should_close(Box::new(move || { - cx.update(|window, cx| f(window, cx)).unwrap_or(true) - })) - } - - /// Register an action listener on this node for the next frame. The type of action - /// is determined by the first parameter of the given listener. When the next frame is rendered - /// the listener will be cleared. - /// - /// This is a fairly low-level method, so prefer using action handlers on elements unless you have - /// a specific need to register a listener yourself. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn on_action( - &mut self, - action_type: TypeId, - listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static, - ) { - self.invalidator.debug_assert_paint(); - - self.next_frame - .dispatch_tree - .on_action(action_type, Rc::new(listener)); - } - - /// Register a capturing action listener on this node for the next frame if the condition is true. - /// The type of action is determined by the first parameter of the given listener. When the next - /// frame is rendered the listener will be cleared. - /// - /// This is a fairly low-level method, so prefer using action handlers on elements unless you have - /// a specific need to register a listener yourself. - /// - /// This method should only be called as part of the paint phase of element drawing. - pub fn on_action_when( - &mut self, - condition: bool, - action_type: TypeId, - listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static, - ) { - self.invalidator.debug_assert_paint(); - - if condition { - self.next_frame - .dispatch_tree - .on_action(action_type, Rc::new(listener)); - } - } - - /// Read information about the GPU backing this window. - /// Currently returns None on Mac and Windows. - pub fn gpu_specs(&self) -> Option { - self.platform_window.gpu_specs() - } - - /// Perform titlebar double-click action. - /// This is macOS specific. - pub fn titlebar_double_click(&self) { - self.platform_window - .titlebar_double_click(self.is_resizable, self.is_minimizable); - } - - /// Gets the window's title at the platform level. - /// This is macOS specific. - pub fn window_title(&self) -> String { - self.platform_window.get_title() - } - - /// Returns a list of all tabbed windows and their titles. - /// This is macOS specific. - pub fn tabbed_windows(&self) -> Option> { - self.platform_window.tabbed_windows() - } - - /// Returns the tab bar visibility. - /// This is macOS specific. - pub fn tab_bar_visible(&self) -> bool { - self.platform_window.tab_bar_visible() - } - - /// Merges all open windows into a single tabbed window. - /// This is macOS specific. - pub fn merge_all_windows(&self) { - self.platform_window.merge_all_windows() - } - - /// Moves the tab to a new containing window. - /// This is macOS specific. - pub fn move_tab_to_new_window(&self) { - self.platform_window.move_tab_to_new_window() - } - - /// Shows or hides the window tab overview. - /// This is macOS specific. - pub fn toggle_window_tab_overview(&self) { - self.platform_window.toggle_window_tab_overview() - } - - /// Sets the tabbing identifier for the window. - /// This is macOS specific. - pub fn set_tabbing_identifier(&self, tabbing_identifier: Option) { - self.platform_window - .set_tabbing_identifier(tabbing_identifier) - } - - /// Request the OS to play an alert sound. On some platforms this is associated - /// with the window, for others it's just a simple global function call. - pub fn play_system_bell(&self) { - self.platform_window.play_system_bell() - } - - /// Returns whether accessibility features are active for this frame, - /// i.e. whether assistive technology (such as a screen reader) is - /// connected and an accessibility tree is being built. - /// - /// Use this to skip computing data during rendering that is only - /// observable through the accessibility tree. When accessibility is - /// activated, a redraw is forced, so gated work is recomputed before the - /// next tree update is sent to the platform. - /// - /// See the [accessibility guide](crate::_accessibility) for an overview. - pub fn is_a11y_active(&self) -> bool { - self.a11y.is_active() - } - - /// Debug representation of the last frame's accessibility information. - pub fn debug_a11y_tree_json(&self) -> Option { - self.a11y.debug_tree_json() - } - - /// Register a listener for an accessibility action on a specific node. - /// The listener will be called when a screen reader requests the given - /// action on the node identified by `node_id`. - /// - /// See the [accessibility guide](crate::_accessibility) for an overview. - pub fn on_a11y_action( - &mut self, - node_id: accesskit::NodeId, - action: accesskit::Action, - listener: impl FnMut(Option<&accesskit::ActionData>, &mut Window, &mut App) + 'static, - ) { - self.a11y - .action_listeners - .entry(node_id) - .or_default() - .push((action, Box::new(listener))); - } - - #[cfg(not(target_family = "wasm"))] - pub(crate) fn handle_a11y_action(&mut self, request: accesskit::ActionRequest, cx: &mut App) { - // Take listeners out temporarily so the closures can borrow Window - // mutably, then restore them afterward. - if let Some(mut listeners) = self.a11y.action_listeners.remove(&request.target_node) { - let extra_data = request.data.as_ref(); - let mut matched = false; - for (action, listener) in &mut listeners { - if *action == request.action { - listener(extra_data, self, cx); - matched = true; - } - } - self.a11y - .action_listeners - .insert(request.target_node, listeners); - if matched { - return; - } - } - - // Fall back to built-in action handling. - match request.action { - accesskit::Action::Click => { - if let Some(bounds) = self.a11y.node_bounds.get(&request.target_node).copied() { - let center = bounds.center(); - let mouse_down = PlatformInput::MouseDown(crate::MouseDownEvent { - button: MouseButton::Left, - position: center, - modifiers: Modifiers::default(), - click_count: 1, - first_mouse: false, - }); - let mouse_up = PlatformInput::MouseUp(MouseUpEvent { - button: MouseButton::Left, - position: center, - modifiers: Modifiers::default(), - click_count: 1, - }); - self.dispatch_event(mouse_down, cx); - self.dispatch_event(mouse_up, cx); - } - } - accesskit::Action::Focus => { - if let Some(focus_id) = self.a11y.focus_ids.get(&request.target_node).copied() - && let Some(handle) = FocusHandle::for_id(focus_id, &cx.focus_handles) - { - self.focus(&handle, cx); - } - } - accesskit::Action::Blur => { - self.blur(cx); - } - _ => { - log::debug!( - "Unhandled a11y action: {:?} on {:?}", - request.action, - request.target_node - ); - } - } - } - - /// Toggles the inspector mode on this window. - #[cfg(any(feature = "inspector", debug_assertions))] - pub fn toggle_inspector(&mut self, cx: &mut App) { - self.inspector = match self.inspector { - None => Some(cx.new(|_| Inspector::new())), - Some(_) => None, - }; - self.refresh(); - } - - /// Returns true if the window is in inspector mode. - pub fn is_inspector_picking(&self, _cx: &App) -> bool { - #[cfg(any(feature = "inspector", debug_assertions))] - { - if let Some(inspector) = &self.inspector { - return inspector.read(_cx).is_picking(); - } - } - false - } - - /// Executes the provided function with mutable access to an inspector state. - #[cfg(any(feature = "inspector", debug_assertions))] - pub fn with_inspector_state( - &mut self, - _inspector_id: Option<&crate::InspectorElementId>, - cx: &mut App, - f: impl FnOnce(&mut Option, &mut Self) -> R, - ) -> R { - if let Some(inspector_id) = _inspector_id - && let Some(inspector) = &self.inspector - { - let inspector = inspector.clone(); - let active_element_id = inspector.read(cx).active_element_id(); - if Some(inspector_id) == active_element_id { - return inspector.update(cx, |inspector, _cx| { - inspector.with_active_element_state(self, f) - }); - } - } - f(&mut None, self) - } - - #[cfg(any(feature = "inspector", debug_assertions))] - pub(crate) fn build_inspector_element_id( - &mut self, - path: crate::InspectorElementPath, - ) -> crate::InspectorElementId { - self.invalidator.debug_assert_paint_or_prepaint(); - let path = Rc::new(path); - let next_instance_id = self - .next_frame - .next_inspector_instance_ids - .entry(path.clone()) - .or_insert(0); - let instance_id = *next_instance_id; - *next_instance_id += 1; - crate::InspectorElementId { path, instance_id } - } - - #[cfg(any(feature = "inspector", debug_assertions))] - fn prepaint_inspector(&mut self, inspector_width: Pixels, cx: &mut App) -> Option { - if let Some(inspector) = self.inspector.take() { - let mut inspector_element = AnyView::from(inspector.clone()).into_any_element(); - inspector_element.prepaint_as_root( - point(self.viewport_size.width - inspector_width, px(0.0)), - size(inspector_width, self.viewport_size.height).into(), - self, - cx, - ); - self.inspector = Some(inspector); - Some(inspector_element) - } else { - None - } - } - - #[cfg(any(feature = "inspector", debug_assertions))] - fn paint_inspector(&mut self, mut inspector_element: Option, cx: &mut App) { - if let Some(mut inspector_element) = inspector_element { - inspector_element.paint(self, cx); - }; - } - - /// Registers a hitbox that can be used for inspector picking mode, allowing users to select and - /// inspect UI elements by clicking on them. - #[cfg(any(feature = "inspector", debug_assertions))] - pub fn insert_inspector_hitbox( - &mut self, - hitbox_id: HitboxId, - inspector_id: Option<&crate::InspectorElementId>, - cx: &App, - ) { - self.invalidator.debug_assert_paint_or_prepaint(); - if !self.is_inspector_picking(cx) { - return; - } - if let Some(inspector_id) = inspector_id { - self.next_frame - .inspector_hitboxes - .insert(hitbox_id, inspector_id.clone()); - } - } - - #[cfg(any(feature = "inspector", debug_assertions))] - fn paint_inspector_hitbox(&mut self, cx: &App) { - if let Some(inspector) = self.inspector.as_ref() { - let inspector = inspector.read(cx); - if let Some((hitbox_id, _)) = self.hovered_inspector_hitbox(inspector, &self.next_frame) - && let Some(hitbox) = self - .next_frame - .hitboxes - .iter() - .find(|hitbox| hitbox.id == hitbox_id) - { - self.paint_quad(crate::fill(hitbox.bounds, crate::rgba(0x61afef4d))); - } - } - } - - #[cfg(any(feature = "inspector", debug_assertions))] - fn handle_inspector_mouse_event(&mut self, event: &dyn Any, cx: &mut App) { - let Some(inspector) = self.inspector.clone() else { - return; - }; - if event.downcast_ref::().is_some() { - inspector.update(cx, |inspector, _cx| { - if let Some((_, inspector_id)) = - self.hovered_inspector_hitbox(inspector, &self.rendered_frame) - { - inspector.hover(inspector_id, self); - } - }); - } else if event.downcast_ref::().is_some() { - inspector.update(cx, |inspector, _cx| { - if let Some((_, inspector_id)) = - self.hovered_inspector_hitbox(inspector, &self.rendered_frame) - { - inspector.select(inspector_id, self); - } - }); - } else if let Some(event) = event.downcast_ref::() { - // This should be kept in sync with SCROLL_LINES in x11 platform. - const SCROLL_LINES: f32 = 3.0; - const SCROLL_PIXELS_PER_LAYER: f32 = 36.0; - let delta_y = event - .delta - .pixel_delta(px(SCROLL_PIXELS_PER_LAYER / SCROLL_LINES)) - .y; - if let Some(inspector) = self.inspector.clone() { - inspector.update(cx, |inspector, _cx| { - if let Some(depth) = inspector.pick_depth.as_mut() { - *depth += f32::from(delta_y) / SCROLL_PIXELS_PER_LAYER; - let max_depth = self.mouse_hit_test.ids.len() as f32 - 0.5; - if *depth < 0.0 { - *depth = 0.0; - } else if *depth > max_depth { - *depth = max_depth; - } - if let Some((_, inspector_id)) = - self.hovered_inspector_hitbox(inspector, &self.rendered_frame) - { - inspector.set_active_element_id(inspector_id, self); - } - } - }); - } - } - } - - #[cfg(any(feature = "inspector", debug_assertions))] - fn hovered_inspector_hitbox( - &self, - inspector: &Inspector, - frame: &Frame, - ) -> Option<(HitboxId, crate::InspectorElementId)> { - if let Some(pick_depth) = inspector.pick_depth { - let depth = (pick_depth as i64).try_into().unwrap_or(0); - let max_skipped = self.mouse_hit_test.ids.len().saturating_sub(1); - let skip_count = (depth as usize).min(max_skipped); - for hitbox_id in self.mouse_hit_test.ids.iter().skip(skip_count) { - if let Some(inspector_id) = frame.inspector_hitboxes.get(hitbox_id) { - return Some((*hitbox_id, inspector_id.clone())); - } - } - } - None - } - - /// For testing: set the current modifier keys state. - /// This does not generate any events. - #[cfg(any(test, feature = "test-support"))] - pub fn set_modifiers(&mut self, modifiers: Modifiers) { - self.modifiers = modifiers; - } - - /// For testing: simulate a mouse move event to the given position. - /// This dispatches the event through the normal event handling path, - /// which will trigger hover states and tooltips. - #[cfg(any(test, feature = "test-support"))] - pub fn simulate_mouse_move(&mut self, position: Point, cx: &mut App) { - let event = PlatformInput::MouseMove(MouseMoveEvent { - position, - modifiers: self.modifiers, - pressed_button: None, - }); - let _ = self.dispatch_event(event, cx); - } -} - -// #[derive(Clone, Copy, Eq, PartialEq, Hash)] -slotmap::new_key_type! { - /// A unique identifier for a window. - pub struct WindowId; -} - -impl WindowId { - /// Converts this window ID to a `u64`. - pub fn as_u64(&self) -> u64 { - self.0.as_ffi() - } -} - -impl From for WindowId { - fn from(value: u64) -> Self { - WindowId(slotmap::KeyData::from_ffi(value)) - } -} - -/// A handle to a window with a specific root view type. -/// Note that this does not keep the window alive on its own. -#[derive(Deref, DerefMut)] -pub struct WindowHandle { - #[deref] - #[deref_mut] - pub(crate) any_handle: AnyWindowHandle, - state_type: PhantomData V>, -} - -impl Debug for WindowHandle { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("WindowHandle") - .field("any_handle", &self.any_handle.id.as_u64()) - .finish() - } -} - -impl WindowHandle { - /// Creates a new handle from a window ID. - /// This does not check if the root type of the window is `V`. - pub fn new(id: WindowId) -> Self { - WindowHandle { - any_handle: AnyWindowHandle { - id, - state_type: TypeId::of::(), - root_entity_type_name: std::any::type_name::(), - }, - state_type: PhantomData, - } - } - - /// Get the root view out of this window. - /// - /// This will fail if the window is closed or if the root view's type does not match `V`. - #[cfg(any(test, feature = "test-support"))] - pub fn root(&self, cx: &mut C) -> Result> - where - C: AppContext, - { - cx.update_window(self.any_handle, |root_view, _, _| { - root_view - .downcast::() - .map_err(|_| anyhow!("the type of the window's root view has changed")) - })? - } - - /// Updates the root view of this window. - /// - /// This will fail if the window has been closed or if the root view's type does not match - pub fn update( - &self, - cx: &mut C, - update: impl FnOnce(&mut V, &mut Window, &mut Context) -> R, - ) -> Result - where - C: AppContext, - { - cx.update_window(self.any_handle, |root_view, window, cx| { - let view = root_view - .downcast::() - .map_err(|_| anyhow!("the type of the window's root view has changed"))?; - - Ok(view.update(cx, |view, cx| update(view, window, cx))) - })? - } - - /// Read the root view out of this window. - /// - /// This will fail if the window is closed or if the root view's type does not match `V`. - pub fn read<'a>(&self, cx: &'a App) -> Result<&'a V> { - let x = cx - .windows - .get(self.id) - .and_then(|window| { - window - .as_deref() - .and_then(|window| window.root.clone()) - .map(|root_view| root_view.downcast::()) - }) - .context("window not found")? - .map_err(|_| anyhow!("the type of the window's root view has changed"))?; - - Ok(x.read(cx)) - } - - /// Read the root view out of this window, with a callback - /// - /// This will fail if the window is closed or if the root view's type does not match `V`. - pub fn read_with(&self, cx: &C, read_with: impl FnOnce(&V, &App) -> R) -> Result - where - C: AppContext, - { - cx.read_window(self, |root_view, cx| read_with(root_view.read(cx), cx)) - } - - /// Read the root view pointer off of this window. - /// - /// This will fail if the window is closed or if the root view's type does not match `V`. - pub fn entity(&self, cx: &C) -> Result> - where - C: AppContext, - { - cx.read_window(self, |root_view, _cx| root_view) - } - - /// Check if this window is 'active'. - /// - /// Will return `None` if the window is closed or currently - /// borrowed. - pub fn is_active(&self, cx: &mut App) -> Option { - cx.update_window(self.any_handle, |_, window, _| window.is_window_active()) - .ok() - } -} - -impl Copy for WindowHandle {} - -impl Clone for WindowHandle { - fn clone(&self) -> Self { - *self - } -} - -impl PartialEq for WindowHandle { - fn eq(&self, other: &Self) -> bool { - self.any_handle == other.any_handle - } -} - -impl Eq for WindowHandle {} - -impl Hash for WindowHandle { - fn hash(&self, state: &mut H) { - self.any_handle.hash(state); - } -} - -impl From> for AnyWindowHandle { - fn from(val: WindowHandle) -> Self { - val.any_handle - } -} - -/// A handle to a window with any root view type, which can be downcast to a window with a specific root view type. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct AnyWindowHandle { - pub(crate) id: WindowId, - state_type: TypeId, - root_entity_type_name: &'static str, -} - -impl AnyWindowHandle { - /// Get the ID of this window. - pub fn window_id(&self) -> WindowId { - self.id - } - - /// Returns the name of the window's declared root entity type. - pub fn root_entity_type_name(&self) -> &'static str { - self.root_entity_type_name - } - - /// Attempt to convert this handle to a window handle with a specific root view type. - /// If the types do not match, this will return `None`. - pub fn downcast(&self) -> Option> { - if TypeId::of::() == self.state_type { - Some(WindowHandle { - any_handle: *self, - state_type: PhantomData, - }) - } else { - None - } - } - - /// Updates the state of the root view of this window. - /// - /// This will fail if the window has been closed. - pub fn update( - self, - cx: &mut C, - update: impl FnOnce(AnyView, &mut Window, &mut App) -> R, - ) -> Result - where - C: AppContext, - { - cx.update_window(self, update) - } - - /// Read the state of the root view of this window. - /// - /// This will fail if the window has been closed. - pub fn read(self, cx: &C, read: impl FnOnce(Entity, &App) -> R) -> Result - where - C: AppContext, - T: 'static, - { - let view = self - .downcast::() - .context("the type of the window's root view has changed")?; - - cx.read_window(&view, read) - } -} - -impl HasWindowHandle for Window { - fn window_handle(&self) -> Result, HandleError> { - self.platform_window.window_handle() - } -} - -impl HasDisplayHandle for Window { - fn display_handle( - &self, - ) -> std::result::Result, HandleError> { - self.platform_window.display_handle() - } -} - -/// An identifier for an [`Element`]. -/// -/// Can be constructed with a string, a number, or both, as well -/// as other internal representations. -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub enum ElementId { - /// The ID of a View element - View(EntityId), - /// An integer ID. - Integer(u64), - /// A string based ID. - Name(SharedString), - /// A UUID. - Uuid(Uuid), - /// An ID that's equated with a focus handle. - FocusHandle(FocusId), - /// A combination of a name and an integer. - NamedInteger(SharedString, u64), - /// A path. - Path(Arc), - /// A code location. - CodeLocation(core::panic::Location<'static>), - /// A labeled child of an element. - NamedChild(Arc, SharedString), - /// A byte array ID (used for text-anchors) - OpaqueId([u8; 20]), -} - -impl ElementId { - /// Constructs an `ElementId::NamedInteger` from a name and `usize`. - pub fn named_usize(name: impl Into, integer: usize) -> ElementId { - Self::NamedInteger(name.into(), integer as u64) - } -} - -impl Display for ElementId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ElementId::View(entity_id) => write!(f, "view-{}", entity_id)?, - ElementId::Integer(ix) => write!(f, "{}", ix)?, - ElementId::Name(name) => write!(f, "{}", name)?, - ElementId::FocusHandle(_) => write!(f, "FocusHandle")?, - ElementId::NamedInteger(s, i) => write!(f, "{}-{}", s, i)?, - ElementId::Uuid(uuid) => write!(f, "{}", uuid)?, - ElementId::Path(path) => write!(f, "{}", path.display())?, - ElementId::CodeLocation(location) => write!(f, "{}", location)?, - ElementId::NamedChild(id, name) => write!(f, "{}-{}", id, name)?, - ElementId::OpaqueId(opaque_id) => write!(f, "{:x?}", opaque_id)?, - } - - Ok(()) - } -} - -impl TryInto for ElementId { - type Error = anyhow::Error; - - fn try_into(self) -> anyhow::Result { - if let ElementId::Name(name) = self { - Ok(name) - } else { - anyhow::bail!("element id is not string") - } - } -} - -impl From for ElementId { - fn from(id: usize) -> Self { - ElementId::Integer(id as u64) - } -} - -impl From for ElementId { - fn from(id: i32) -> Self { - Self::Integer(id as u64) - } -} - -impl From for ElementId { - fn from(name: SharedString) -> Self { - ElementId::Name(name) - } -} - -impl From for ElementId { - fn from(name: String) -> Self { - ElementId::Name(name.into()) - } -} - -impl From> for ElementId { - fn from(name: Arc) -> Self { - ElementId::Name(name.into()) - } -} - -impl From> for ElementId { - fn from(path: Arc) -> Self { - ElementId::Path(path) - } -} - -impl From<&'static str> for ElementId { - fn from(name: &'static str) -> Self { - ElementId::Name(SharedString::new_static(name)) - } -} - -impl<'a> From<&'a FocusHandle> for ElementId { - fn from(handle: &'a FocusHandle) -> Self { - ElementId::FocusHandle(handle.id) - } -} - -impl From<(&'static str, EntityId)> for ElementId { - fn from((name, id): (&'static str, EntityId)) -> Self { - ElementId::NamedInteger(SharedString::new_static(name), id.as_u64()) - } -} - -impl From<(&'static str, usize)> for ElementId { - fn from((name, id): (&'static str, usize)) -> Self { - ElementId::NamedInteger(SharedString::new_static(name), id as u64) - } -} - -impl From<(SharedString, usize)> for ElementId { - fn from((name, id): (SharedString, usize)) -> Self { - ElementId::NamedInteger(name, id as u64) - } -} - -impl From<(&'static str, u64)> for ElementId { - fn from((name, id): (&'static str, u64)) -> Self { - ElementId::NamedInteger(SharedString::new_static(name), id) - } -} - -impl From for ElementId { - fn from(value: Uuid) -> Self { - Self::Uuid(value) - } -} - -impl From<(&'static str, u32)> for ElementId { - fn from((name, id): (&'static str, u32)) -> Self { - ElementId::NamedInteger(SharedString::new_static(name), u64::from(id)) - } -} - -impl> From<(ElementId, T)> for ElementId { - fn from((id, name): (ElementId, T)) -> Self { - ElementId::NamedChild(Arc::new(id), name.into()) - } -} - -impl From<&'static core::panic::Location<'static>> for ElementId { - fn from(location: &'static core::panic::Location<'static>) -> Self { - ElementId::CodeLocation(*location) - } -} - -impl From<[u8; 20]> for ElementId { - fn from(opaque_id: [u8; 20]) -> Self { - ElementId::OpaqueId(opaque_id) - } -} - -/// A rectangle to be rendered in the window at the given position and size. -/// Passed as an argument [`Window::paint_quad`]. -#[derive(Clone)] -pub struct PaintQuad { - /// The bounds of the quad within the window. - pub bounds: Bounds, - /// The radii of the quad's corners. - pub corner_radii: Corners, - /// The background color of the quad. - pub background: Background, - /// The widths of the quad's borders. - pub border_widths: Edges, - /// The color of the quad's borders. - pub border_color: Hsla, - /// The style of the quad's borders. - pub border_style: BorderStyle, -} - -impl PaintQuad { - /// Sets the corner radii of the quad. - pub fn corner_radii(self, corner_radii: impl Into>) -> Self { - PaintQuad { - corner_radii: corner_radii.into(), - ..self - } - } - - /// Sets the border widths of the quad. - pub fn border_widths(self, border_widths: impl Into>) -> Self { - PaintQuad { - border_widths: border_widths.into(), - ..self - } - } - - /// Sets the border color of the quad. - pub fn border_color(self, border_color: impl Into) -> Self { - PaintQuad { - border_color: border_color.into(), - ..self - } - } - - /// Sets the background color of the quad. - pub fn background(self, background: impl Into) -> Self { - PaintQuad { - background: background.into(), - ..self - } - } -} - -/// Creates a quad with the given parameters. -pub fn quad( - bounds: Bounds, - corner_radii: impl Into>, - background: impl Into, - border_widths: impl Into>, - border_color: impl Into, - border_style: BorderStyle, -) -> PaintQuad { - PaintQuad { - bounds, - corner_radii: corner_radii.into(), - background: background.into(), - border_widths: border_widths.into(), - border_color: border_color.into(), - border_style, - } -} - -/// Creates a filled quad with the given bounds and background color. -pub fn fill(bounds: impl Into>, background: impl Into) -> PaintQuad { - PaintQuad { - bounds: bounds.into(), - corner_radii: (0.).into(), - background: background.into(), - border_widths: (0.).into(), - border_color: transparent_black(), - border_style: BorderStyle::default(), - } -} - -/// Creates a rectangle outline with the given bounds, border color, and a 1px border width -pub fn outline( - bounds: impl Into>, - border_color: impl Into, - border_style: BorderStyle, -) -> PaintQuad { - PaintQuad { - bounds: bounds.into(), - corner_radii: (0.).into(), - background: transparent_black().into(), - border_widths: (1.).into(), - border_color: border_color.into(), - border_style, - } -} - -#[cfg(test)] -mod tests { - use std::{ - cell::{Cell, RefCell}, - path::PathBuf, - rc::Rc, - time::Duration, - }; - - use crate::{ - canvas, div, point, px, size, AnyWindowHandle, AppContext as _, Bounds, ContentMask, - Context, Corners, DispatchPhase, DragMoveEvent, Empty, ExternalDragPayload, ExternalPaths, - FileDragPaths, FileDropEvent, FocusHandle, InputEvent as _, InteractiveElement as _, - IntoElement, LongPressEvent, MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, - Pixels, Point, Render, RequestFrameOptions, StatefulInteractiveElement as _, Styled, - TestAppContext, TouchDragEvent, TouchEvent, TouchId, TouchPhase, Window, WindowAppearance, - WindowOptions, - }; - - struct EmptyView; - - #[test] - fn rounded_content_mask_intersection_keeps_the_original_curve() { - let outer = ContentMask { - bounds: Bounds::from_corners(point(px(0.), px(0.)), point(px(100.), px(100.))), - corner_radii: Corners::all(px(16.)), - ..Default::default() - }; - let inset = ContentMask { - bounds: Bounds::from_corners(point(px(4.), px(4.)), point(px(96.), px(96.))), - ..Default::default() - }; - - let intersection = outer.intersect(&inset); - assert_eq!(intersection.bounds.origin, point(px(4.), px(4.))); - assert_eq!(intersection.rounded_clips[0].bounds, outer.bounds); - assert!(intersection.contains(point(px(4.5), px(8.5)))); - } - - impl Render for EmptyView { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - } - } - - struct OpensWindowOnPaint { - opened: Rc>, - } - - impl Render for OpensWindowOnPaint { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let opened = self.opened.clone(); - div() - .size_full() - .child(canvas( - |_, _, _| {}, - move |_, _, _window, cx| { - if !opened.replace(true) { - cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| EmptyView)) - .unwrap(); - } - }, - )) - // Siblings painted after the canvas: their elements were - // allocated in the arena before the nested draw, so they detect - // a mid-draw arena clear when painted afterwards. - .child(div().child("after")) - } - } - - /// Opening a window synchronously draws it and requests an element arena - /// clear. When that happens from within another window's draw (here: from - /// an element's paint), the clear must be deferred until the outer draw - /// finishes, or the outer draw's arena-allocated elements would be freed - /// out from under it. - #[test] - fn test_window_opened_during_draw_defers_arena_clear() { - let mut cx = TestAppContext::single(); - - let opened = Rc::new(Cell::new(false)); - // add_window draws once, which runs the nested open_window mid-draw. - let window = cx.add_window({ - let opened = opened.clone(); - move |_, _| OpensWindowOnPaint { opened } - }); - - assert!(opened.get()); - assert_eq!(cx.windows().len(), 2); - - // The deferred clear must actually run once the outer draw unwinds: - // subsequent draws of both windows work against a fresh arena. - cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - } - - /// Platforms that stop requesting frames for idle windows (currently web) - /// rely on the frame waker firing whenever frame demand arises; a demand - /// source that skips the waker shows up there as a window that silently - /// stops repainting until unrelated activity wakes it. - #[gpui::test] - fn test_frame_waker_fires_on_frame_demand(cx: &mut TestAppContext) { - let window = cx.add_window(|_, _| EmptyView); - let test_window = cx.test_window(window.into()); - - // Windows start dirty, and that can predate waker installation; - // installing the waker must deliver the pending wake or the first - // frame would never be requested. - assert!( - test_window.frame_wake_count() >= 1, - "opening a window must wake the frame source for the initial frame" - ); - - // Serve outstanding demand (present the frame drawn by `add_window`). - test_window.simulate_frame_request(RequestFrameOptions::default()); - - // An idle window must not wake on clean frames or plain updates, or - // the frame source could never stop. - let baseline = test_window.frame_wake_count(); - test_window.simulate_frame_request(RequestFrameOptions::default()); - window.update(cx, |_, _, _| {}).unwrap(); - assert_eq!( - test_window.frame_wake_count(), - baseline, - "clean frames and non-notifying updates must not wake the frame source" - ); - - // Notifying a view in an idle window is the core demand signal. - window.update(cx, |_, _, cx| cx.notify()).unwrap(); - assert!( - test_window.frame_wake_count() > baseline, - "notifying a view in an idle window must wake the frame source" - ); - - // Serving that demand returns to idle without further wakes. - test_window.simulate_frame_request(RequestFrameOptions::default()); - let baseline = test_window.frame_wake_count(); - test_window.simulate_frame_request(RequestFrameOptions::default()); - assert_eq!( - test_window.frame_wake_count(), - baseline, - "serving demand must return the window to idle" - ); - - // Next-frame callbacks create demand without dirtying the window. - window - .update(cx, |_, window, _| window.on_next_frame(|_, _| {})) - .unwrap(); - assert!( - test_window.frame_wake_count() > baseline, - "scheduling a next-frame callback in an idle window must wake the frame source" - ); - } - - /// A frame request that arrives while next-frame callbacks are pending - /// must never strand them: either the frame runs them, or (when the - /// inactive-window frame-rate throttle defers the frame) the waker fires - /// so another request is delivered. - #[gpui::test] - fn test_pending_next_frame_callbacks_are_not_stranded(cx: &mut TestAppContext) { - let window = cx.add_window(|_, _| EmptyView); - let test_window = cx.test_window(window.into()); - // Establish a recent last-frame time so the inactive-window throttle - // can engage on the next request. - test_window.simulate_frame_request(RequestFrameOptions::default()); - - let callback_ran = Rc::new(Cell::new(false)); - window - .update(cx, { - let callback_ran = callback_ran.clone(); - move |_, window, _| { - window.on_next_frame(move |_, _| callback_ran.set(true)); - } - }) - .unwrap(); - - let baseline = test_window.frame_wake_count(); - test_window.simulate_frame_request(RequestFrameOptions::default()); - // The test window is inactive, so this request throttles to ~30fps - // when it lands within the throttle interval of the previous frame - // (the common case here, but timing-dependent): the callback is - // deferred and the waker must re-arm the frame source. On a slow run - // the request instead lands outside the interval and runs the - // callback directly. - assert!( - test_window.frame_wake_count() > baseline || callback_ran.get(), - "a frame request with pending next-frame callbacks must either run them or re-arm the frame source" - ); - } - - #[gpui::test] - fn test_window_reports_no_raw_handle_instead_of_panicking(cx: &mut TestAppContext) { - use raw_window_handle::{HandleError, HasDisplayHandle as _, HasWindowHandle as _}; - - let window = cx.add_window(|_, _| EmptyView); - window - .update(cx, |_, window, _| { - assert!(matches!( - window.window_handle(), - Err(HandleError::NotSupported) - )); - assert!(matches!( - window.display_handle(), - Err(HandleError::NotSupported) - )); - }) - .unwrap(); - } - - #[gpui::test] - fn test_appearance_change_runs_after_app_update(cx: &mut TestAppContext) { - let window = cx.add_window(|_, _| EmptyView); - let observed_appearance = Rc::new(Cell::new(None)); - let _subscription = window - .update(cx, { - let observed_appearance = observed_appearance.clone(); - move |_, window, _| { - window.observe_window_appearance(move |window, _| { - observed_appearance.set(Some(window.appearance())); - }) - } - }) - .unwrap(); - let test_window = cx.test_window(window.into()); - - cx.update(|_| { - test_window.simulate_appearance_change(WindowAppearance::Dark); - assert_eq!(observed_appearance.get(), None); - }); - cx.run_until_parked(); - - assert_eq!(observed_appearance.get(), Some(WindowAppearance::Dark)); - } - - #[gpui::test] - fn queued_frame_callback_wakes_a_parked_render_loop(cx: &mut TestAppContext) { - let window = cx.add_window(|_, _| Empty); - let test_window = cx.test_window(window.into()); - - assert!(test_window.simulate_scheduled_frame()); - assert!(test_window.simulate_scheduled_frame()); - assert!(!test_window.frame_scheduled()); - - cx.update_window(window.into(), |_, window, _| { - window.active.set(true); - window.on_next_frame(|_, _| {}); - }) - .unwrap(); - assert!( - test_window.frame_scheduled(), - "queuing work on a parked window must wake the render loop" - ); - - assert!(test_window.simulate_scheduled_frame()); - assert!( - test_window.frame_scheduled(), - "presenting the frame must await one compositor callback" - ); - assert!(test_window.simulate_scheduled_frame()); - assert!(!test_window.frame_scheduled()); - } - - #[gpui::test] - fn pending_presentation_wakes_a_parked_render_loop(cx: &mut TestAppContext) { - let window = cx.add_window(|_, _| Empty); - let test_window = cx.test_window(window.into()); - - assert!(test_window.simulate_scheduled_frame()); - assert!(test_window.simulate_scheduled_frame()); - assert!(!test_window.frame_scheduled()); - - cx.update_window(window.into(), |_, window, cx| window.draw(cx).clear(cx)) - .unwrap(); - - assert!( - test_window.frame_scheduled(), - "a rendered scene awaiting presentation must wake the render loop" - ); - } - - #[gpui::test] - fn callback_queued_during_a_frame_requests_a_follow_up(cx: &mut TestAppContext) { - let window = cx.add_window(|_, _| Empty); - let test_window = cx.test_window(window.into()); - - let callback_ran = Rc::new(Cell::new(false)); - cx.update_window(window.into(), |_, window, _| { - // Inactive windows are frame-rate throttled, which would defer the - // ticks this test drives manually. - window.active.set(true); - let callback_ran = callback_ran.clone(); - window.on_next_frame(move |window, _| { - window.on_next_frame(move |_, _| callback_ran.set(true)); - }); - }) - .unwrap(); - - assert!(test_window.simulate_scheduled_frame()); - assert!(!callback_ran.get()); - assert!( - test_window.frame_scheduled(), - "a callback queued mid-frame must schedule a follow-up before the loop parks" - ); - - assert!(test_window.simulate_scheduled_frame()); - assert!(callback_ran.get()); - } - - struct RootView { - explicit_size: bool, - child_bounds: Rc>>, - } - - impl Render for RootView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - let child_bounds = self.child_bounds.clone(); - let root = div().flex().flex_col().child( - canvas( - move |bounds, _, _| child_bounds.set(bounds), - |_, _, _, _| {}, - ) - .size_full(), - ); - if self.explicit_size { - root.w(px(300.)).h(px(200.)) - } else { - root - } - } - } - - #[test] - fn auto_sized_window_root_fills_the_window() { - let mut cx = TestAppContext::single(); - let child_bounds = Rc::new(Cell::new(Bounds::default())); - let window = cx.add_window({ - let child_bounds = child_bounds.clone(); - move |_, _| RootView { - explicit_size: false, - child_bounds, - } - }); - - let viewport_size = cx - .update_window(window.into(), |_, window, cx| { - window.draw(cx).clear(cx); - window.viewport_size() - }) - .unwrap(); - - assert_eq!(child_bounds.get().size, viewport_size); - } - - #[test] - fn explicitly_sized_window_root_keeps_its_size() { - let mut cx = TestAppContext::single(); - let child_bounds = Rc::new(Cell::new(Bounds::default())); - let window = cx.add_window({ - let child_bounds = child_bounds.clone(); - move |_, _| RootView { - explicit_size: true, - child_bounds, - } - }); - - cx.update_window(window.into(), |_, window, cx| { - window.draw(cx).clear(cx); - }) - .unwrap(); - - assert_eq!(child_bounds.get().size, size(px(300.), px(200.))); - } - - struct FileDragView { - path: PathBuf, - observed_drag_moves: Rc>>>, - observed_drops: Rc>>, - } - - impl Render for FileDragView { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - div() - .id("file-drag") - .size_full() - .on_drag(self.path.clone(), |_, _, _, cx| cx.new(|_| Empty)) - .external_drag_payload(|path: &PathBuf, _, _| { - Some(ExternalDragPayload::Files(FileDragPaths::new([( - path.clone(), - true, - )]))) - }) - .on_drag_move({ - let observed_drag_moves = self.observed_drag_moves.clone(); - move |event: &DragMoveEvent, _, _| { - observed_drag_moves.borrow_mut().push(event.event.position); - } - }) - .on_drop({ - let observed_drops = self.observed_drops.clone(); - move |path: &PathBuf, _, _| observed_drops.borrow_mut().push(path.clone()) - }) - } - } - - #[gpui::test] - fn file_drag_is_promoted_once_and_restored_in_source_window(cx: &mut TestAppContext) { - struct Drag { - window: AnyWindowHandle, - observed_drag_moves: Rc>>>, - observed_drops: Rc>>, - } - - fn start_drag(cx: &mut TestAppContext, path: PathBuf, platform_result: bool) -> Drag { - let observed_drag_moves = Rc::new(RefCell::new(Vec::new())); - let observed_drops = Rc::new(RefCell::new(Vec::new())); - let window: AnyWindowHandle = cx - .add_window({ - let observed_drag_moves = observed_drag_moves.clone(); - let observed_drops = observed_drops.clone(); - move |_, _| FileDragView { - path, - observed_drag_moves, - observed_drops, - } - }) - .into(); - cx.test_window(window) - .set_start_external_drag_result(platform_result); - - let update_result = cx.update_window(window, |_, window, cx| { - window.draw(cx).clear(cx); - window.dispatch_event( - MouseDownEvent { - position: point(px(10.), px(10.)), - button: MouseButton::Left, - modifiers: Default::default(), - click_count: 1, - first_mouse: false, - } - .to_platform_input(), - cx, - ); - window.dispatch_event( - MouseMoveEvent { - position: point(px(20.), px(20.)), - pressed_button: Some(MouseButton::Left), - modifiers: Default::default(), - } - .to_platform_input(), - cx, - ); - assert!(cx.active_drag.is_some()); - }); - assert!( - update_result.is_ok(), - "failed to start drag: {update_result:?}" - ); - - assert!(cx.test_window(window).external_drag_files().is_empty()); - Drag { - window, - observed_drag_moves, - observed_drops, - } - } - - let successful_path = PathBuf::from("/tmp/successful-drag"); - let successful = start_drag(cx, successful_path.clone(), true); - let outside_position = point(px(-1.), px(20.)); - let update_result = cx.update_window(successful.window, |_, window, cx| { - window.dispatch_event( - MouseMoveEvent { - position: outside_position, - pressed_button: Some(MouseButton::Left), - modifiers: Default::default(), - } - .to_platform_input(), - cx, - ); - assert!(cx.active_drag.is_none()); - }); - assert!( - update_result.is_ok(), - "failed to promote drag: {update_result:?}" - ); - assert_eq!( - cx.test_window(successful.window).external_drag_files(), - [(successful_path.clone(), true)] - ); - // Views must still see the move that leaves the window, otherwise they never learn to tear - // down the drag state they built up while the pointer was inside. - assert_eq!( - successful.observed_drag_moves.borrow().last(), - Some(&outside_position) - ); - - let destination: AnyWindowHandle = cx.add_window(|_, _| EmptyView).into(); - let reentry_position = point(px(30.), px(30.)); - let external_paths = || ExternalPaths([successful_path.clone()].into_iter().collect()); - let update_result = cx.update_window(destination, |_, window, cx| { - window.dispatch_event( - FileDropEvent::Entered { - position: reentry_position, - paths: external_paths(), - } - .to_platform_input(), - cx, - ); - assert!(cx - .active_drag - .as_ref() - .is_some_and(|drag| drag.value.downcast_ref::().is_some())); - window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx); - assert!(cx.active_drag.is_none()); - }); - assert!( - update_result.is_ok(), - "failed to handle drag in destination window: {update_result:?}" - ); - - let update_result = cx.update_window(successful.window, |_, window, cx| { - window.dispatch_event( - FileDropEvent::Entered { - position: reentry_position, - paths: external_paths(), - } - .to_platform_input(), - cx, - ); - assert!(cx - .active_drag - .as_ref() - .is_some_and(|drag| drag.value.downcast_ref::().is_some())); - assert_eq!( - successful.observed_drag_moves.borrow().last(), - Some(&reentry_position) - ); - - window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx); - assert!(cx.active_drag.is_none()); - - window.dispatch_event( - FileDropEvent::Entered { - position: reentry_position, - paths: external_paths(), - } - .to_platform_input(), - cx, - ); - assert!(cx - .active_drag - .as_ref() - .is_some_and(|drag| drag.value.downcast_ref::().is_some())); - - window.dispatch_event( - FileDropEvent::Submit { - position: reentry_position, - } - .to_platform_input(), - cx, - ); - assert_eq!( - successful.observed_drops.borrow().as_slice(), - std::slice::from_ref(&successful_path) - ); - assert!(cx.active_drag.is_none()); - - window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx); - assert!(cx.active_drag.is_none()); - window.dispatch_event(FileDropEvent::Ended.to_platform_input(), cx); - assert!(cx.active_drag.is_none()); - - window.dispatch_event( - FileDropEvent::Entered { - position: reentry_position, - paths: external_paths(), - } - .to_platform_input(), - cx, - ); - assert!(cx - .active_drag - .as_ref() - .is_some_and(|drag| drag.value.downcast_ref::().is_some())); - window.dispatch_event(FileDropEvent::Exited.to_platform_input(), cx); - }); - assert!( - update_result.is_ok(), - "failed to restore drag in source window: {update_result:?}" - ); - - let cancelled_path = PathBuf::from("/tmp/cancelled-drag"); - let cancelled = start_drag(cx, cancelled_path.clone(), true); - let update_result = cx.update_window(cancelled.window, |_, window, cx| { - window.dispatch_event( - MouseMoveEvent { - position: outside_position, - pressed_button: Some(MouseButton::Left), - modifiers: Default::default(), - } - .to_platform_input(), - cx, - ); - assert!(cx.active_drag.is_none()); - - window.dispatch_event( - FileDropEvent::Entered { - position: reentry_position, - paths: ExternalPaths([cancelled_path].into_iter().collect()), - } - .to_platform_input(), - cx, - ); - assert!(cx - .active_drag - .as_ref() - .is_some_and(|drag| drag.value.downcast_ref::().is_some())); - assert!(cx.stop_active_drag(window)); - assert!(cx.active_drag.is_none()); - }); - assert!( - update_result.is_ok(), - "failed to cancel restored drag: {update_result:?}" - ); - assert!(!cx.update(|cx| cx.end_platform_drag(cancelled.window.window_id()))); - - let removed_path = PathBuf::from("/tmp/removed-window-drag"); - let removed = start_drag(cx, removed_path, true); - let removed_window_id = removed.window.window_id(); - let update_result = cx.update_window(removed.window, |_, window, cx| { - window.dispatch_event( - MouseMoveEvent { - position: outside_position, - pressed_button: Some(MouseButton::Left), - modifiers: Default::default(), - } - .to_platform_input(), - cx, - ); - assert!(cx.active_drag.is_none()); - window.remove_window(); - }); - assert!( - update_result.is_ok(), - "failed to remove drag source window: {update_result:?}" - ); - assert!(!cx.update(|cx| cx.end_platform_drag(removed_window_id))); - - let failed_path = PathBuf::from("/tmp/failed-drag"); - let failed = start_drag(cx, failed_path.clone(), false); - let update_result = cx.update_window(failed.window, |_, window, cx| { - for x_position in [-1., -2.] { - window.dispatch_event( - MouseMoveEvent { - position: point(px(x_position), px(20.)), - pressed_button: Some(MouseButton::Left), - modifiers: Default::default(), - } - .to_platform_input(), - cx, - ); - } - assert!(cx.active_drag.is_some()); - }); - assert!( - update_result.is_ok(), - "failed to retain drag after platform failure: {update_result:?}" - ); - assert_eq!( - cx.test_window(failed.window).external_drag_files(), - [(failed_path, true)] - ); - } - - struct FocusForwarder { - a: FocusHandle, - b: FocusHandle, - } - - impl Render for FocusForwarder { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { - div() - .size_full() - .child(div().w(px(50.)).h(px(50.)).track_focus(&self.a)) - .child(div().w(px(50.)).h(px(50.)).track_focus(&self.b)) - } - } - - /// When a focus listener moves focus again (e.g. a dock forwarding focus to its - /// active panel), the resulting focus events must be dispatched without waiting - /// for an unrelated redraw of the window. - #[gpui::test] - fn test_focus_moved_by_focus_listener_is_dispatched(cx: &mut TestAppContext) { - let b_focus_count = Rc::new(Cell::new(0)); - let window = cx.add_window({ - let b_focus_count = b_focus_count.clone(); - move |window, cx| { - let a = cx.focus_handle(); - let b = cx.focus_handle(); - cx.on_focus(&a, window, |this: &mut FocusForwarder, window, cx| { - let b = this.b.clone(); - window.focus(&b, cx); - }) - .detach(); - cx.on_focus(&b, window, move |_, _, _| { - b_focus_count.set(b_focus_count.get() + 1); - }) - .detach(); - FocusForwarder { a, b } - } - }); - - window - .update(cx, |_, window, _| window.activate_window()) - .unwrap(); - cx.executor().run_until_parked(); - - window - .update(cx, |this, window, cx| { - let a = this.a.clone(); - window.focus(&a, cx); - }) - .unwrap(); - cx.executor().run_until_parked(); - - window - .update(cx, |this, window, _| { - assert!(this.b.is_focused(window)); - }) - .unwrap(); - assert_eq!(b_focus_count.get(), 1); - } - - #[gpui::test] - fn claimed_touch_drag_receives_movement_and_release(cx: &mut TestAppContext) { - let events = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let events = events.clone(); - move |_, _| TouchDragListener { events } - }); - let touch = TouchId(1); - - dispatch_touch(window, cx, touch, TouchPhase::Started, 10.); - dispatch_touch(window, cx, touch, TouchPhase::Moved, 30.); - dispatch_touch(window, cx, touch, TouchPhase::Ended, 40.); - - assert_eq!( - events.borrow().as_slice(), - [ - (TouchPhase::Started, px(10.)), - (TouchPhase::Moved, px(30.)), - (TouchPhase::Ended, px(40.)), - ] - ); - } - - struct TouchDragListener { - events: Rc>>, - } - - impl Render for TouchDragListener { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - let events = self.events.clone(); - canvas( - |_, _, _| {}, - move |_, _, window, _| { - window.on_mouse_event(move |event: &TouchDragEvent, phase, window, _cx| { - if phase != DispatchPhase::Bubble { - return; - } - events.borrow_mut().push((event.phase, event.position.x)); - if event.phase == TouchPhase::Started { - window.prevent_default(); - } - }); - }, - ) - } - } - - #[gpui::test] - fn long_press_is_claimed_only_when_started_prevents_default(cx: &mut TestAppContext) { - for response in [ - LongPressResponse::PreventDefault, - LongPressResponse::StopPropagation, - LongPressResponse::None, - ] { - let phases = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let phases = phases.clone(); - move |_, _| LongPressListener { phases, response } - }); - dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.); - cx.executor().advance_clock(Duration::from_millis(501)); - cx.executor().run_until_parked(); - window - .update(cx, |_, window, _| { - assert_eq!( - window.long_press_capture.is_some(), - response == LongPressResponse::PreventDefault - ); - }) - .unwrap(); - dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.); - dispatch_touch(window, cx, TouchId(1), TouchPhase::Ended, 2.); - window - .update(cx, |_, window, _| { - assert!(window.long_press_capture.is_none()); - }) - .unwrap(); - - let phases = phases.borrow(); - if response == LongPressResponse::PreventDefault { - assert_eq!( - phases.as_slice(), - [TouchPhase::Started, TouchPhase::Moved, TouchPhase::Ended] - ); - } else { - assert_eq!(phases.as_slice(), [TouchPhase::Started]); - } - } - } - - #[gpui::test] - fn stale_default_prevention_does_not_claim_long_press(cx: &mut TestAppContext) { - let phases = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let phases = phases.clone(); - move |_, _| LongPressListener { - phases, - response: LongPressResponse::None, - } - }); - window - .update(cx, |_, window, _| { - window.prevent_default(); - }) - .unwrap(); - - dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.); - cx.executor().advance_clock(Duration::from_millis(501)); - cx.executor().run_until_parked(); - dispatch_touch(window, cx, TouchId(1), TouchPhase::Moved, 2.); - - assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]); - } - - #[gpui::test] - fn resolved_touch_cancels_scheduled_long_press(cx: &mut TestAppContext) { - for (phase, position) in [ - (TouchPhase::Ended, 0.), - (TouchPhase::Cancelled, 0.), - (TouchPhase::Moved, 20.), - ] { - let phases = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let phases = phases.clone(); - move |_, _| LongPressListener { - phases, - response: LongPressResponse::PreventDefault, - } - }); - dispatch_touch(window, cx, TouchId(1), TouchPhase::Started, 0.); - dispatch_touch(window, cx, TouchId(1), phase, position); - cx.executor().advance_clock(Duration::from_millis(501)); - cx.executor().run_until_parked(); - - assert!(phases.borrow().is_empty(), "{phase:?} allowed long press"); - } - } - - #[gpui::test] - fn stale_long_press_timer_cannot_affect_replacement_touch(cx: &mut TestAppContext) { - let phases = Rc::new(RefCell::new(Vec::new())); - let window = cx.add_window({ - let phases = phases.clone(); - move |_, _| LongPressListener { - phases, - response: LongPressResponse::PreventDefault, - } - }); - let first_touch = TouchId(1); - dispatch_touch(window, cx, first_touch, TouchPhase::Started, 0.); - cx.executor().advance_clock(Duration::from_millis(250)); - dispatch_touch(window, cx, first_touch, TouchPhase::Cancelled, 0.); - dispatch_touch(window, cx, TouchId(2), TouchPhase::Started, 10.); - - cx.executor().advance_clock(Duration::from_millis(251)); - cx.executor().run_until_parked(); - assert!(phases.borrow().is_empty()); - - cx.executor().advance_clock(Duration::from_millis(250)); - cx.executor().run_until_parked(); - assert_eq!(phases.borrow().as_slice(), [TouchPhase::Started]); - } - - #[derive(Clone, Copy, PartialEq)] - enum LongPressResponse { - PreventDefault, - StopPropagation, - None, - } - - struct LongPressListener { - phases: Rc>>, - response: LongPressResponse, - } - - impl Render for LongPressListener { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let entity = cx.entity(); - let phases = self.phases.clone(); - let response = self.response; - canvas( - |_, _, _| {}, - move |_, _, window, _| { - window.on_mouse_event(move |event: &LongPressEvent, phase, window, cx| { - if phase != DispatchPhase::Bubble { - return; - } - phases.borrow_mut().push(event.phase); - match response { - LongPressResponse::PreventDefault => { - window.capture_long_press(&entity); - window.prevent_default(); - } - LongPressResponse::StopPropagation => cx.stop_propagation(), - LongPressResponse::None => {} - } - }); - }, - ) - } - } - - fn dispatch_touch( - window: crate::WindowHandle, - cx: &mut TestAppContext, - id: TouchId, - phase: TouchPhase, - x: f32, - ) { - window - .update(cx, |_, window, cx| { - window.dispatch_event( - TouchEvent { - id, - phase, - position: point(px(x), px(0.)), - predicted_position: None, - force: None, - } - .to_platform_input(), - cx, - ); - }) - .unwrap(); - } -} diff --git a/crates/gpui_pre/src/window/a11y.rs b/crates/gpui_pre/src/window/a11y.rs deleted file mode 100644 index 234e7f0a..00000000 --- a/crates/gpui_pre/src/window/a11y.rs +++ /dev/null @@ -1,888 +0,0 @@ -//! Accessibility support, provided by [AccessKit][accesskit]. -//! -//! There are user-facing guide-level docs [here](crate::_accessibility). -//! -//! ## Architecture -//! -//! ```text -//! ┌────────────────────────────────┐ ┌─────────────────────┐ -//! ┌─▶│ AccessKit Adapter (MacOS) │◀─▶│ MacOS System APIs │ -//! │ └────────────────────────────────┘ └─────────────────────┘ -//! │ -//! ┌──────┐ ┌───────────┐ │ ┌────────────────────────────────┐ ┌─────────────────────┐ -//! │ GPUI │◀─▶│ AccessKit │◀─┼─▶│ AccessKit Adapter (Windows) │◀─▶│ Windows System APIs │ -//! └──────┘ └───────────┘ │ └────────────────────────────────┘ └─────────────────────┘ -//! │ -//! │ ┌────────────────────────────────┐ ┌─────────────────────┐ -//! └─▶│ AccessKit Adapter (Linux) │◀─▶│ dbus │ -//! └────────────────────────────────┘ └─────────────────────┘ -//! ``` -//! -//! In order for GPUI apps to be usable for people using assistive technology, -//! we must do a few things: -//! - Inform the system when the UI changes meaningfully. This includes: -//! - Reporting new/removed/changed UI elements -//! - *Not* reporting irrelevant UI changes, e.g. an invisible `div()` being -//! added. -//! - Reporting the appearance and capabilities of each UI element. For example: -//! - What does this piece of text say? -//! - How far along is this progress bar? -//! - Can this node be focused? -//! - Can this node have a value directly assigned? (e.g. a slider) -//! - Allowing the system to interact with the UI by dispatching actions to -//! nodes. Note that AccessKit has its own [`Action`] type, which is not the -//! [`crate::Action`] trait. -//! - Activate and deactivate accessibility features when requested by the -//! system. -//! -//! Activating and deactivating at the right time is trivial, so I won't go into -//! detail here. The other two are almost orthogonal in implementation. -//! -//! The state for both lives in the [`A11y`] struct in this module. -//! -//! ### Reporting UI changes -//! -//! Every frame, we build a [`TreeUpdate`] and send it to the platform-specific -//! adapter. A [`TreeUpdate`] is a representation of a subset of the UI tree. -//! When the adapter receives the update, it diffs it against the previous -//! update, and calls platform-specific APIs to inform screen readers about the -//! changes. Nodes may have been created, destroyed, or updated. -//! -//! Each node has an ID, and this ID *should* be stable across frames. If a -//! node's ID changes, then, from AccessKit's point of view, it is a different -//! node. -//! -//! We derive the node ID from the [`GlobalElementId`] in -//! [`GlobalElementId::accesskit_node_id`]. Nodes without [`GlobalElementId`]s -//! cannot produce an AccessKit [`NodeId`], and so are not included in the -//! accessibility tree. We try to warn when using accessibility APIs on -//! [`div()`] without setting an ID. -//! -//! This all happens in [`Drawable::prepaint`]. The [`A11y`] struct maintains a -//! stack of nodes during prepainting, which we can use to calculate the -//! [`NodeId`]s, and record parent-child relationships. Once all [`Element`]s in -//! a frame have been prepainted, we send the resulting [`TreeUpdate`] object to -//! the adapter and the screen reader can announce the changes. -//! -//! #### Synthetic children -//! -//! Additionally, some nodes can register "synthetic children" using -//! [`Element::a11y_synthetic_children`]. Normally, one accesskit node is pushed -//! for every [`Element`] with a role and id. However, sometimes a single -//! element may want to produce many accesskit nodes. These extra nodes are -//! referred to as "synthetic children" of the element providing a non-default -//! [`Element::a11y_synthetic_children`] implementation. -//! -//! The user is provided a builder-style API using [`A11ySubtreeBuilder`], which -//! allows them to create push nodes that are children of the current node, as -//! well as modify the current node itself. -//! -//! GPUI calls this callback *after* prepainting (and just before popping the -//! corresponding element), since this step may need prepaint information to be -//! available. In the future, we may want to add prepaint information more -//! generally to [`Element::write_a11y_info`], but for now that's not necessary. -//! -//! ### Responding to actions -//! -//! On adapter creation, we provide a callback to the adapter, which can be used -//! to dispatch actions. This callback forwards to [`A11y::action_listeners`], a -//! mapping from [`NodeId`]s to action handlers (basically just `Box`). -//! -//! This is populated in: -//! - [`Window::on_a11y_action`], which is called by: -//! - [`Interactivity::paint`], which is called by: -//! - [`StatefulInteractiveElement::on_a11y_action`], which is a public-facing API -//! -//! These are cleared at the start of a frame, and re-populated during painting. -//! -//! [`NodeId`]: accesskit::NodeId - -use crate::*; - -pub(crate) mod debug; - -use crate::{App, Bounds, FocusId, Pixels, SharedString, Window}; -use accesskit::{Action, NodeId, TreeUpdate}; -use collections::{FxHashMap, FxHashSet}; -use smallvec::SmallVec; -use std::hash::{Hash, Hasher}; -use std::sync::{ - Arc, - atomic::{AtomicBool, Ordering}, -}; - -/// The fixed AccessKit node ID used for the root of every window's a11y tree. -pub(crate) const ROOT_NODE_ID: NodeId = NodeId(0); - -/// A listener for an accessibility action on a specific node. -pub(crate) type A11yActionListener = - Box, &mut Window, &mut App) + 'static>; - -/// Per-window accessibility state. -/// -/// Manages the AccessKit tree that is built each frame and the mappings -/// needed to dispatch incoming action requests back to the right elements. -pub(crate) struct A11y { - /// Whether accessibility has been [forcibly disabled] for this window. - /// - /// [forcibly disabled]: crate::Application::new_inaccessible - force_disabled: bool, - /// Whether a11y features have been requested by the system. - /// - /// Updated by AccessKit using callbacks provided to the adapter. Can change - /// halfway through a frame. - active_flag: Arc, - /// Whether a11y features are active for *this specific frame*. - /// - /// At the start of each frame, we load [`Self::active_flag`] (using - /// [`Self::sync_active_flag`]) and use this to determine whether we - /// should construct a [`TreeUpdate`] for this frame. It's important that - /// this value is stable within a frame, because the builder API exposed by - /// this type maintains a stack of nodes and each must be pushed and popped - /// exactly once. - /// - /// At the end of the frame, we re-call [`Self::sync_active_flag`] to - /// determine whether we should actually send the finished [`TreeUpdate`]. - active_this_frame: bool, - pub(crate) nodes: A11yNodeBuilder, - pub(crate) focus_ids: FxHashMap, - pub(crate) node_bounds: FxHashMap>, - pub(crate) action_listeners: FxHashMap>, - /// The window's title, used to label the root node so assistive - /// technology can tell windows apart. - window_title: Option, - /// The focus id we most recently reported as having no accessibility node, - /// used to log at most once per focus change rather than every frame. - last_focus_without_node: Option, - /// Retains the last tree update (and, in debug builds, per-node provenance) - /// so it can be dumped via [`crate::Window::debug_a11y_tree_json`]. - debug: debug::A11yDebug, - /// Maps a view's [`EntityId`] to its `Render` type name - #[cfg(debug_assertions)] - pub(crate) view_type_names: FxHashMap, -} - -impl A11y { - pub(crate) fn new( - active_flag: Arc, - force_disabled: bool, - window_title: Option, - ) -> Self { - Self { - force_disabled, - active_flag, - active_this_frame: false, - nodes: A11yNodeBuilder::new(), - focus_ids: FxHashMap::default(), - node_bounds: FxHashMap::default(), - action_listeners: FxHashMap::default(), - window_title, - last_focus_without_node: None, - debug: debug::A11yDebug::default(), - #[cfg(debug_assertions)] - view_type_names: FxHashMap::default(), - } - } - - /// Logs (once per focus change) that the focused element is not exposed to - /// assistive technology because it has no accessibility node. When this - /// happens, screen readers fall back to announcing the whole window instead - /// of the focused element. The fix is to give the element both an - /// `.id(...)` and a `.role(...)`. - pub(crate) fn note_focus_without_node(&mut self, focus_id: FocusId, reason: &str) { - if self.last_focus_without_node != Some(focus_id) { - self.last_focus_without_node = Some(focus_id); - log::info!( - "a11y: focused element ({focus_id:?}) has no accessibility node \ - ({reason}); assistive technology will announce the whole window \ - instead. Give it both an `.id(...)` and a `.role(...)` to expose it." - ); - } - } - - pub(crate) fn set_window_title(&mut self, title: impl Into) { - self.window_title = Some(title.into()); - } - - /// Ensures that [`Self::is_active`] returns up to date information. - /// - /// See the docs for [`Self::active_flag`] and [`Self::active_this_frame`] - /// for more commentary. - pub(crate) fn sync_active_flag(&mut self) { - self.active_this_frame = !self.force_disabled && self.active_flag.load(Ordering::SeqCst); - } - - pub(crate) fn is_active(&self) -> bool { - self.active_this_frame - } - - pub(crate) fn set_focusable(&mut self, node_id: NodeId, focus_id: FocusId) { - self.focus_ids.insert(node_id, focus_id); - } - - /// Report `node_id` as the currently-focused node, if it is present in the - /// tree. - /// - /// Must only be called once per frame. - pub(crate) fn set_focus(&mut self, node_id: NodeId) { - // A focused node must have been registered as focusable this frame. - if !self.focus_ids.contains_key(&node_id) { - if cfg!(debug_assertions) { - panic!("set_focus called for a node that was not registered with set_focusable"); - } else { - log::warn!( - "a11y: set_focus called for a node that was not registered with \ - set_focusable ({node_id:?})" - ); - } - } - if self.nodes.has_node(node_id) { - // The focused element is properly exposed; reset the dedup so a - // later focus on a node-less element logs again. - self.last_focus_without_node = None; - self.nodes.set_focus(node_id); - } else { - // The element registered a focus handle and an id, but never got a - // node because it has no role. - if let Some(focus_id) = self.focus_ids.get(&node_id).copied() { - self.note_focus_without_node(focus_id, "it has an id but no role"); - } - } - } - - pub(crate) fn set_active_descendant(&mut self, node_id: NodeId) { - // The active descendant must be a descendant of the focused container, - // not the focused node itself. - if self.nodes.node_is_focused(node_id) { - if cfg!(debug_assertions) { - panic!("set_active_descendant called on the focused node"); - } else { - log::warn!("a11y: set_active_descendant called on the focused node ({node_id:?})"); - } - return; - } - if self.nodes.has_node(node_id) && self.nodes.focus_is_ancestor_of_current() { - self.nodes.set_active_descendant(node_id); - } - } - - /// Clear per-frame state and push the root node to start a new frame. - pub(crate) fn begin_frame(&mut self) { - self.focus_ids.clear(); - self.node_bounds.clear(); - self.action_listeners.clear(); - self.nodes.begin_frame(self.window_title.as_ref()); - } - - /// Finalize the tree and produce a [`TreeUpdate`] for the platform adapter. - pub(crate) fn end_frame(&mut self, frame: debug::FrameDebugInfo) -> TreeUpdate { - let update = self.nodes.finalize(); - self.debug.capture( - &update, - self.nodes.focus, - self.nodes.active_descendant, - self.window_title.as_ref(), - frame, - ); - #[cfg(debug_assertions)] - self.debug.capture_node_info(&self.nodes.node_info); - update - } - - pub(crate) fn debug_tree_json(&self) -> Option { - self.debug.to_json() - } -} - -/// Builder API for synthetic children. See the docs for -/// [`Element::a11y_synthetic_children`]. -pub struct A11ySubtreeBuilder<'a> { - parent_id: NodeId, - nodes: &'a mut A11yNodeBuilder, - /// Provenance of the real element whose `a11y_synthetic_children` is - /// running. - #[cfg(debug_assertions)] - creator: debug::NodeCreator, -} - -impl<'a> A11ySubtreeBuilder<'a> { - pub(crate) fn new(parent_id: NodeId, nodes: &'a mut A11yNodeBuilder) -> Self { - Self { - parent_id, - nodes, - #[cfg(debug_assertions)] - creator: debug::NodeCreator::default(), - } - } - - #[cfg(debug_assertions)] - pub(crate) fn with_creator(mut self, creator: debug::NodeCreator) -> Self { - self.creator = creator; - self - } - - /// Derive a [`NodeId`] for a synthetic child. - /// - /// The generated ID is based on the hash of `key`, as well as the parent's - /// ID. This means that `key`s must be unique within the same - /// [`Element::a11y_synthetic_children`] call, but may be duplicated across - /// different calls. - pub fn synthetic_node_id(&self, key: impl Hash) -> NodeId { - let mut hasher = std::hash::DefaultHasher::default(); - self.parent_id.0.hash(&mut hasher); - key.hash(&mut hasher); - NodeId(hasher.finish()) - } - - /// Append a synthetic leaf node as a child of this element's node. - /// - /// Returns `false` if a node with this id is already present in the tree, - /// in which case the node is discarded. - pub fn push_child(&mut self, id: NodeId, node: accesskit::Node) -> bool { - let pushed = self.nodes.push_leaf(id, node); - #[cfg(debug_assertions)] - if pushed { - self.nodes.record_node_info( - id, - debug::NodeDebugInfo { - synthetic: true, - view: self.creator.view, - element_id: self.creator.element_id.clone(), - source_location: self.creator.source_location, - }, - ); - } - pushed - } - - /// A mutable reference to the parent node. - pub fn parent_node(&mut self) -> &mut accesskit::Node { - self.nodes - .current_node_mut() - .expect("A11ySubtreeBuilder exists only while its element's node is on the stack") - } -} - -pub(crate) struct A11yNodeBuilder { - ids_stack: SmallVec<[NodeId; 16]>, - nodes_stack: SmallVec<[accesskit::Node; 16]>, - /// This is the exact type required by accesskit, so we can't just make it a - /// `HashMap` to remove the need for `seen_ids` - all_nodes: Vec<(NodeId, accesskit::Node)>, - seen_ids: FxHashSet, - /// The node that GPUI considers focused. Note that this may be different to - /// what is reported to accesskit - see [`Self::active_descendant`] - focus: Option, - /// If a node calls `.aria_active_descendant()`, AND an ancestor is focused, - /// override it as the focused node. This supports the "active descendant" - /// pattern, which allows a focused container to act as if a descendant is - /// focused. - active_descendant: Option, - #[cfg(debug_assertions)] - node_info: FxHashMap, -} - -impl A11yNodeBuilder { - fn new() -> Self { - Self { - ids_stack: SmallVec::new(), - nodes_stack: SmallVec::new(), - all_nodes: Vec::new(), - seen_ids: FxHashSet::default(), - focus: None, - active_descendant: None, - #[cfg(debug_assertions)] - node_info: FxHashMap::default(), - } - } - - /// Records provenance for a node already pushed this frame. Debug builds only. - #[cfg(debug_assertions)] - pub(crate) fn record_node_info(&mut self, id: NodeId, info: debug::NodeDebugInfo) { - self.node_info.insert(id, info); - } - - #[must_use] - fn can_push(&mut self, id: NodeId) -> bool { - debug_assert!(!self.ids_stack.is_empty(), "node pushed before push_root"); - - if !self.seen_ids.insert(id) { - debug_assert!( - false, - "Duplicate a11y node id: {id:?}. In a release build, this node would be silently discarded from the a11y tree." - ); - return false; - } - - true - } - - /// Push a new node onto the stack. It becomes a child of the current - /// top-of-stack node. - /// - /// Returns `true` if the node was successfully pushed. - pub(crate) fn push(&mut self, id: NodeId, node: accesskit::Node) -> bool { - if !self.can_push(id) { - return false; - } - - if let Some(parent) = self.nodes_stack.last_mut() { - parent.push_child(id); - } - self.ids_stack.push(id); - self.nodes_stack.push(node); - true - } - - /// Add a leaf node as a child of the current top-of-stack node, without - /// pushing it onto the stack. Semantically equivalent to a [`Self::push`] - /// followed by a [`Self::pop`]. - /// - /// Returns `true` if the node was successfully pushed. - pub(crate) fn push_leaf(&mut self, id: NodeId, node: accesskit::Node) -> bool { - if !self.can_push(id) { - return false; - } - - if let Some(parent) = self.nodes_stack.last_mut() { - parent.push_child(id); - } - self.all_nodes.push((id, node)); - true - } - - pub(crate) fn current_node_mut(&mut self) -> Option<&mut accesskit::Node> { - self.nodes_stack.last_mut() - } - - /// Pop the current node off the stack and finalize it into the all_nodes - /// list. - pub(crate) fn pop(&mut self) { - debug_assert!(self.ids_stack.len() > 1, "pop would remove the root node"); - - if let (Some(id), Some(node)) = (self.ids_stack.pop(), self.nodes_stack.pop()) { - self.all_nodes.push((id, node)); - } - } - - /// Push the root node to start a new frame. - fn begin_frame(&mut self, window_title: Option<&SharedString>) { - self.all_nodes.clear(); - self.ids_stack.clear(); - self.nodes_stack.clear(); - self.seen_ids.clear(); - #[cfg(debug_assertions)] - self.node_info.clear(); - let mut root_node = accesskit::Node::new(accesskit::Role::Window); - if let Some(title) = window_title { - root_node.set_label(title.to_string()); - } - - self.ids_stack.push(ROOT_NODE_ID); - self.nodes_stack.push(root_node); - self.focus = None; - self.active_descendant = None; - } - - /// Returns whether a node with the given ID has been pushed in this frame. - pub(crate) fn has_node(&self, id: NodeId) -> bool { - id == ROOT_NODE_ID || self.seen_ids.contains(&id) - } - - /// Returns whether `id` is the node currently reported as focused. - pub(crate) fn node_is_focused(&self, id: NodeId) -> bool { - self.focus == Some(id) - } - - pub(crate) fn focus_is_ancestor_of_current(&self) -> bool { - let Some(focus) = self.focus else { - return false; - }; - - // The current node is on top of the stack; everything below it is an - // ancestor. - let ancestor_count = self.ids_stack.len().saturating_sub(1); - self.ids_stack[..ancestor_count].contains(&focus) - } - - pub(crate) fn set_active_descendant(&mut self, id: NodeId) { - if self - .active_descendant - .is_some_and(|existing| existing != id) - { - if cfg!(debug_assertions) { - panic!("active descendant claimed by multiple nodes in one frame"); - } else { - log::warn!( - "a11y: multiple nodes claimed the active descendant this frame; \ - using last-wins ({id:?})" - ); - } - } - self.active_descendant = Some(id); - } - - pub(crate) fn set_focus(&mut self, id: NodeId) { - if self.focus.is_some() { - if cfg!(debug_assertions) { - panic!("set_focus called more than once in a single frame"); - } else { - log::warn!( - "a11y: set_focus called more than once in a single frame; \ - using last-wins ({id:?})" - ); - } - } - self.focus = Some(id); - } - - fn finalize(&mut self) -> TreeUpdate { - // Stack should contain only the root node - debug_assert_eq!(self.ids_stack.len(), 1); - debug_assert_eq!(self.ids_stack[0], ROOT_NODE_ID); - - if self.ids_stack.len() != 1 { - log::error!( - "a11y: Stack imbalance at end of frame: expected 1 (root), got {}. \ - Some elements may have pushed without popping.", - self.ids_stack.len() - ); - } - - // Pop remaining nodes (should just be the root). - while !self.ids_stack.is_empty() { - if let (Some(id), Some(node)) = (self.ids_stack.pop(), self.nodes_stack.pop()) { - self.all_nodes.push((id, node)); - } - } - - let focus = match self.active_descendant { - Some(id) if self.has_node(id) => id, - Some(id) => { - if cfg!(debug_assertions) { - panic!("active_descendant set to {id:?}, which is not in the tree"); - } else { - log::warn!("active_descendant set to {id:?}, which is not in the tree"); - self.focus.unwrap_or(ROOT_NODE_ID) - } - } - - _ => self.focus.unwrap_or(ROOT_NODE_ID), - }; - - let nodes = std::mem::take(&mut self.all_nodes); - let update = TreeUpdate { - nodes, - tree: Some(accesskit::Tree::new(ROOT_NODE_ID)), - tree_id: accesskit::TreeId::ROOT, - focus, - }; - - Self::repair_tree_update(update) - } - - /// Accesskit panics on invalid [`TreeUpdate`]s. This function defensively - /// checks invariants that accesskit panics on, and tries to fix them. - fn repair_tree_update(mut update: TreeUpdate) -> TreeUpdate { - let node_ids: FxHashSet = update.nodes.iter().map(|(id, _)| *id).collect(); - - // Focus must point to a node in the tree. - if !node_ids.contains(&update.focus) { - log::error!( - "a11y: Focused node {:?} is not in the tree ({} nodes). \ - Falling back to root. This is a bug in the a11y tree builder.", - update.focus, - update.nodes.len() - ); - update.focus = ROOT_NODE_ID; - } - - // Every child reference must point to a node in the update. - for (id, node) in &mut update.nodes { - let has_invalid_child = node - .children() - .iter() - .any(|child_id| !node_ids.contains(child_id)); - if has_invalid_child { - let children = node.children(); - let invalid_count = children - .iter() - .filter(|child_id| !node_ids.contains(child_id)) - .count(); - log::error!( - "a11y: Node {:?} references {} children not present in the tree. \ - Stripping invalid child references.", - id, - invalid_count - ); - let valid: Vec = children - .iter() - .copied() - .filter(|child_id| node_ids.contains(child_id)) - .collect(); - node.set_children(valid); - } - } - - update - } -} - -#[cfg(test)] -mod tests { - // Import specific items rather than glob-importing `super`, which would pull - // in gpui's own `test` attribute macro and shadow the standard one. - use super::{A11y, A11yNodeBuilder, ROOT_NODE_ID}; - use crate::FocusId; - use accesskit::{NodeId, Role}; - use std::sync::{Arc, atomic::AtomicBool}; - - fn test_node() -> accesskit::Node { - accesskit::Node::new(Role::GenericContainer) - } - - fn new_builder() -> A11yNodeBuilder { - let mut builder = A11yNodeBuilder::new(); - builder.begin_frame(None); - builder - } - - fn new_a11y() -> A11y { - let mut a11y = A11y::new(Arc::new(AtomicBool::new(true)), false, None); - a11y.begin_frame(); - a11y - } - - #[test] - fn active_descendant_honored_when_container_focused() { - let mut builder = new_builder(); - let container = NodeId(1); - let item = NodeId(2); - - assert!(builder.push(container, test_node())); - builder.set_focus(container); - assert!(builder.push(item, test_node())); - - // The item is on top of the stack; the focused container is its - // ancestor, so the claim is honored. - assert!(builder.focus_is_ancestor_of_current()); - builder.set_active_descendant(item); - - builder.pop(); // item - builder.pop(); // container - let update = builder.finalize(); - assert_eq!(update.focus, item); - } - - #[test] - fn active_descendant_honored_for_deep_descendant() { - let mut builder = new_builder(); - let container = NodeId(1); - let group = NodeId(2); - let item = NodeId(3); - - assert!(builder.push(container, test_node())); - builder.set_focus(container); - assert!(builder.push(group, test_node())); - assert!(builder.push(item, test_node())); - - // The item is a grandchild of the focused container; depth doesn't - // matter, the focused ancestor is still on the stack. - assert!(builder.focus_is_ancestor_of_current()); - builder.set_active_descendant(item); - - builder.pop(); // item - builder.pop(); // group - builder.pop(); // container - let update = builder.finalize(); - assert_eq!(update.focus, item); - } - - #[test] - fn active_descendant_ignored_when_focus_in_other_subtree() { - let mut builder = new_builder(); - let focused_container = NodeId(1); - let focused_leaf = NodeId(2); - let other_container = NodeId(3); - let other_item = NodeId(4); - - // First subtree holds real focus. - assert!(builder.push(focused_container, test_node())); - assert!(builder.push(focused_leaf, test_node())); - builder.set_focus(focused_leaf); - builder.pop(); // focused_leaf - builder.pop(); // focused_container - - // Second subtree: its item would claim the active descendant, but the - // focus is not on any of its ancestors, so the gate rejects it. - assert!(builder.push(other_container, test_node())); - assert!(builder.push(other_item, test_node())); - assert!(!builder.focus_is_ancestor_of_current()); - builder.pop(); // other_item - builder.pop(); // other_container - - let update = builder.finalize(); - assert_eq!(update.focus, focused_leaf); - } - - #[test] - fn active_descendant_ignored_when_nothing_focused() { - let mut builder = new_builder(); - let container = NodeId(1); - let item = NodeId(2); - - assert!(builder.push(container, test_node())); - assert!(builder.push(item, test_node())); - - // Nothing is focused (focus defaults to the root window node), so the - // gate rejects the claim. - assert!(!builder.focus_is_ancestor_of_current()); - builder.pop(); - builder.pop(); - - let update = builder.finalize(); - assert_eq!(update.focus, ROOT_NODE_ID); - } - - #[test] - fn regular_focus_used_when_no_active_descendant() { - let mut builder = new_builder(); - let focused = NodeId(1); - - assert!(builder.push(focused, test_node())); - builder.set_focus(focused); - builder.pop(); - - let update = builder.finalize(); - assert_eq!(update.focus, focused); - } - - #[test] - fn focus_is_ancestor_excludes_self_and_non_ancestors() { - let mut builder = new_builder(); - let container = NodeId(1); - let item = NodeId(2); - - assert!(builder.push(container, test_node())); - builder.set_focus(container); - - // With the focused container itself on top, it is not its own (strict) - // ancestor, so the gate is false. - assert!(!builder.focus_is_ancestor_of_current()); - - assert!(builder.push(item, test_node())); - // Now the focused container is a strict ancestor of the item on top. - assert!(builder.focus_is_ancestor_of_current()); - - builder.pop(); - builder.pop(); - } - - // The double-claim guard panics only in debug builds; in release it falls - // back to last-wins with a warning. - #[test] - #[cfg_attr( - debug_assertions, - should_panic(expected = "active descendant claimed by multiple nodes") - )] - fn multiple_active_descendant_claims_panic_in_debug() { - let mut builder = new_builder(); - builder.set_active_descendant(NodeId(1)); - builder.set_active_descendant(NodeId(2)); - } - - // Setting focus twice in one frame means two elements both claimed window - // focus; that panics in debug and falls back to last-wins in release. - #[test] - #[cfg_attr( - debug_assertions, - should_panic(expected = "set_focus called more than once") - )] - fn setting_focus_twice_panics_in_debug() { - let mut builder = new_builder(); - builder.set_focus(NodeId(1)); - builder.set_focus(NodeId(2)); - } - - // Focusing a node that was never registered as focusable is a bug: panic in - // debug, warn in release. - #[test] - #[cfg_attr( - debug_assertions, - should_panic(expected = "was not registered with set_focusable") - )] - fn set_focus_without_set_focusable() { - let mut a11y = new_a11y(); - let node = NodeId(1); - assert!(a11y.nodes.push(node, test_node())); - // set_focusable was never called for `node`. - a11y.set_focus(node); - } - - // The focused node cannot also be its own active descendant: panic in - // debug, warn in release. - #[test] - #[cfg_attr(debug_assertions, should_panic(expected = "on the focused node"))] - fn set_active_descendant_on_focused_node() { - let mut a11y = new_a11y(); - let node = NodeId(1); - assert!(a11y.nodes.push(node, test_node())); - a11y.set_focusable(node, FocusId::default()); - a11y.set_focus(node); - a11y.set_active_descendant(node); - } - - // Two sibling children of a focused container both claim the active - // descendant (both pass the focus gate). The second claim is a bug: panic - // in debug, last-wins + warn in release. - #[test] - #[cfg_attr( - debug_assertions, - should_panic(expected = "active descendant claimed by multiple nodes") - )] - fn two_siblings_claiming_active_descendant() { - let mut a11y = new_a11y(); - let container = NodeId(1); - let first = NodeId(2); - let second = NodeId(3); - - assert!(a11y.nodes.push(container, test_node())); - a11y.set_focusable(container, FocusId::default()); - a11y.set_focus(container); - - assert!(a11y.nodes.push(first, test_node())); - a11y.set_active_descendant(first); - a11y.nodes.pop(); // first - - assert!(a11y.nodes.push(second, test_node())); - a11y.set_active_descendant(second); - a11y.nodes.pop(); // second - - a11y.nodes.pop(); // container - } - - // Node A is focused; node C (a child of the unfocused node B) claims the - // active descendant. The final tree must still report A as focused. - #[test] - fn active_descendant_in_unfocused_subtree_keeps_real_focus() { - let mut a11y = new_a11y(); - let a = NodeId(1); - let b = NodeId(2); - let c = NodeId(3); - - assert!(a11y.nodes.push(a, test_node())); - a11y.set_focusable(a, FocusId::default()); - a11y.set_focus(a); - a11y.nodes.pop(); // a - - assert!(a11y.nodes.push(b, test_node())); - assert!(a11y.nodes.push(c, test_node())); - a11y.set_active_descendant(c); - a11y.nodes.pop(); // c - a11y.nodes.pop(); // b - - let update = a11y.end_frame(Default::default()); - assert_eq!(update.focus, a); - } -} diff --git a/crates/gpui_pre/src/window/a11y/debug.rs b/crates/gpui_pre/src/window/a11y/debug.rs deleted file mode 100644 index ae98aa52..00000000 --- a/crates/gpui_pre/src/window/a11y/debug.rs +++ /dev/null @@ -1,330 +0,0 @@ -//! Developer tooling for inspecting the accessibility tree. -//! -//! [`A11yDebug`] retains the last [`TreeUpdate`] sent to the platform adapter so -//! it can be serialized on demand (see -//! [`crate::Window::debug_a11y_tree_json`]). In `cfg(debug_assertions)` builds, -//! we capture extra info. - -use accesskit::{Action, NodeId, TreeUpdate}; -use collections::FxHashMap; - -use crate::{Pixels, SharedString, Size}; - -#[derive(Default)] -pub(crate) struct FrameDebugInfo { - pub viewport_size: Size, - pub scale_factor: f32, - pub tab_stop_count: usize, -} - -struct CapturedFrame { - rendered_at: String, - frame_number: u64, - window_title: Option, - node_count: usize, - tab_stop_count: usize, - viewport_size: Size, - scale_factor: f32, -} - -#[cfg(debug_assertions)] -#[derive(Clone, Default)] -pub(crate) struct NodeDebugInfo { - /// Whether the node was synthesized via - /// [`crate::Element::a11y_synthetic_children`] rather than created from a - /// real element with a role and ID. - pub synthetic: bool, - /// The type name of the `Render` view that was rendering when the node was - /// created. - pub view: Option<&'static str>, - /// The [`ElementId`](crate::ElementId) of the creating element (the leaf of - /// its `GlobalElementId`, not the full path). For a synthetic node, this is - /// the real element whose `a11y_synthetic_children` produced it. - pub element_id: Option, - /// Source location where the creating element was constructed. - pub source_location: Option<&'static core::panic::Location<'static>>, -} - -#[cfg(debug_assertions)] -#[derive(Clone, Default)] -pub(crate) struct NodeCreator { - pub view: Option<&'static str>, - pub element_id: Option, - pub source_location: Option<&'static core::panic::Location<'static>>, -} - -#[derive(Default)] -pub(crate) struct A11yDebug { - last_tree_update: Option, - last_gpui_focus: Option, - last_active_descendant: Option, - /// Monotonic counter incremented on each captured frame, so a re-dump makes - /// it obvious whether the tree actually refreshed. - frame_number: u64, - /// Metadata about the most recently captured frame. - last_frame: Option, - #[cfg(debug_assertions)] - last_node_info: FxHashMap, -} - -impl A11yDebug { - pub(crate) fn capture( - &mut self, - update: &TreeUpdate, - gpui_focus: Option, - active_descendant: Option, - window_title: Option<&SharedString>, - frame: FrameDebugInfo, - ) { - self.last_tree_update = Some(update.clone()); - self.last_gpui_focus = gpui_focus; - self.last_active_descendant = active_descendant; - self.frame_number += 1; - self.last_frame = Some(CapturedFrame { - rendered_at: chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, false), - frame_number: self.frame_number, - window_title: window_title.cloned(), - node_count: update.nodes.len(), - tab_stop_count: frame.tab_stop_count, - viewport_size: frame.viewport_size, - scale_factor: frame.scale_factor, - }); - } - - #[cfg(debug_assertions)] - pub(crate) fn capture_node_info(&mut self, node_info: &FxHashMap) { - self.last_node_info = node_info.clone(); - } - - /// Serialize the last tree update to a readable JSON string. Node ids are - /// replaced with short ephemeral ids (`a`, `b`, ..., `z`, `aa`, ...). - pub(crate) fn to_json(&self) -> Option { - let update = self.last_tree_update.as_ref()?; - - let mut ephemeral: FxHashMap = FxHashMap::default(); - for (index, (id, _)) in update.nodes.iter().enumerate() { - ephemeral.insert(*id, ephemeral_id(index)); - } - - let mut nodes = serde_json::Map::new(); - for (id, node) in &update.nodes { - let key = ephemeral - .get(id) - .cloned() - .unwrap_or_else(|| id.0.to_string()); - #[cfg(debug_assertions)] - let provenance = self - .last_node_info - .get(id) - .map(|info| NodeProvenance { - element_id: info.element_id.as_deref(), - view: info.view, - source_location: info.source_location.map(|loc| loc.to_string()), - // Only surface synthetic nodes; `false` is the default and - // would just be noise on every real node. - synthetic: info.synthetic.then_some(true), - }) - .unwrap_or_default(); - #[cfg(not(debug_assertions))] - let provenance = NodeProvenance::default(); - let value = node_to_json(*id, node, &ephemeral, &provenance); - nodes.insert(key, value); - } - - let frame = self.last_frame.as_ref().map(|frame| { - serde_json::json!({ - "rendered_at": frame.rendered_at, - "frame_number": frame.frame_number, - "window_title": frame.window_title.as_ref().map(|title| title.to_string()), - "node_count": frame.node_count, - "tab_stop_count": frame.tab_stop_count, - "viewport_size": { - "width": frame.viewport_size.width.0, - "height": frame.viewport_size.height.0, - }, - "scale_factor": frame.scale_factor, - }) - }); - - let root = update - .tree - .as_ref() - .map(|tree| tree.root) - .and_then(|id| ephemeral.get(&id).cloned()); - - let value = serde_json::json!({ - "root": root, - "gpui_focus": self.last_gpui_focus.and_then(|id| ephemeral.get(&id).cloned()), - "active_descendant_focus": self.last_active_descendant.and_then(|id| ephemeral.get(&id).cloned()), - "frame": frame, - "nodes": nodes, - }); - Some(serde_json::to_string_pretty(&value).unwrap_or_else(|_| "{}".to_string())) - } -} - -#[derive(Default)] -struct NodeProvenance<'a> { - element_id: Option<&'a str>, - view: Option<&'a str>, - source_location: Option, - synthetic: Option, -} - -fn node_to_json( - id: NodeId, - node: &accesskit::Node, - ephemeral: &FxHashMap, - provenance: &NodeProvenance, -) -> serde_json::Value { - use serde_json::json; - - let mut map = serde_json::Map::new(); - map.insert("accesskit_id".into(), json!(id.0.to_string())); - - let children: Vec = node - .children() - .iter() - .map(|child| { - ephemeral - .get(child) - .cloned() - .unwrap_or_else(|| child.0.to_string()) - }) - .collect(); - if !children.is_empty() { - map.insert("children".into(), json!(children)); - } - - // Provenance (debug builds only), ordered before the accessibility section. - if let Some(element_id) = provenance.element_id { - map.insert("element_id".into(), json!(element_id)); - } - if let Some(view) = provenance.view { - map.insert("view".into(), json!(view)); - } - if let Some(source_location) = &provenance.source_location { - map.insert("source_location".into(), json!(source_location)); - } - if let Some(synthetic) = provenance.synthetic { - map.insert("synthetic".into(), json!(synthetic)); - } - - // Accessibility semantics for this node, grouped together. - let mut aria = serde_json::Map::new(); - aria.insert("role".into(), json!(format!("{:?}", node.role()))); - - // Which action types the node supports. AccessKit keeps these in a private - // bitset with no getter or iterator, so we probe each variant. `Action::n` - // (from AccessKit's `enumn` feature) maps a discriminant to its variant, - // returning `None` past the last one - so this can't drift out of sync with - // AccessKit's `Action` enum the way a hand-maintained list would. - let mut next_action = 0u8; - let on_action: Vec = std::iter::from_fn(move || { - let action = Action::n(next_action)?; - next_action += 1; - Some(action) - }) - .filter(|action| node.supports_action(*action)) - .map(|action| format!("{action:?}")) - .collect(); - if !on_action.is_empty() { - aria.insert("on_action".into(), json!(on_action)); - } - - // String properties. - if let Some(v) = node.label() { - aria.insert("label".into(), json!(v)); - } - if let Some(v) = node.description() { - aria.insert("description".into(), json!(v)); - } - if let Some(v) = node.value() { - aria.insert("value".into(), json!(v)); - } - if let Some(v) = node.keyboard_shortcut() { - aria.insert("keyboard_shortcut".into(), json!(v)); - } - if let Some(v) = node.access_key() { - aria.insert("access_key".into(), json!(v)); - } - if let Some(v) = node.placeholder() { - aria.insert("placeholder".into(), json!(v)); - } - if let Some(v) = node.tooltip() { - aria.insert("tooltip".into(), json!(v)); - } - if let Some(v) = node.role_description() { - aria.insert("role_description".into(), json!(v)); - } - - // Boolean / enum states. - if let Some(v) = node.is_selected() { - aria.insert("selected".into(), json!(v)); - } - if let Some(v) = node.is_expanded() { - aria.insert("expanded".into(), json!(v)); - } - if let Some(v) = node.toggled() { - aria.insert("toggled".into(), json!(format!("{v:?}"))); - } - if let Some(v) = node.orientation() { - aria.insert("orientation".into(), json!(format!("{v:?}"))); - } - - // Numeric properties. - if let Some(v) = node.numeric_value() { - aria.insert("numeric_value".into(), json!(v)); - } - if let Some(v) = node.min_numeric_value() { - aria.insert("min_numeric_value".into(), json!(v)); - } - if let Some(v) = node.max_numeric_value() { - aria.insert("max_numeric_value".into(), json!(v)); - } - if let Some(v) = node.numeric_value_step() { - aria.insert("numeric_value_step".into(), json!(v)); - } - - // Set / table properties. - if let Some(v) = node.level() { - aria.insert("level".into(), json!(v)); - } - if let Some(v) = node.position_in_set() { - aria.insert("position_in_set".into(), json!(v)); - } - if let Some(v) = node.size_of_set() { - aria.insert("size_of_set".into(), json!(v)); - } - if let Some(v) = node.row_index() { - aria.insert("row_index".into(), json!(v)); - } - if let Some(v) = node.column_index() { - aria.insert("column_index".into(), json!(v)); - } - if let Some(v) = node.row_count() { - aria.insert("row_count".into(), json!(v)); - } - if let Some(v) = node.column_count() { - aria.insert("column_count".into(), json!(v)); - } - - map.insert("aria".into(), serde_json::Value::Object(aria)); - - serde_json::Value::Object(map) -} - -/// Maps a 0-based index to a short id in the sequence `a, b, ..., z, aa, ab, -/// ...` (bijective base-26). -fn ephemeral_id(mut index: usize) -> String { - let mut bytes = Vec::new(); - loop { - bytes.push(b'a' + (index % 26) as u8); - if index < 26 { - break; - } - index = index / 26 - 1; - } - bytes.reverse(); - String::from_utf8(bytes).unwrap_or_default() -} diff --git a/crates/gpui_pre/src/window/prompts.rs b/crates/gpui_pre/src/window/prompts.rs deleted file mode 100644 index 980c6f68..00000000 --- a/crates/gpui_pre/src/window/prompts.rs +++ /dev/null @@ -1,232 +0,0 @@ -use std::ops::Deref; - -use futures::channel::oneshot; - -use crate::{ - AnyView, App, AppContext as _, Context, Entity, EventEmitter, FocusHandle, Focusable, - InteractiveElement, IntoElement, ParentElement, PromptButton, PromptLevel, Render, - StatefulInteractiveElement, Styled, div, opaque_grey, white, -}; - -use super::Window; - -/// The event emitted when a prompt's option is selected. -/// The usize is the index of the selected option, from the actions -/// passed to the prompt. -pub struct PromptResponse(pub usize); - -/// A prompt that can be rendered in the window. -pub trait Prompt: EventEmitter + Focusable {} - -impl + Focusable> Prompt for V {} - -/// A handle to a prompt that can be used to interact with it. -pub struct PromptHandle { - sender: oneshot::Sender, -} - -impl PromptHandle { - pub(crate) fn new(sender: oneshot::Sender) -> Self { - Self { sender } - } - - /// Construct a new prompt handle from a view of the appropriate types - pub fn with_view( - self, - view: Entity, - window: &mut Window, - cx: &mut App, - ) -> RenderablePromptHandle { - let mut sender = Some(self.sender); - let previous_focus = window.focused(cx); - let window_handle = window.window_handle(); - cx.subscribe(&view, move |_: Entity, e: &PromptResponse, cx| { - if let Some(sender) = sender.take() { - sender.send(e.0).ok(); - window_handle - .update(cx, |_, window, cx| { - window.prompt.take(); - if let Some(previous_focus) = &previous_focus { - window.focus(previous_focus, cx); - } - }) - .ok(); - } - }) - .detach(); - - window.focus(&view.focus_handle(cx), cx); - - RenderablePromptHandle { - view: Box::new(view), - } - } -} - -/// A prompt handle capable of being rendered in a window. -pub struct RenderablePromptHandle { - pub(crate) view: Box, -} - -/// Use this function in conjunction with [App::set_prompt_builder] to force -/// GPUI to always use the fallback prompt renderer. -pub fn fallback_prompt_renderer( - level: PromptLevel, - message: &str, - detail: Option<&str>, - actions: &[PromptButton], - handle: PromptHandle, - window: &mut Window, - cx: &mut App, -) -> RenderablePromptHandle { - let renderer = cx.new(|cx| FallbackPromptRenderer { - _level: level, - message: message.to_string(), - detail: detail.map(ToString::to_string), - actions: actions.to_vec(), - focus: cx.focus_handle(), - }); - - handle.with_view(renderer, window, cx) -} - -/// The default GPUI fallback for rendering prompts, when the platform doesn't support it. -pub struct FallbackPromptRenderer { - _level: PromptLevel, - message: String, - detail: Option, - actions: Vec, - focus: FocusHandle, -} - -impl Render for FallbackPromptRenderer { - fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { - let prompt = div() - .cursor_default() - .track_focus(&self.focus) - .w_72() - .bg(white()) - .rounded_lg() - .overflow_hidden() - .p_3() - .child( - div() - .w_full() - .flex() - .flex_row() - .justify_around() - .child(div().overflow_hidden().child(self.message.clone())), - ) - .children(self.detail.clone().map(|detail| { - div() - .w_full() - .flex() - .flex_row() - .justify_around() - .text_sm() - .mb_2() - .child(div().child(detail)) - })) - .children(self.actions.iter().enumerate().map(|(ix, action)| { - div() - .flex() - .flex_row() - .justify_around() - .border_1() - .border_color(opaque_grey(0.2, 0.5)) - .mt_1() - .rounded_xs() - .cursor_pointer() - .text_sm() - .child(action.label().clone()) - .id(ix) - .on_click(cx.listener(move |_, _, _, cx| { - cx.emit(PromptResponse(ix)); - cx.stop_propagation(); - })) - })); - - div() - .size_full() - .child( - div() - .size_full() - .bg(opaque_grey(0.5, 0.6)) - .absolute() - .top_0() - .left_0(), - ) - .child( - div() - .size_full() - .absolute() - .top_0() - .left_0() - .flex() - .flex_col() - .justify_around() - .child( - div() - .w_full() - .flex() - .flex_row() - .justify_around() - .child(prompt), - ), - ) - } -} - -impl EventEmitter for FallbackPromptRenderer {} - -impl Focusable for FallbackPromptRenderer { - fn focus_handle(&self, _: &crate::App) -> FocusHandle { - self.focus.clone() - } -} - -pub(crate) trait PromptViewHandle { - fn any_view(&self) -> AnyView; -} - -impl PromptViewHandle for Entity { - fn any_view(&self) -> AnyView { - self.clone().into() - } -} - -pub(crate) enum PromptBuilder { - Default, - Custom( - Box< - dyn Fn( - PromptLevel, - &str, - Option<&str>, - &[PromptButton], - PromptHandle, - &mut Window, - &mut App, - ) -> RenderablePromptHandle, - >, - ), -} - -impl Deref for PromptBuilder { - type Target = dyn Fn( - PromptLevel, - &str, - Option<&str>, - &[PromptButton], - PromptHandle, - &mut Window, - &mut App, - ) -> RenderablePromptHandle; - - fn deref(&self) -> &Self::Target { - match self { - Self::Default => &fallback_prompt_renderer, - Self::Custom(f) => f.as_ref(), - } - } -} diff --git a/crates/gpui_pre/tests/action_macros.rs b/crates/gpui_pre/tests/action_macros.rs deleted file mode 100644 index 66ef6fba..00000000 --- a/crates/gpui_pre/tests/action_macros.rs +++ /dev/null @@ -1,55 +0,0 @@ -use gpui::{Action, actions}; -use gpui_macros::register_action; -use schemars::JsonSchema; -use serde::Deserialize; - -#[test] -fn test_action_macros() { - actions!( - test_only, - [ - SomeAction, - /// Documented action - SomeActionWithDocs, - ] - ); - - #[derive(PartialEq, Clone, Deserialize, JsonSchema, Action)] - #[action(namespace = test_only)] - #[serde(deny_unknown_fields)] - struct AnotherAction; - - #[derive(PartialEq, Clone, gpui::private::serde::Deserialize)] - #[serde(deny_unknown_fields)] - struct RegisterableAction {} - - register_action!(RegisterableAction); - - impl gpui::Action for RegisterableAction { - fn boxed_clone(&self) -> Box { - unimplemented!() - } - - fn partial_eq(&self, _action: &dyn gpui::Action) -> bool { - unimplemented!() - } - - fn name(&self) -> &'static str { - unimplemented!() - } - - fn name_for_type() -> &'static str - where - Self: Sized, - { - unimplemented!() - } - - fn build(_value: serde_json::Value) -> anyhow::Result> - where - Self: Sized, - { - unimplemented!() - } - } -} diff --git a/crates/gpui_pre_apple/Cargo.lock b/crates/gpui_pre_apple/Cargo.lock deleted file mode 100644 index 72ca7627..00000000 --- a/crates/gpui_pre_apple/Cargo.lock +++ /dev/null @@ -1,4811 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "accesskit" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b7f7f85a7e5f68090000ed7622545829afd484d210358702ae4cb97dd0c320" -dependencies = [ - "enumn", - "uuid", -] - -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "aligned" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" -dependencies = [ - "as-slice", -] - -[[package]] -name = "aligned-vec" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" -dependencies = [ - "equator", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - -[[package]] -name = "anstyle" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" - -[[package]] -name = "anyhow" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" - -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" - -[[package]] -name = "arg_enum_proc_macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "as-slice" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-compression" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a89bce6054c720275ac2432fbba080a66a2106a44a1b804553930ca6909f4e0" -dependencies = [ - "compression-codecs", - "compression-core", - "futures-core", - "futures-io", - "pin-project-lite", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "av-scenechange" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" -dependencies = [ - "aligned", - "anyhow", - "arg_enum_proc_macro", - "arrayvec", - "log", - "num-rational", - "num-traits", - "pastey", - "rayon", - "thiserror 2.0.17", - "v_frame", - "y4m", -] - -[[package]] -name = "av1-grain" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link 0.2.1", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags 2.13.1", - "cexpr", - "clang-sys", - "itertools 0.11.0", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex 1.3.0", - "syn 2.0.117", -] - -[[package]] -name = "bit-set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" - -[[package]] -name = "bit_field" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" - -[[package]] -name = "bitstream-io" -version = "4.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" -dependencies = [ - "core2", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "borsh" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" -dependencies = [ - "cfg_aliases", -] - -[[package]] -name = "built" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytemuck" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "bzip2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" -dependencies = [ - "libbz2-rs-sys", -] - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - -[[package]] -name = "cbindgen" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff" -dependencies = [ - "heck 0.4.1", - "indexmap", - "log", - "proc-macro2", - "quote", - "serde", - "serde_json", - "syn 2.0.117", - "tempfile", - "toml", -] - -[[package]] -name = "cc" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex 2.0.1", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "cgl" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" -dependencies = [ - "libc", -] - -[[package]] -name = "chrono" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link 0.2.1", -] - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "4.5.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f" -dependencies = [ - "clap_builder", -] - -[[package]] -name = "clap_builder" -version = "4.5.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730" -dependencies = [ - "anstyle", - "clap_lex", -] - -[[package]] -name = "clap_lex" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" - -[[package]] -name = "cocoa" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation 0.1.2", - "core-foundation 0.9.4", - "core-graphics 0.23.2", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" -dependencies = [ - "bitflags 2.13.1", - "block", - "cocoa-foundation 0.2.0", - "core-foundation 0.10.0", - "core-graphics 0.24.0", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" -dependencies = [ - "bitflags 2.13.1", - "block", - "core-foundation 0.10.0", - "core-graphics-types 0.2.0", - "libc", - "objc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "compression-codecs" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8a506ec4b81c460798f572caead636d57d3d7e940f998160f52bd254bf2d23" -dependencies = [ - "bzip2", - "compression-core", - "flate2", - "memchr", -] - -[[package]] -name = "compression-core" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "convert_case" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "core-graphics" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" -dependencies = [ - "bitflags 2.13.1", - "core-foundation 0.10.0", - "core-graphics-types 0.2.0", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-helmer-fork" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32eb7c354ae9f6d437a6039099ce7ecd049337a8109b23d73e48e8ffba8e9cd5" -dependencies = [ - "bitflags 2.13.1", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" -dependencies = [ - "bitflags 2.13.1", - "core-foundation 0.10.0", - "libc", -] - -[[package]] -name = "core-graphics2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4416167a69126e617f8d0a214af0e3c1dbdeffcb100ddf72dcd1a1ac9893c146" -dependencies = [ - "bitflags 2.13.1", - "block", - "cfg-if", - "core-foundation 0.10.0", - "libc", -] - -[[package]] -name = "core-video" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139679cc63eb9504bdbe37e37874b0247136177655f0008588781e90863afa62" -dependencies = [ - "block", - "core-foundation 0.10.0", - "core-graphics2", - "io-surface", - "libc", - "metal", -] - -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - -[[package]] -name = "core_maths" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" -dependencies = [ - "libm", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "criterion" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" -dependencies = [ - "anes", - "cast", - "ciborium", - "clap", - "criterion-plot", - "is-terminal", - "itertools 0.10.5", - "num-traits", - "once_cell", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools 0.10.5", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - -[[package]] -name = "ctor" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d83cb7e7a873830708d6b02a78cd36a592c6fa14bf267b68725103b85c0d77f" -dependencies = [ - "link-section", - "linktime-proc-macro", -] - -[[package]] -name = "data-url" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" - -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "convert_case 0.10.0", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.117", - "unicode-xid", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "enumn" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "equator" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" -dependencies = [ - "equator-macro", -] - -[[package]] -name = "equator-macro" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "erased-serde" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" -dependencies = [ - "serde", - "serde_core", - "typeid", -] - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "etagere" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" -dependencies = [ - "euclid", - "svg_fmt", -] - -[[package]] -name = "euclid" -version = "0.22.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" -dependencies = [ - "num-traits", -] - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - -[[package]] -name = "exr" -version = "1.74.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" -dependencies = [ - "bit_field", - "half", - "lebe", - "miniz_oxide", - "rayon-core", - "smallvec", - "zune-inflate", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "fax" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" -dependencies = [ - "fax_derive", -] - -[[package]] -name = "fax_derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" - -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - -[[package]] -name = "flate2" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" - -[[package]] -name = "float_next_after" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" - -[[package]] -name = "flume" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" -dependencies = [ - "fastrand", - "futures-core", - "futures-sink", - "spin 0.9.8", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "fontconfig-parser" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" -dependencies = [ - "roxmltree 0.20.0", -] - -[[package]] -name = "fontdb" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" -dependencies = [ - "fontconfig-parser", - "log", - "memmap2", - "slotmap", - "tinyvec", - "ttf-parser", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-concurrency" -version = "7.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175cd8cca9e1d45b87f18ffa75088f2099e3c4fe5e2f83e42de112560bea8ea6" -dependencies = [ - "fixedbitset", - "futures-core", - "futures-lite", - "pin-project", - "smallvec", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", - "wasip3", -] - -[[package]] -name = "gif" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" -dependencies = [ - "color_quant", - "weezl", -] - -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "gpui-pre" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b0f6f593153b6eb84c336ea7ccf12a1fca186f0db99af628e72f85048c1d7b" -dependencies = [ - "accesskit", - "anyhow", - "async-channel", - "async-task", - "backtrace", - "bindgen", - "bitflags 2.13.1", - "chrono", - "core-video", - "criterion", - "ctor", - "derive_more", - "etagere", - "futures", - "futures-concurrency", - "getrandom 0.3.4", - "gpui-pre-collections", - "gpui-pre-http-client", - "gpui-pre-macros", - "gpui-pre-refineable", - "gpui-pre-scheduler", - "gpui-pre-shared-string", - "gpui-pre-sum-tree", - "gpui-pre-util", - "gpui-pre-util-macros", - "gpui-pre-ztracing", - "hdrhistogram", - "heapless", - "image", - "inventory", - "itertools 0.14.0", - "log", - "lyon", - "num_cpus", - "parking", - "parking_lot", - "pin-project", - "pollster 0.4.0", - "postage", - "profiling", - "proptest", - "rand 0.9.4", - "raw-window-handle", - "regex", - "resvg", - "schemars", - "seahash", - "serde", - "serde_json", - "slotmap", - "smallvec", - "spin 0.10.0", - "strum", - "taffy", - "thiserror 2.0.17", - "tracing", - "ttf-parser", - "url", - "usvg", - "uuid", - "waker-fn", - "web-time", - "windows 0.62.2", - "zed-scap", -] - -[[package]] -name = "gpui-pre-apple" -version = "0.3.3" -dependencies = [ - "anyhow", - "block", - "cbindgen", - "cocoa 0.26.0", - "core-foundation 0.10.0", - "core-video", - "derive_more", - "etagere", - "foreign-types", - "gpui-pre", - "gpui-pre-collections", - "image", - "log", - "metal", - "objc", - "parking_lot", -] - -[[package]] -name = "gpui-pre-collections" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c8efa2e51e368c8538a7be1ea9a12127ca03abe6cc01d9d3474e9ac4f53016" -dependencies = [ - "gpui-pre-util", - "indexmap", - "rustc-hash", -] - -[[package]] -name = "gpui-pre-derive-refineable" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a098d319acc9f84bf159944f96c5ea43a4f4cd7ed759f984acbd15719495aa0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gpui-pre-http-client" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3495a45a28cb800c8626d2053406114bcaca26d390a4d4b183365b5bb8fdaf02" -dependencies = [ - "anyhow", - "async-compression", - "bytes", - "derive_more", - "futures", - "http", - "http-body", - "log", - "parking_lot", - "serde", - "serde_json", - "serde_urlencoded", - "url", -] - -[[package]] -name = "gpui-pre-macros" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be2db7b5097b4d523b2bce933604bcc5acdaf679bb9a150e8299e6c07efc29c" -dependencies = [ - "heck 0.5.0", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gpui-pre-perf" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1db0c046b93c2a29120f8ee4c04bc80a4d7d6117d1164ef349faface8943491" -dependencies = [ - "gpui-pre-collections", - "serde", - "serde_json", -] - -[[package]] -name = "gpui-pre-refineable" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864e2e54a3029481dae5b6aae3ba2905f2fb1dfe66ced913b68c9ff527f8e327" -dependencies = [ - "gpui-pre-derive-refineable", -] - -[[package]] -name = "gpui-pre-scheduler" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b58a78c4e0c032900704ea49922641c534cf8bbf1bf22eda6ba00a76e88c6c3" -dependencies = [ - "async-task", - "backtrace", - "chrono", - "flume", - "futures", - "parking_lot", - "rand 0.9.4", - "web-time", -] - -[[package]] -name = "gpui-pre-shared-string" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fc99fe88e44173758a500522d3f4059a6541da4b471af720e3f60cf34a2bc3" -dependencies = [ - "schemars", - "serde", - "smol_str", -] - -[[package]] -name = "gpui-pre-sum-tree" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002baed852f20cef1188d3d5e0f749025fc718e4d6cea026b9077a9a6c10d042" -dependencies = [ - "gpui-pre-ztracing", - "heapless", - "log", - "rayon", - "tracing", -] - -[[package]] -name = "gpui-pre-util" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe779c4cb00929aafcb2b307cd1240588aebb5d1eb328c59b80f77c05a41fad" -dependencies = [ - "anyhow", - "log", - "which", -] - -[[package]] -name = "gpui-pre-util-macros" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f0ccc4bccb6a31786095d15fc9f40d6a4c6a295522e3460331dd7e929ff2f79" -dependencies = [ - "gpui-pre-perf", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gpui-pre-zlog" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1416ea5f018e3a8a1c8be266332c443583f28f8f87379a84ab1e77622173ac0" -dependencies = [ - "anyhow", - "chrono", - "gpui-pre-collections", - "log", -] - -[[package]] -name = "gpui-pre-ztracing" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cc64a25a3cc4e4f1d8acb00074d3925339dae657c020083735738ba8af96bf7" -dependencies = [ - "gpui-pre-zlog", - "gpui-pre-ztracing-macro", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "gpui-pre-ztracing-macro" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3ea75672348f37d94472e579f5979ee19b744d0fa5aaadc2fe129ccafa80b" - -[[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", -] - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "hdrhistogram" -version = "7.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" -dependencies = [ - "byteorder", - "num-traits", -] - -[[package]] -name = "heapless" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" -dependencies = [ - "hash32", - "stable_deref_trait", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "http" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core 0.62.2", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" - -[[package]] -name = "icu_properties" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" - -[[package]] -name = "icu_provider" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "image" -version = "0.25.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" -dependencies = [ - "bytemuck", - "byteorder-lite", - "color_quant", - "exr", - "gif", - "image-webp", - "moxcms", - "num-traits", - "png 0.18.0", - "qoi", - "ravif", - "rayon", - "tiff", - "zune-core", - "zune-jpeg", -] - -[[package]] -name = "image-webp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" -dependencies = [ - "byteorder-lite", - "quick-error 2.0.1", -] - -[[package]] -name = "imagesize" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" - -[[package]] -name = "imgref" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", -] - -[[package]] -name = "interpolate_name" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "inventory" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" -dependencies = [ - "rustversion", -] - -[[package]] -name = "io-surface" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "554b8c5d64ec09a3a520fe58e4d48a73e00ff32899cdcbe32a4877afd4968b8e" -dependencies = [ - "cgl", - "core-foundation 0.10.0", - "core-foundation-sys", - "leaky-cow", -] - -[[package]] -name = "is-terminal" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" -dependencies = [ - "cfg-if", - "futures-util", - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "kurbo" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b60dfc32f652b926df6192e55525b16d186c69d47876c3ead4da5cc9f8450e2" -dependencies = [ - "arrayvec", - "euclid", - "polycool", - "smallvec", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leak" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd100e01f1154f2908dfa7d02219aeab25d0b9c7fa955164192e3245255a0c73" - -[[package]] -name = "leaky-cow" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a8225d44241fd324a8af2806ba635fc7c8a7e9a7de4d5cf3ef54e71f5926fc" -dependencies = [ - "leak", -] - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "lebe" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" - -[[package]] -name = "libbz2-rs-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "libfuzzer-sys" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" -dependencies = [ - "arbitrary", - "cc", -] - -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link 0.2.1", -] - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "link-section" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee1a0d6e252afe82e7bc2db42fba60e02ddf3b1accaf8cb21d96e34ba61f3d4" - -[[package]] -name = "linktime-proc-macro" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348d0075b1fc163b26d72a7f75fc5141daf2fd1bdf128d873cbaf6785d495bdf" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "litemap" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -dependencies = [ - "serde_core", - "value-bag", -] - -[[package]] -name = "loop9" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" -dependencies = [ - "imgref", -] - -[[package]] -name = "lyon" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcb7d54d54c8937364c9d41902d066656817dce1e03a44e5533afebd1ef4352" -dependencies = [ - "lyon_algorithms", - "lyon_tessellation", -] - -[[package]] -name = "lyon_algorithms" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c0829e28c4f336396f250d850c3987e16ce6db057ffe047ce0dd54aab6b647" -dependencies = [ - "lyon_path", - "num-traits", -] - -[[package]] -name = "lyon_geom" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e16770d760c7848b0c1c2d209101e408207a65168109509f8483837a36cf2e7" -dependencies = [ - "arrayvec", - "euclid", - "num-traits", -] - -[[package]] -name = "lyon_path" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aeca86bcfd632a15984ba029b539ffb811e0a70bf55e814ef8b0f54f506fdeb" -dependencies = [ - "lyon_geom", - "num-traits", -] - -[[package]] -name = "lyon_tessellation" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f586142e1280335b1bc89539f7c97dd80f08fc43e9ab1b74ef0a42b04aa353" -dependencies = [ - "float_next_after", - "lyon_path", - "num-traits", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "maybe-rayon" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" -dependencies = [ - "cfg-if", - "rayon", -] - -[[package]] -name = "memchr" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" - -[[package]] -name = "memmap2" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" -dependencies = [ - "libc", -] - -[[package]] -name = "metal" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" -dependencies = [ - "bitflags 2.13.1", - "block", - "core-graphics-types 0.2.0", - "foreign-types", - "log", - "objc", - "paste", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "moxcms" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" -dependencies = [ - "num-traits", - "pxfm", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - -[[package]] -name = "ntapi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" -dependencies = [ - "winapi", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-bigint" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[package]] -name = "objc-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[package]] -name = "objc_id" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -dependencies = [ - "objc", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "oorandom" -version = "11.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link 0.2.1", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pastey" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - -[[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "plotters" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" - -[[package]] -name = "plotters-svg" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "png" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" -dependencies = [ - "bitflags 2.13.1", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "pollster" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" - -[[package]] -name = "pollster" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" - -[[package]] -name = "polycool" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50596ddc09eb5ad5f75cacd40209568e66df71baf86e1499a0e99c4cff12a5a6" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "postage" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" -dependencies = [ - "atomic", - "crossbeam-queue", - "futures", - "log", - "parking_lot", - "pin-project", - "pollster 0.2.5", - "static_assertions", - "thiserror 1.0.69", -] - -[[package]] -name = "potential_utf" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" -dependencies = [ - "zerovec", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - -[[package]] -name = "proc-macro-crate" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" -dependencies = [ - "toml_edit 0.23.7", -] - -[[package]] -name = "proc-macro2" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "profiling" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" -dependencies = [ - "quote", - "syn 2.0.117", -] - -[[package]] -name = "proptest" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.13.1", - "num-traits", - "proptest-macro", - "rand 0.9.4", - "rand_chacha 0.9.0", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "proptest-macro" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaa288b896cb2b345da7b7f2110ab19e51565b83495b56fcec98a62f8b1f33e" -dependencies = [ - "convert_case 0.11.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pxfm" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cbdf373972bf78df4d3b518d07003938e2c7d1fb5891e55f9cb6df57009d84" -dependencies = [ - "num-traits", -] - -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quick-xml" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" -dependencies = [ - "memchr", -] - -[[package]] -name = "quote" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rand_xorshift" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" -dependencies = [ - "rand_core 0.9.3", -] - -[[package]] -name = "rav1e" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" -dependencies = [ - "aligned-vec", - "arbitrary", - "arg_enum_proc_macro", - "arrayvec", - "av-scenechange", - "av1-grain", - "bitstream-io", - "built", - "cfg-if", - "interpolate_name", - "itertools 0.14.0", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive", - "num-traits", - "paste", - "profiling", - "rand 0.9.4", - "rand_chacha 0.9.0", - "simd_helpers", - "thiserror 2.0.17", - "v_frame", - "wasm-bindgen", -] - -[[package]] -name = "ravif" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" -dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error 2.0.1", - "rav1e", - "rayon", - "rgb", -] - -[[package]] -name = "raw-window-handle" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" - -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.13.1", -] - -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" - -[[package]] -name = "resvg" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b563218631706d614e23059436526d005b50ab5f2d506b55a17eb65c5eb83419" -dependencies = [ - "gif", - "image-webp", - "log", - "pico-args", - "rgb", - "svgtypes", - "tiny-skia", - "usvg", - "zune-jpeg", -] - -[[package]] -name = "rgb" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "roxmltree" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" - -[[package]] -name = "roxmltree" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb" -dependencies = [ - "memchr", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.13.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "rusty-fork" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" -dependencies = [ - "fnv", - "quick-error 1.2.3", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "rustybuzz" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" -dependencies = [ - "bitflags 2.13.1", - "bytemuck", - "core_maths", - "log", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schemars" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" -dependencies = [ - "dyn-clone", - "indexmap", - "ref-cast", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.117", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "screencapturekit" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5eeeb57ac94960cfe5ff4c402be6585ae4c8d29a2cf41b276048c2e849d64e" -dependencies = [ - "screencapturekit-sys", -] - -[[package]] -name = "screencapturekit-sys" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22411b57f7d49e7fe08025198813ee6fd65e1ee5eff4ebc7880c12c82bde4c60" -dependencies = [ - "block", - "dispatch", - "objc", - "objc-foundation", - "objc_id", - "once_cell", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_fmt" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_json" -version = "1.0.151" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" -dependencies = [ - "indexmap", - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "simd_helpers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" -dependencies = [ - "quote", -] - -[[package]] -name = "simplecss" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" -dependencies = [ - "log", -] - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "slotmap" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" -dependencies = [ - "version_check", -] - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "smol_str" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aaa7368fcf4852a4c2dd92df0cace6a71f2091ca0a23391ce7f3a31833f1523" -dependencies = [ - "borsh", - "serde_core", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spin" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" -dependencies = [ - "lock_api", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -dependencies = [ - "float-cmp", -] - -[[package]] -name = "strum" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sval" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94c4464e595f0284970fd9c7e9013804d035d4a61ab74b113242c874c05814d" - -[[package]] -name = "sval_buffer" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0f46e34b20a39e6a2bf02b926983149b3af6609fd1ee8a6e63f6f340f3e2164" -dependencies = [ - "sval", - "sval_ref", -] - -[[package]] -name = "sval_dynamic" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d0970e53c92ab5381d3b2db1828da8af945954d4234225f6dd9c3afbcef3f5" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_fmt" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e5e6e1613e1e7fc2e1a9fdd709622e54c122ceb067a60d170d75efd491a839" -dependencies = [ - "itoa", - "ryu", - "sval", -] - -[[package]] -name = "sval_json" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aec382f7bfa6e367b23c9611f129b94eb7daaf3d8fae45a8d0a0211eb4d4c8e6" -dependencies = [ - "itoa", - "ryu", - "sval", -] - -[[package]] -name = "sval_nested" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3049d0f99ce6297f8f7d9953b35a0103b7584d8f638de40e64edb7105fa578ae" -dependencies = [ - "sval", - "sval_buffer", - "sval_ref", -] - -[[package]] -name = "sval_ref" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88913e77506085c0a8bf6912bb6558591a960faf5317df6c1d9b227224ca6e1" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_serde" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f579fd7254f4be6cd7b450034f856b78523404655848789c451bacc6aa8b387d" -dependencies = [ - "serde_core", - "sval", - "sval_nested", -] - -[[package]] -name = "svg_fmt" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" - -[[package]] -name = "svgtypes" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "695b5790b3131dafa99b3bbfd25a216edb3d216dad9ca208d4657bfb8f2abc3d" -dependencies = [ - "kurbo", - "siphasher", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sysinfo" -version = "0.31.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" -dependencies = [ - "core-foundation-sys", - "libc", - "memchr", - "ntapi", - "rayon", - "windows 0.57.0", -] - -[[package]] -name = "taffy" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c034e05f6ee85a12daa63863c2245797715075c70649947aa0da54f3f2ab1d0f" -dependencies = [ - "arrayvec", - "serde", - "slotmap", - "smallvec", -] - -[[package]] -name = "tao-core-video-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271450eb289cb4d8d0720c6ce70c72c8c858c93dd61fc625881616752e6b98f6" -dependencies = [ - "cfg-if", - "core-foundation-sys", - "libc", - "objc", -] - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.1", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl 2.0.17", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "tiff" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" -dependencies = [ - "fax", - "flate2", - "half", - "quick-error 2.0.1", - "weezl", - "zune-jpeg", -] - -[[package]] -name = "tiny-skia" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if", - "log", - "png 0.17.16", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - -[[package]] -name = "tinystr" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime 0.6.11", - "toml_edit 0.22.27", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_datetime" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime 0.6.11", - "toml_write", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" -dependencies = [ - "indexmap", - "toml_datetime 0.7.3", - "toml_parser", - "winnow", -] - -[[package]] -name = "toml_parser" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" -dependencies = [ - "winnow", -] - -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - -[[package]] -name = "tracing" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tracing-core" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "ttf-parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -dependencies = [ - "core_maths", -] - -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - -[[package]] -name = "unicode-bidi-mirroring" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" - -[[package]] -name = "unicode-ccc" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-properties" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" - -[[package]] -name = "unicode-script" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" - -[[package]] -name = "unicode-segmentation" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" - -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "url" -version = "2.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "usvg" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e419dff010bb12512b0ae9e3d2f318dfbdf0167fde7eb05465134d4e8756076f" -dependencies = [ - "base64", - "data-url", - "flate2", - "fontdb", - "imagesize", - "kurbo", - "log", - "pico-args", - "roxmltree 0.21.1", - "rustybuzz", - "simplecss", - "siphasher", - "strict-num", - "svgtypes", - "tiny-skia-path", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" -dependencies = [ - "getrandom 0.3.4", - "js-sys", - "serde", - "sha1_smol", - "wasm-bindgen", -] - -[[package]] -name = "v_frame" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "value-bag" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" -dependencies = [ - "value-bag-serde1", - "value-bag-sval2", -] - -[[package]] -name = "value-bag-serde1" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16530907bfe2999a1773ca5900a65101e092c70f642f25cc23ca0c43573262c5" -dependencies = [ - "erased-serde", - "serde_core", - "serde_fmt", -] - -[[package]] -name = "value-bag-sval2" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00ae130edd690eaa877e4f40605d534790d1cf1d651e7685bd6a144521b251f" -dependencies = [ - "sval", - "sval_buffer", - "sval_dynamic", - "sval_fmt", - "sval_json", - "sval_ref", - "sval_serde", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "wait-timeout" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" -dependencies = [ - "libc", -] - -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen 0.46.0", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.117", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.13.1", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - -[[package]] -name = "web-sys" -version = "0.3.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "weezl" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" - -[[package]] -name = "which" -version = "8.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3ef584124b911bcc3875c2f1472e80f24361ceb789bd1c62b3e9a3df9ff43c" -dependencies = [ - "libc", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" -dependencies = [ - "windows-core 0.57.0", - "windows-targets", -] - -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections 0.2.0", - "windows-core 0.61.2", - "windows-future 0.2.1", - "windows-link 0.1.3", - "windows-numerics 0.2.0", -] - -[[package]] -name = "windows" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" -dependencies = [ - "windows-collections 0.3.2", - "windows-core 0.62.2", - "windows-future 0.3.2", - "windows-numerics 0.3.1", -] - -[[package]] -name = "windows-capture" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a4df73e95feddb9ec1a7e9c2ca6323b8c97d5eeeff78d28f1eccdf19c882b24" -dependencies = [ - "parking_lot", - "rayon", - "thiserror 2.0.17", - "windows 0.61.3", - "windows-future 0.2.1", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", -] - -[[package]] -name = "windows-collections" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" -dependencies = [ - "windows-core 0.62.2", -] - -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets", -] - -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", - "windows-threading 0.1.0", -] - -[[package]] -name = "windows-future" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" -dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", - "windows-threading 0.2.1", -] - -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", -] - -[[package]] -name = "windows-numerics" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" -dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", -] - -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-threading" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck 0.5.0", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck 0.5.0", - "indexmap", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.13.1", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "writeable" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "xcb" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f07c123b796139bfe0603e654eaf08e132e52387ba95b252c78bad3640ba37ea" -dependencies = [ - "bitflags 1.3.2", - "libc", - "quick-xml", - "x11", -] - -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - -[[package]] -name = "y4m" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" - -[[package]] -name = "yoke" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zed-scap" -version = "0.0.8-zed" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b338d705ae33a43ca00287c11129303a7a0aa57b101b72a1c08c863f698ac8" -dependencies = [ - "anyhow", - "cocoa 0.25.0", - "core-graphics-helmer-fork", - "log", - "objc", - "rand 0.8.6", - "screencapturekit", - "screencapturekit-sys", - "sysinfo", - "tao-core-video-sys", - "windows 0.61.3", - "windows-capture", - "x11", - "xcb", -] - -[[package]] -name = "zerocopy" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zerotrie" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f212a141d820099d57ffafb9569be9617a6f27d3dc881fbee8fb56642f917a9" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.3", -] - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" - -[[package]] -name = "zune-core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "zune-jpeg" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" -dependencies = [ - "zune-core", -] diff --git a/crates/gpui_pre_apple/Cargo.toml b/crates/gpui_pre_apple/Cargo.toml deleted file mode 100644 index 1ff1738a..00000000 --- a/crates/gpui_pre_apple/Cargo.toml +++ /dev/null @@ -1,152 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies. -# -# If you are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2024" -name = "gpui-pre-apple" -version = "0.3.3" -build = "build.rs" -publish = true -autolib = false -autobins = false -autoexamples = false -autotests = false -autobenches = false -description = "Zed's `gpui_apple` crate (gpui-pre snapshot of zed@5b055fa)" -readme = false -license = "Apache-2.0" -repository = "https://github.com/zed-industries/zed" -resolver = "2" - -[package.metadata.gpui-pre] -zed-crate = "gpui_apple" -zed-version = "0.1.0" -zed-rev = "5b055fa789a8b8d38ac951a6e0cde272f66b4495" - -[features] -bench-support = ["gpui/bench-support"] -default = [] -runtime_shaders = [] -test-support = ["gpui/test-support"] - -[lib] -name = "gpui_apple" -path = "src/gpui_apple.rs" - -[dependencies.gpui] -version = "=0.3.3" -default-features = false -package = "gpui-pre" - -[target.'cfg(target_os = "macos")'.dependencies.anyhow] -version = "1.0.86" - -[target.'cfg(target_os = "macos")'.dependencies.block] -version = "0.1" - -[target.'cfg(target_os = "macos")'.dependencies.cocoa] -version = "=0.26.0" - -[target.'cfg(target_os = "macos")'.dependencies.collections] -version = "=0.3.3" -package = "gpui-pre-collections" - -[target.'cfg(target_os = "macos")'.dependencies.core-foundation] -version = "0.10" - -[target.'cfg(target_os = "macos")'.dependencies.core-video] -version = "0.5.2" -features = ["metal"] - -[target.'cfg(target_os = "macos")'.dependencies.derive_more] -version = "2.1.1" -features = [ - "add", - "add_assign", - "deref", - "deref_mut", - "display", - "from", - "from_str", - "mul", - "mul_assign", - "not", -] - -[target.'cfg(target_os = "macos")'.dependencies.etagere] -version = "0.2" - -[target.'cfg(target_os = "macos")'.dependencies.foreign-types] -version = "0.5" - -[target.'cfg(target_os = "macos")'.dependencies.image] -version = "0.25.1" -features = [ - "bmp", - "dds", - "exr", - "ff", - "gif", - "hdr", - "ico", - "jpeg", - "png", - "pnm", - "qoi", - "rayon", - "tga", - "tiff", - "webp", -] -default-features = false - -[target.'cfg(target_os = "macos")'.dependencies.log] -version = "0.4.16" -features = [ - "kv_unstable_serde", - "serde", -] - -[target.'cfg(target_os = "macos")'.dependencies.metal] -version = "0.33" - -[target.'cfg(target_os = "macos")'.dependencies.objc] -version = "0.2" - -[target.'cfg(target_os = "macos")'.dependencies.parking_lot] -version = "0.12.1" - -[target.'cfg(target_os = "macos")'.build-dependencies.cbindgen] -version = "0.28.0" -default-features = false - -[lints.clippy] -dbg_macro = "deny" -declare_interior_mutable_const = "deny" -disallowed_methods = "deny" -large_enum_variant = "allow" -let_underscore_future = "allow" -nonminimal_bool = "allow" -redundant_clone = "deny" -single_range_in_vec_init = "allow" -todo = "deny" -too_many_arguments = "allow" -type_complexity = "allow" - -[lints.clippy.style] -level = "allow" -priority = -1 - -[lints.rust.unexpected_cfgs] -level = "allow" -priority = 0 - -[workspace] diff --git a/crates/gpui_pre_apple/LICENSE-APACHE b/crates/gpui_pre_apple/LICENSE-APACHE deleted file mode 100644 index 461a0fe5..00000000 --- a/crates/gpui_pre_apple/LICENSE-APACHE +++ /dev/null @@ -1,222 +0,0 @@ -Copyright 2022 - 2025 Zed Industries, Inc. - - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - - http://www.apache.org/licenses/LICENSE-2.0 - - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - - -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - - 1. Definitions. - - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - - END OF TERMS AND CONDITIONS diff --git a/crates/gpui_pre_apple/build.rs b/crates/gpui_pre_apple/build.rs deleted file mode 100644 index c8f0db8b..00000000 --- a/crates/gpui_pre_apple/build.rs +++ /dev/null @@ -1,190 +0,0 @@ -// Modified for gpui-pre (snapshot of zed@5b055fa): the gpui sources it reads are vendored under `vendor/gpui`. -#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")] - -fn main() { - #[cfg(target_os = "macos")] - macos_build::run(); -} - -#[cfg(target_os = "macos")] -mod macos_build { - use std::{ - env, - path::{Path, PathBuf}, - }; - - use cbindgen::Config; - - pub fn run() { - let header_path = generate_shader_bindings(); - - #[cfg(feature = "runtime_shaders")] - emit_stitched_shaders(&header_path); - #[cfg(not(feature = "runtime_shaders"))] - compile_metal_shaders(&header_path); - } - - fn generate_shader_bindings() -> PathBuf { - let output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("scene.h"); - - let gpui_dir = find_gpui_crate_dir(); - - let mut config = Config { - include_guard: Some("SCENE_H".into()), - language: cbindgen::Language::C, - no_includes: true, - ..Default::default() - }; - config.export.include.extend([ - "Bounds".into(), - "Corners".into(), - "Edges".into(), - "Size".into(), - "Pixels".into(), - "PointF".into(), - "Hsla".into(), - "ContentMask".into(), - "RoundedClip_ScaledPixels".into(), - "Uniforms".into(), - "AtlasTile".into(), - "PathRasterizationInputIndex".into(), - "PathVertex_ScaledPixels".into(), - "PathRasterizationVertex".into(), - "ShadowInputIndex".into(), - "Shadow".into(), - "QuadInputIndex".into(), - "Underline".into(), - "UnderlineInputIndex".into(), - "Quad".into(), - "BorderStyle".into(), - "SpriteInputIndex".into(), - "MonochromeSprite".into(), - "PolychromeSprite".into(), - "PathSprite".into(), - "SurfaceInputIndex".into(), - "SurfaceBounds".into(), - "TransformationMatrix".into(), - ]); - config.no_includes = true; - config.enumeration.prefix_with_name = true; - - let mut builder = cbindgen::Builder::new(); - - let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - - // Source files from gpui that define types used in shaders - let gpui_src_paths = [ - gpui_dir.join("src/scene.rs"), - gpui_dir.join("src/clip.rs"), - gpui_dir.join("src/geometry.rs"), - gpui_dir.join("src/color.rs"), - gpui_dir.join("src/window.rs"), - gpui_dir.join("src/platform.rs"), - ]; - - // Source files from this crate - let local_src_paths = [crate_dir.join("src/metal_renderer.rs")]; - - for src_path in gpui_src_paths.iter().chain(local_src_paths.iter()) { - println!("cargo:rerun-if-changed={}", src_path.display()); - builder = builder.with_src(src_path); - } - - builder - .with_config(config) - .generate() - .expect("Unable to generate bindings") - .write_to_file(&output_path); - - output_path - } - - /// Locate the gpui crate directory relative to this crate. Resolved at - /// build-script runtime against this crate's manifest dir, so no checkout - /// path is baked into a compiled artifact (which corgi rejects). - fn find_gpui_crate_dir() -> PathBuf { - PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("vendor/gpui") - } - - /// To enable runtime compilation, we need to "stitch" the shaders file with the generated header - /// so that it is self-contained. - #[cfg(feature = "runtime_shaders")] - fn emit_stitched_shaders(header_path: &Path) { - fn stitch_header(header: &Path, shader_path: &Path) -> std::io::Result { - let header_contents = std::fs::read_to_string(header)?; - let shader_contents = std::fs::read_to_string(shader_path)?; - let stitched_contents = format!("{header_contents}\n{shader_contents}"); - let out_path = - PathBuf::from(env::var("OUT_DIR").unwrap()).join("stitched_shaders.metal"); - std::fs::write(&out_path, stitched_contents)?; - Ok(out_path) - } - let shader_source_path = "./src/shaders.metal"; - let shader_path = PathBuf::from(shader_source_path); - stitch_header(header_path, &shader_path).unwrap(); - println!("cargo:rerun-if-changed={shader_source_path}"); - } - - #[cfg(not(feature = "runtime_shaders"))] - fn compile_metal_shaders(header_path: &Path) { - use std::process::{self, Command}; - let shader_path = "./src/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_path); - - // The metal compiler records the resolved absolute path of its input - // unconditionally. Compile a copy staged in OUT_DIR so the recorded - // location is the build's canonical output directory, never the - // checkout (corgi rejects artifacts that embed the build path). - let staged_shader_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.metal"); - std::fs::copy(shader_path, &staged_shader_path).unwrap(); - - let output = Command::new("xcrun") - .args([ - "-sdk", - "macosx", - "metal", - "-gline-tables-only", - "-mmacosx-version-min=10.15.7", - "-MO", - "-c", - ]) - .arg(&staged_shader_path) - .args(["-include", header_path.to_str().unwrap(), "-o"]) - .arg(&air_output_path) - .output() - .unwrap(); - - if !output.status.success() { - println!( - "cargo::error=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() { - println!( - "cargo::error=metallib compilation failed:\n{}", - String::from_utf8_lossy(&output.stderr) - ); - process::exit(1); - } - - // The .air intermediate records the compiler's working directory in - // its debug info; the metallib built from it does not. Nothing reads - // the .air after this point, so drop it rather than leave a - // checkout-path-bearing file in OUT_DIR. - std::fs::remove_file(&air_output_path).unwrap(); - } -} diff --git a/crates/gpui_pre_apple/src/gpui_apple.rs b/crates/gpui_pre_apple/src/gpui_apple.rs deleted file mode 100644 index 208d297e..00000000 --- a/crates/gpui_pre_apple/src/gpui_apple.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![cfg(target_os = "macos")] -//! Shared Apple platform support for GPUI. -//! -//! This crate contains the Metal renderer and GPU resource management shared -//! by GPUI's Apple platform backends. - -mod metal_atlas; -pub mod metal_renderer; diff --git a/crates/gpui_pre_apple/src/metal_atlas.rs b/crates/gpui_pre_apple/src/metal_atlas.rs deleted file mode 100644 index 9cfd445b..00000000 --- a/crates/gpui_pre_apple/src/metal_atlas.rs +++ /dev/null @@ -1,379 +0,0 @@ -use anyhow::{Context as _, Result}; -use collections::FxHashMap; -use derive_more::{Deref, DerefMut}; -use etagere::BucketedAtlasAllocator; -use gpui::{ - AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTextureList, AtlasTile, Bounds, DevicePixels, - PlatformAtlas, Point, Size, -}; -use metal::Device; -use parking_lot::Mutex; -use std::borrow::Cow; - -pub struct MetalAtlas(Mutex); - -impl MetalAtlas { - pub(crate) fn new(device: Device, is_apple_gpu: bool) -> Self { - MetalAtlas(Mutex::new(MetalAtlasState { - device: AssertSend(device), - is_apple_gpu, - monochrome_textures: Default::default(), - polychrome_textures: Default::default(), - tiles_by_key: Default::default(), - })) - } - - pub(crate) fn metal_texture(&self, id: AtlasTextureId) -> metal::Texture { - self.0.lock().texture(id).metal_texture.clone() - } -} - -struct MetalAtlasState { - device: AssertSend, - is_apple_gpu: bool, - monochrome_textures: AtlasTextureList, - polychrome_textures: AtlasTextureList, - tiles_by_key: FxHashMap, -} - -impl PlatformAtlas for MetalAtlas { - fn get_or_insert_with<'a>( - &self, - key: &AtlasKey, - build: &mut dyn FnMut() -> Result, Cow<'a, [u8]>)>>, - ) -> Result> { - let mut lock = self.0.lock(); - if let Some(tile) = lock.tiles_by_key.get(key) { - Ok(Some(*tile)) - } else { - let Some((size, bytes)) = build()? else { - return Ok(None); - }; - let tile = lock - .allocate(size, key.texture_kind()) - .context("failed to allocate")?; - let texture = lock.texture(tile.texture_id); - texture.upload(tile.bounds, &bytes); - lock.tiles_by_key.insert(key.clone(), tile); - Ok(Some(tile)) - } - } - - fn remove(&self, key: &AtlasKey) { - let mut lock = self.0.lock(); - let Some(tile) = lock.tiles_by_key.remove(key) else { - return; - }; - let id = tile.texture_id; - - let textures = match id.kind { - AtlasTextureKind::Monochrome => &mut lock.monochrome_textures, - AtlasTextureKind::Polychrome => &mut lock.polychrome_textures, - AtlasTextureKind::Subpixel => unreachable!(), - }; - - let Some(texture_slot) = textures - .textures - .iter_mut() - .find(|texture| texture.as_ref().is_some_and(|v| v.id == id)) - else { - return; - }; - - if let Some(mut texture) = texture_slot.take() { - texture.allocator.deallocate(tile.tile_id.into()); - texture.decrement_ref_count(); - if texture.is_unreferenced() { - textures.free_list.push(id.index as usize); - } else { - *texture_slot = Some(texture); - } - } - } -} - -impl MetalAtlasState { - fn allocate( - &mut self, - size: Size, - texture_kind: AtlasTextureKind, - ) -> Option { - { - let textures = match texture_kind { - AtlasTextureKind::Monochrome => &mut self.monochrome_textures, - AtlasTextureKind::Polychrome => &mut self.polychrome_textures, - AtlasTextureKind::Subpixel => unreachable!(), - }; - - if let Some(tile) = textures - .iter_mut() - .rev() - .find_map(|texture| texture.allocate(size)) - { - return Some(tile); - } - } - - let texture = self.push_texture(size, texture_kind); - texture.allocate(size) - } - - fn push_texture( - &mut self, - min_size: Size, - kind: AtlasTextureKind, - ) -> &mut MetalAtlasTexture { - const DEFAULT_ATLAS_SIZE: Size = Size { - width: DevicePixels(1024), - height: DevicePixels(1024), - }; - // Max texture size on all modern Apple GPUs. Anything bigger than that crashes in validateWithDevice. - const MAX_ATLAS_SIZE: Size = Size { - width: DevicePixels(16384), - height: DevicePixels(16384), - }; - let size = min_size.min(&MAX_ATLAS_SIZE).max(&DEFAULT_ATLAS_SIZE); - let texture_descriptor = metal::TextureDescriptor::new(); - texture_descriptor.set_width(size.width.into()); - texture_descriptor.set_height(size.height.into()); - let pixel_format; - let usage; - match kind { - AtlasTextureKind::Monochrome => { - pixel_format = metal::MTLPixelFormat::A8Unorm; - usage = metal::MTLTextureUsage::ShaderRead; - } - AtlasTextureKind::Polychrome => { - pixel_format = metal::MTLPixelFormat::BGRA8Unorm; - usage = metal::MTLTextureUsage::ShaderRead; - } - AtlasTextureKind::Subpixel => unreachable!(), - } - texture_descriptor.set_pixel_format(pixel_format); - texture_descriptor.set_usage(usage); - // Shared memory mode can be used only on Apple GPU families - // https://developer.apple.com/documentation/metal/mtlresourceoptions/storagemodeshared - texture_descriptor.set_storage_mode(if self.is_apple_gpu { - metal::MTLStorageMode::Shared - } else { - metal::MTLStorageMode::Managed - }); - let metal_texture = self.device.new_texture(&texture_descriptor); - - let texture_list = match kind { - AtlasTextureKind::Monochrome => &mut self.monochrome_textures, - AtlasTextureKind::Polychrome => &mut self.polychrome_textures, - AtlasTextureKind::Subpixel => unreachable!(), - }; - - let index = texture_list.free_list.pop(); - - let atlas_texture = MetalAtlasTexture { - id: AtlasTextureId { - index: index.unwrap_or(texture_list.textures.len()) as u32, - kind, - }, - allocator: etagere::BucketedAtlasAllocator::new(size_to_etagere(size)), - metal_texture: AssertSend(metal_texture), - live_atlas_keys: 0, - }; - - if let Some(ix) = index { - texture_list.textures[ix] = Some(atlas_texture); - texture_list.textures.get_mut(ix) - } else { - texture_list.textures.push(Some(atlas_texture)); - texture_list.textures.last_mut() - } - .unwrap() - .as_mut() - .unwrap() - } - - fn texture(&self, id: AtlasTextureId) -> &MetalAtlasTexture { - let textures = match id.kind { - AtlasTextureKind::Monochrome => &self.monochrome_textures, - AtlasTextureKind::Polychrome => &self.polychrome_textures, - AtlasTextureKind::Subpixel => unreachable!(), - }; - textures[id.index as usize].as_ref().unwrap() - } -} - -struct MetalAtlasTexture { - id: AtlasTextureId, - allocator: BucketedAtlasAllocator, - metal_texture: AssertSend, - live_atlas_keys: u32, -} - -impl MetalAtlasTexture { - fn allocate(&mut self, size: Size) -> Option { - let allocation = self.allocator.allocate(size_to_etagere(size))?; - let tile = AtlasTile { - texture_id: self.id, - tile_id: allocation.id.into(), - bounds: Bounds { - origin: point_from_etagere(allocation.rectangle.min), - size, - }, - padding: 0, - }; - self.live_atlas_keys += 1; - Some(tile) - } - - fn upload(&self, bounds: Bounds, bytes: &[u8]) { - let region = metal::MTLRegion::new_2d( - bounds.origin.x.into(), - bounds.origin.y.into(), - bounds.size.width.into(), - bounds.size.height.into(), - ); - self.metal_texture.replace_region( - region, - 0, - bytes.as_ptr() as *const _, - bounds.size.width.to_bytes(self.bytes_per_pixel()) as u64, - ); - } - - fn bytes_per_pixel(&self) -> u8 { - use metal::MTLPixelFormat::*; - match self.metal_texture.pixel_format() { - A8Unorm | R8Unorm => 1, - RGBA8Unorm | BGRA8Unorm => 4, - _ => unimplemented!(), - } - } - - fn decrement_ref_count(&mut self) { - self.live_atlas_keys -= 1; - } - - fn is_unreferenced(&mut self) -> bool { - self.live_atlas_keys == 0 - } -} - -fn size_to_etagere(size: Size) -> etagere::Size { - etagere::Size::new(size.width.into(), size.height.into()) -} - -fn point_from_etagere(value: etagere::Point) -> Point { - Point { - x: DevicePixels::from(value.x), - y: DevicePixels::from(value.y), - } -} - -#[derive(Deref, DerefMut)] -struct AssertSend(T); - -unsafe impl Send for AssertSend {} - -#[cfg(test)] -mod tests { - use super::*; - use gpui::PlatformAtlas; - use std::borrow::Cow; - - fn create_atlas() -> Option { - let device = metal::Device::system_default()?; - Some(MetalAtlas::new(device, true)) - } - - fn make_image_key(image_id: usize, frame_index: usize) -> AtlasKey { - AtlasKey::Image(gpui::RenderImageParams { - image_id: gpui::ImageId(image_id), - frame_index, - }) - } - - fn insert_tile(atlas: &MetalAtlas, key: &AtlasKey, size: Size) -> AtlasTile { - atlas - .get_or_insert_with(key, &mut || { - let byte_count = (size.width.0 as usize) * (size.height.0 as usize) * 4; - Ok(Some((size, Cow::Owned(vec![0u8; byte_count])))) - }) - .expect("allocation should succeed") - .expect("callback returns Some") - } - - #[test] - fn test_remove_clears_stale_keys_from_tiles_by_key() { - let Some(atlas) = create_atlas() else { - return; - }; - - let small = Size { - width: DevicePixels(64), - height: DevicePixels(64), - }; - - let key_a = make_image_key(1, 0); - let key_b = make_image_key(2, 0); - let key_c = make_image_key(3, 0); - - let tile_a = insert_tile(&atlas, &key_a, small); - let tile_b = insert_tile(&atlas, &key_b, small); - let tile_c = insert_tile(&atlas, &key_c, small); - - assert_eq!(tile_a.texture_id, tile_b.texture_id); - assert_eq!(tile_b.texture_id, tile_c.texture_id); - - // Remove A: texture still has B and C, so it stays. - // The key for A must be removed from tiles_by_key. - atlas.remove(&key_a); - - // Remove B: texture still has C. - atlas.remove(&key_b); - - // Remove C: texture becomes unreferenced and is deleted. - atlas.remove(&key_c); - - // Re-inserting A must allocate a fresh tile on a new texture, - // NOT return a stale tile referencing the deleted texture. - let tile_a2 = insert_tile(&atlas, &key_a, small); - - // The texture must actually exist — this would panic before the fix. - let _texture = atlas.metal_texture(tile_a2.texture_id); - } - - #[test] - fn test_remove_deallocates_tile_space_for_reuse() { - let Some(atlas) = create_atlas() else { - return; - }; - - let small = Size { - width: DevicePixels(64), - height: DevicePixels(64), - }; - let big = Size { - width: DevicePixels(700), - height: DevicePixels(700), - }; - - let keeper_key = make_image_key(1, 0); - let big_key_a = make_image_key(2, 0); - let big_key_b = make_image_key(3, 0); - - let keeper_tile = insert_tile(&atlas, &keeper_key, small); - let tile_a = insert_tile(&atlas, &big_key_a, big); - assert_eq!(keeper_tile.texture_id, tile_a.texture_id); - - atlas.remove(&big_key_a); - let tile_b = insert_tile(&atlas, &big_key_b, big); - assert_eq!(tile_b.texture_id, keeper_tile.texture_id); - } - - #[test] - fn test_remove_nonexistent_key_is_noop() { - let Some(atlas) = create_atlas() else { - return; - }; - let key = make_image_key(999, 0); - atlas.remove(&key); - } -} diff --git a/crates/gpui_pre_apple/src/metal_renderer.rs b/crates/gpui_pre_apple/src/metal_renderer.rs deleted file mode 100644 index c021ed58..00000000 --- a/crates/gpui_pre_apple/src/metal_renderer.rs +++ /dev/null @@ -1,1649 +0,0 @@ -use crate::metal_atlas::MetalAtlas; -use anyhow::{Context as _, Result}; -use block::ConcreteBlock; -use cocoa::{ - base::{NO, YES}, - foundation::{NSSize, NSUInteger}, - quartzcore::AutoresizingMask, -}; -use gpui::{ - point, size, AtlasTextureId, Background, Bounds, ContentMask, DevicePixels, PaintSurface, Path, - Point, PrimitiveBatch, ScaledPixels, Scene, Size, -}; -#[cfg(any(test, feature = "bench-support", feature = "test-support"))] -use image::RgbaImage; - -use core_foundation::base::TCFType; -use core_video::{ - metal_texture::CVMetalTextureGetTexture, metal_texture_cache::CVMetalTextureCache, - pixel_buffer::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, -}; -use foreign_types::{ForeignType, ForeignTypeRef}; -use metal::{ - CAMetalLayer, CommandQueue, MTLGPUFamily, MTLPixelFormat, MTLResourceOptions, NSRange, -}; -use objc::{self, msg_send, sel, sel_impl}; -use parking_lot::Mutex; - -use std::{cell::Cell, ffi::c_void, mem, mem::MaybeUninit, ops::Range, ptr, slice, sync::Arc}; - -// Exported to metal -pub(crate) type PointF = gpui::Point; - -#[cfg(not(feature = "runtime_shaders"))] -const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib")); -#[cfg(feature = "runtime_shaders")] -const SHADERS_SOURCE_FILE: &str = include_str!(concat!(env!("OUT_DIR"), "/stitched_shaders.metal")); -// Use 4x MSAA, all devices support it. -// https://developer.apple.com/documentation/metal/mtldevice/1433355-supportstexturesamplecount -const PATH_SAMPLE_COUNT: u32 = 4; -/// Metal requires the offset a buffer is bound at to be 256-byte aligned. -const INSTANCE_BUFFER_ALIGNMENT: usize = 256; -const MAX_INSTANCE_BUFFER_SIZE: usize = 256 * 1024 * 1024; - -pub type Context = Arc>; -pub type Renderer = MetalRenderer; - -pub unsafe fn new_renderer( - context: self::Context, - _native_window: *mut c_void, - _native_view: *mut c_void, - _bounds: gpui::Size, - transparent: bool, -) -> Renderer { - MetalRenderer::new(context, transparent) -} - -pub struct InstanceBufferPool { - buffer_size: usize, - buffers: Vec, -} - -impl Default for InstanceBufferPool { - fn default() -> Self { - Self { - buffer_size: 2 * 1024 * 1024, - buffers: Vec::new(), - } - } -} - -pub(crate) struct InstanceBuffer { - metal_buffer: metal::Buffer, - size: usize, -} - -impl InstanceBufferPool { - pub(crate) fn reset(&mut self, buffer_size: usize) { - self.buffer_size = buffer_size; - self.buffers.clear(); - } - - pub(crate) fn acquire( - &mut self, - device: &metal::Device, - unified_memory: bool, - ) -> InstanceBuffer { - let buffer = self.buffers.pop().unwrap_or_else(|| { - let options = if unified_memory { - MTLResourceOptions::StorageModeShared - // Buffers are write only which can benefit from the combined cache - // https://developer.apple.com/documentation/metal/mtlresourceoptions/cpucachemodewritecombined - | MTLResourceOptions::CPUCacheModeWriteCombined - } else { - MTLResourceOptions::StorageModeManaged - }; - - device.new_buffer(self.buffer_size as u64, options) - }); - InstanceBuffer { - metal_buffer: buffer, - size: self.buffer_size, - } - } - - pub(crate) fn release(&mut self, buffer: InstanceBuffer) { - if buffer.size == self.buffer_size { - self.buffers.push(buffer.metal_buffer) - } - } -} - -pub struct MetalRenderer { - device: metal::Device, - layer: Option, - is_apple_gpu: bool, - is_unified_memory: bool, - presents_with_transaction: bool, - /// For headless rendering, tracks whether output should be opaque - opaque: bool, - command_queue: CommandQueue, - paths_rasterization_pipeline_state: metal::RenderPipelineState, - path_sprites_pipeline_state: metal::RenderPipelineState, - shadows_pipeline_state: metal::RenderPipelineState, - quads_pipeline_state: metal::RenderPipelineState, - underlines_pipeline_state: metal::RenderPipelineState, - monochrome_sprites_pipeline_state: metal::RenderPipelineState, - polychrome_sprites_pipeline_state: metal::RenderPipelineState, - surfaces_pipeline_state: metal::RenderPipelineState, - unit_vertices: metal::Buffer, - #[allow(clippy::arc_with_non_send_sync)] - instance_buffer_pool: Arc>, - sprite_atlas: Arc, - core_video_texture_cache: core_video::metal_texture_cache::CVMetalTextureCache, - path_intermediate_texture: Option, - path_intermediate_msaa_texture: Option, - path_sample_count: u32, - /// Offscreen render target reused across `render_scene` calls when - /// rendering headlessly without reading pixels back. - #[cfg(any(test, feature = "bench-support", feature = "test-support"))] - headless_render_target: Option, -} - -#[repr(C)] -pub struct PathRasterizationVertex { - pub xy_position: Point, - pub st_position: Point, - pub color: Background, - pub bounds: Bounds, - pub content_mask: ContentMask, -} - -impl MetalRenderer { - /// Creates a new MetalRenderer with a CAMetalLayer for window-based rendering. - pub fn new(instance_buffer_pool: Arc>, transparent: bool) -> Self { - let device = Self::create_device(); - - let layer = metal::MetalLayer::new(); - layer.set_device(&device); - layer.set_pixel_format(MTLPixelFormat::BGRA8Unorm); - // Support direct-to-display rendering if the window is not transparent - // https://developer.apple.com/documentation/metal/managing-your-game-window-for-metal-in-macos - layer.set_opaque(!transparent); - layer.set_maximum_drawable_count(3); - // Allow texture reading for visual tests (captures screenshots without ScreenCaptureKit) - #[cfg(any(test, feature = "test-support"))] - layer.set_framebuffer_only(false); - unsafe { - let _: () = msg_send![&*layer, setAllowsNextDrawableTimeout: NO]; - let _: () = msg_send![&*layer, setNeedsDisplayOnBoundsChange: YES]; - let _: () = msg_send![ - &*layer, - setAutoresizingMask: AutoresizingMask::WIDTH_SIZABLE - | AutoresizingMask::HEIGHT_SIZABLE - ]; - } - - Self::new_internal(device, Some(layer), !transparent, instance_buffer_pool) - } - - /// Creates a new headless MetalRenderer for offscreen rendering without a window. - /// - /// This renderer can render scenes to images without requiring a CAMetalLayer, - /// window, or AppKit. Use `render_scene_to_image()` to render scenes. - #[cfg(any(test, feature = "bench-support", feature = "test-support"))] - pub fn new_headless(instance_buffer_pool: Arc>) -> Self { - let device = Self::create_device(); - Self::new_internal(device, None, true, instance_buffer_pool) - } - - fn create_device() -> metal::Device { - // Prefer low‐power integrated GPUs on Intel Mac. On Apple - // Silicon, there is only ever one GPU, so this is equivalent to - // `metal::Device::system_default()`. - if let Some(d) = metal::Device::all() - .into_iter() - .min_by_key(|d| (d.is_removable(), !d.is_low_power())) - { - d - } else { - // For some reason `all()` can return an empty list, see https://github.com/zed-industries/zed/issues/37689 - // In that case, we fall back to the system default device. - log::error!( - "Unable to enumerate Metal devices; attempting to use system default device" - ); - metal::Device::system_default().unwrap_or_else(|| { - log::error!("unable to access a compatible graphics device"); - std::process::exit(1); - }) - } - } - - fn new_internal( - device: metal::Device, - layer: Option, - opaque: bool, - instance_buffer_pool: Arc>, - ) -> Self { - #[cfg(feature = "runtime_shaders")] - let library = device - .new_library_with_source(&SHADERS_SOURCE_FILE, &metal::CompileOptions::new()) - .expect("error building metal library"); - #[cfg(not(feature = "runtime_shaders"))] - let library = device - .new_library_with_data(SHADERS_METALLIB) - .expect("error building metal library"); - - fn to_float2_bits(point: PointF) -> u64 { - let mut output = point.y.to_bits() as u64; - output <<= 32; - output |= point.x.to_bits() as u64; - output - } - - // Shared memory can be used only if CPU and GPU share the same memory space. - // https://developer.apple.com/documentation/metal/setting-resource-storage-modes - let is_unified_memory = device.has_unified_memory(); - // Apple GPU families support memoryless textures, which can significantly reduce - // memory usage by keeping render targets in on-chip tile memory instead of - // allocating backing store in system memory. - // https://developer.apple.com/documentation/metal/mtlgpufamily - let is_apple_gpu = device.supports_family(MTLGPUFamily::Apple1); - - let unit_vertices = [ - to_float2_bits(point(0., 0.)), - to_float2_bits(point(1., 0.)), - to_float2_bits(point(0., 1.)), - to_float2_bits(point(0., 1.)), - to_float2_bits(point(1., 0.)), - to_float2_bits(point(1., 1.)), - ]; - let unit_vertices = device.new_buffer_with_data( - unit_vertices.as_ptr() as *const c_void, - mem::size_of_val(&unit_vertices) as u64, - if is_unified_memory { - MTLResourceOptions::StorageModeShared - | MTLResourceOptions::CPUCacheModeWriteCombined - } else { - MTLResourceOptions::StorageModeManaged - }, - ); - - let paths_rasterization_pipeline_state = build_path_rasterization_pipeline_state( - &device, - &library, - "paths_rasterization", - "path_rasterization_vertex", - "path_rasterization_fragment", - MTLPixelFormat::BGRA8Unorm, - PATH_SAMPLE_COUNT, - ); - let path_sprites_pipeline_state = build_path_sprite_pipeline_state( - &device, - &library, - "path_sprites", - "path_sprite_vertex", - "path_sprite_fragment", - MTLPixelFormat::BGRA8Unorm, - ); - let shadows_pipeline_state = build_pipeline_state( - &device, - &library, - "shadows", - "shadow_vertex", - "shadow_fragment", - MTLPixelFormat::BGRA8Unorm, - ); - let quads_pipeline_state = build_pipeline_state( - &device, - &library, - "quads", - "quad_vertex", - "quad_fragment", - MTLPixelFormat::BGRA8Unorm, - ); - let underlines_pipeline_state = build_pipeline_state( - &device, - &library, - "underlines", - "underline_vertex", - "underline_fragment", - MTLPixelFormat::BGRA8Unorm, - ); - let monochrome_sprites_pipeline_state = build_pipeline_state( - &device, - &library, - "monochrome_sprites", - "monochrome_sprite_vertex", - "monochrome_sprite_fragment", - MTLPixelFormat::BGRA8Unorm, - ); - let polychrome_sprites_pipeline_state = build_pipeline_state( - &device, - &library, - "polychrome_sprites", - "polychrome_sprite_vertex", - "polychrome_sprite_fragment", - MTLPixelFormat::BGRA8Unorm, - ); - let surfaces_pipeline_state = build_pipeline_state( - &device, - &library, - "surfaces", - "surface_vertex", - "surface_fragment", - MTLPixelFormat::BGRA8Unorm, - ); - - let command_queue = device.new_command_queue(); - let sprite_atlas = Arc::new(MetalAtlas::new(device.clone(), is_apple_gpu)); - let core_video_texture_cache = - CVMetalTextureCache::new(None, device.clone(), None).unwrap(); - - Self { - device, - layer, - presents_with_transaction: false, - is_apple_gpu, - is_unified_memory, - opaque, - command_queue, - paths_rasterization_pipeline_state, - path_sprites_pipeline_state, - shadows_pipeline_state, - quads_pipeline_state, - underlines_pipeline_state, - monochrome_sprites_pipeline_state, - polychrome_sprites_pipeline_state, - surfaces_pipeline_state, - unit_vertices, - instance_buffer_pool, - sprite_atlas, - core_video_texture_cache, - path_intermediate_texture: None, - path_intermediate_msaa_texture: None, - path_sample_count: PATH_SAMPLE_COUNT, - #[cfg(any(test, feature = "bench-support", feature = "test-support"))] - headless_render_target: None, - } - } - - pub fn layer(&self) -> Option<&metal::MetalLayerRef> { - self.layer.as_ref().map(|l| l.as_ref()) - } - - pub fn layer_ptr(&self) -> *mut CAMetalLayer { - self.layer - .as_ref() - .map(|l| l.as_ptr()) - .unwrap_or(ptr::null_mut()) - } - - pub fn sprite_atlas(&self) -> &Arc { - &self.sprite_atlas - } - - pub fn set_presents_with_transaction(&mut self, presents_with_transaction: bool) { - self.presents_with_transaction = presents_with_transaction; - if let Some(layer) = &self.layer { - layer.set_presents_with_transaction(presents_with_transaction); - } - } - - pub fn update_drawable_size(&mut self, size: Size) { - if let Some(layer) = &self.layer { - let ns_size = NSSize { - width: size.width.0 as f64, - height: size.height.0 as f64, - }; - unsafe { - let _: () = msg_send![ - layer.as_ref(), - setDrawableSize: ns_size - ]; - } - } - self.update_path_intermediate_textures(size); - } - - fn update_path_intermediate_textures(&mut self, size: Size) { - // We are uncertain when this happens, but sometimes size can be 0 here. Most likely before - // the layout pass on window creation. Zero-sized texture creation causes SIGABRT. - // https://github.com/zed-industries/zed/issues/36229 - if size.width.0 <= 0 || size.height.0 <= 0 { - self.path_intermediate_texture = None; - self.path_intermediate_msaa_texture = None; - return; - } - - let texture_descriptor = metal::TextureDescriptor::new(); - texture_descriptor.set_width(size.width.0 as u64); - texture_descriptor.set_height(size.height.0 as u64); - texture_descriptor.set_pixel_format(metal::MTLPixelFormat::BGRA8Unorm); - texture_descriptor.set_storage_mode(metal::MTLStorageMode::Private); - texture_descriptor - .set_usage(metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead); - self.path_intermediate_texture = Some(self.device.new_texture(&texture_descriptor)); - - if self.path_sample_count > 1 { - // https://developer.apple.com/documentation/metal/choosing-a-resource-storage-mode-for-apple-gpus - // Rendering MSAA textures are done in a single pass, so we can use memory-less storage on Apple Silicon - let storage_mode = if self.is_apple_gpu { - metal::MTLStorageMode::Memoryless - } else { - metal::MTLStorageMode::Private - }; - - let msaa_descriptor = texture_descriptor; - msaa_descriptor.set_texture_type(metal::MTLTextureType::D2Multisample); - msaa_descriptor.set_storage_mode(storage_mode); - msaa_descriptor.set_sample_count(self.path_sample_count as _); - self.path_intermediate_msaa_texture = Some(self.device.new_texture(&msaa_descriptor)); - } else { - self.path_intermediate_msaa_texture = None; - } - } - - pub fn update_transparency(&mut self, transparent: bool) { - self.opaque = !transparent; - if let Some(layer) = &self.layer { - layer.set_opaque(!transparent); - } - } - - pub fn destroy(&self) { - // nothing to do - } - - pub fn draw(&mut self, scene: &Scene) { - let layer = match &self.layer { - Some(l) => l.clone(), - None => { - log::error!( - "draw() called on headless renderer - use render_scene_to_image() instead" - ); - return; - } - }; - let viewport_size = layer.drawable_size(); - let viewport_size: Size = size( - (viewport_size.width.ceil() as i32).into(), - (viewport_size.height.ceil() as i32).into(), - ); - let drawable = if let Some(drawable) = layer.next_drawable() { - drawable - } else { - log::error!( - "failed to retrieve next drawable, drawable size: {:?}", - viewport_size - ); - return; - }; - - let command_buffer = match self.render_frame(scene, drawable.texture(), viewport_size) { - Ok(command_buffer) => command_buffer, - Err(error) => { - log::error!("failed to render: {error:#}"); - return; - } - }; - - if self.presents_with_transaction { - command_buffer.commit(); - command_buffer.wait_until_scheduled(); - drawable.present(); - } else { - command_buffer.present_drawable(drawable); - command_buffer.commit(); - } - } - - fn render_frame( - &mut self, - scene: &Scene, - texture: &metal::TextureRef, - viewport_size: Size, - ) -> Result { - let mut writer = InstanceBufferWriter::new( - &self.device, - &self.instance_buffer_pool, - self.is_unified_memory, - ); - let instance_bindings = write_instances(scene, &mut writer).with_context(|| { - format!( - "scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces", - scene.paths.len(), - scene.shadows.len(), - scene.quads.len(), - scene.underlines.len(), - scene.monochrome_sprites.len(), - scene.polychrome_sprites.len(), - scene.surfaces.len(), - ) - })?; - let command_buffer = self.draw_primitives_to_texture( - scene, - &instance_bindings, - &mut writer, - texture, - viewport_size, - )?; - - let instance_buffer_pool = self.instance_buffer_pool.clone(); - let instance_buffer = Cell::new(Some(writer.finish())); - let block = ConcreteBlock::new(move |_| { - if let Some(instance_buffer) = instance_buffer.take() { - instance_buffer_pool.lock().release(instance_buffer); - } - }); - let block = block.copy(); - command_buffer.add_completed_handler(&block); - - Ok(command_buffer) - } - - /// Renders the scene to a texture and returns the pixel data as an RGBA image. - /// This does not present the frame to screen - useful for visual testing - /// where we want to capture what would be rendered without displaying it. - /// - /// Note: This requires a layer-backed renderer. For headless rendering, - /// use `render_scene_to_image()` instead. - #[cfg(any(test, feature = "test-support"))] - pub fn render_to_image(&mut self, scene: &Scene) -> Result { - let layer = self - .layer - .clone() - .ok_or_else(|| anyhow::anyhow!("render_to_image requires a layer-backed renderer"))?; - let viewport_size = layer.drawable_size(); - let viewport_size: Size = size( - (viewport_size.width.ceil() as i32).into(), - (viewport_size.height.ceil() as i32).into(), - ); - let drawable = layer - .next_drawable() - .ok_or_else(|| anyhow::anyhow!("Failed to get drawable for render_to_image"))?; - - let command_buffer = self.render_frame(scene, drawable.texture(), viewport_size)?; - - // Commit and wait for completion without presenting - command_buffer.commit(); - command_buffer.wait_until_completed(); - - read_texture_to_image(drawable.texture()) - } - - /// Renders a scene to an image without requiring a window or CAMetalLayer. - /// - /// This is the primary method for headless rendering. It creates an offscreen - /// texture, renders the scene to it, and returns the pixel data as an RGBA image. - #[cfg(any(test, feature = "bench-support", feature = "test-support"))] - pub fn render_scene_to_image( - &mut self, - scene: &Scene, - size: Size, - ) -> Result { - if size.width.0 <= 0 || size.height.0 <= 0 { - anyhow::bail!("Invalid size for render_scene_to_image: {:?}", size); - } - - // Update path intermediate textures for this size - self.update_path_intermediate_textures(size); - - // Create an offscreen texture as render target - let texture_descriptor = metal::TextureDescriptor::new(); - texture_descriptor.set_width(size.width.0 as u64); - texture_descriptor.set_height(size.height.0 as u64); - texture_descriptor.set_pixel_format(MTLPixelFormat::BGRA8Unorm); - texture_descriptor - .set_usage(metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead); - texture_descriptor.set_storage_mode(metal::MTLStorageMode::Managed); - let target_texture = self.device.new_texture(&texture_descriptor); - - let command_buffer = self.render_frame(scene, &target_texture, size)?; - - // On discrete GPUs (non-unified memory), Managed textures require an - // explicit blit synchronize before the CPU can read back the rendered - // data. Without this, get_bytes returns stale zeros. - if !self.is_unified_memory { - let blit = command_buffer.new_blit_command_encoder(); - blit.synchronize_resource(&target_texture); - blit.end_encoding(); - } - - // Commit and wait for completion - command_buffer.commit(); - command_buffer.wait_until_completed(); - - read_texture_to_image(&target_texture) - } - - /// Renders a scene to a reused offscreen texture without reading pixels - /// back or blocking on GPU completion. - /// - /// This mirrors the CPU cost of presenting a frame to a window (scene - /// encoding, instance buffer writes, command submission) and is used by - /// headless benchmark rendering, where the produced pixels are never - /// inspected. - #[cfg(any(test, feature = "bench-support", feature = "test-support"))] - pub fn render_scene(&mut self, scene: &Scene, size: Size) -> Result<()> { - if size.width.0 <= 0 || size.height.0 <= 0 { - anyhow::bail!("Invalid size for render_scene: {:?}", size); - } - - self.update_path_intermediate_textures(size); - - let needs_new_target = self.headless_render_target.as_ref().is_none_or(|texture| { - texture.width() != size.width.0 as u64 || texture.height() != size.height.0 as u64 - }); - if needs_new_target { - let texture_descriptor = metal::TextureDescriptor::new(); - texture_descriptor.set_width(size.width.0 as u64); - texture_descriptor.set_height(size.height.0 as u64); - texture_descriptor.set_pixel_format(MTLPixelFormat::BGRA8Unorm); - texture_descriptor.set_usage( - metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead, - ); - texture_descriptor.set_storage_mode(metal::MTLStorageMode::Private); - self.headless_render_target = Some(self.device.new_texture(&texture_descriptor)); - } - let target_texture = self - .headless_render_target - .clone() - .expect("just ensured the render target exists"); - - let command_buffer = self.render_frame(scene, &target_texture, size)?; - - // Commit without waiting, mirroring presentation to a real window where - // the CPU doesn't block on the GPU. - command_buffer.commit(); - Ok(()) - } - - fn draw_primitives_to_texture( - &mut self, - scene: &Scene, - instance_bindings: &InstanceBindings, - writer: &mut InstanceBufferWriter, - texture: &metal::TextureRef, - viewport_size: Size, - ) -> Result { - let command_queue = self.command_queue.clone(); - let command_buffer = command_queue.new_command_buffer(); - let alpha = if self.opaque { 1. } else { 0. }; - - let mut command_encoder = new_command_encoder_for_texture( - command_buffer, - texture, - viewport_size, - Some(metal::MTLClearColor::new(0., 0., 0., alpha)), - ); - - command_encoder.set_fragment_buffer( - 8, - Some(&instance_bindings.clips.buffer), - instance_bindings.clips.offset as u64, - ); - for batch in scene.batches() { - match batch { - PrimitiveBatch::Shadows(range) => { - self.draw_shadows(range, instance_bindings, viewport_size, command_encoder) - } - PrimitiveBatch::Quads(range) => { - self.draw_quads(range, instance_bindings, viewport_size, command_encoder) - } - PrimitiveBatch::Paths(range) => { - let paths = &scene.paths[range]; - command_encoder.end_encoding(); - - let did_draw = self.draw_paths_to_intermediate( - paths, - writer, - viewport_size, - command_buffer, - &instance_bindings.clips, - )?; - - command_encoder = new_command_encoder_for_texture( - command_buffer, - texture, - viewport_size, - None, - ); - - command_encoder.set_fragment_buffer( - 8, - Some(&instance_bindings.clips.buffer), - instance_bindings.clips.offset as u64, - ); - if did_draw { - if let Err(error) = self.draw_paths_from_intermediate( - paths, - writer, - viewport_size, - command_encoder, - ) { - command_encoder.end_encoding(); - return Err(error); - } - } - } - PrimitiveBatch::Underlines(range) => { - self.draw_underlines(range, instance_bindings, viewport_size, command_encoder) - } - PrimitiveBatch::MonochromeSprites { texture_id, range } => self - .draw_monochrome_sprites( - texture_id, - range, - instance_bindings, - viewport_size, - command_encoder, - ), - PrimitiveBatch::PolychromeSprites { texture_id, range } => self - .draw_polychrome_sprites( - texture_id, - range, - instance_bindings, - viewport_size, - command_encoder, - ), - PrimitiveBatch::Surfaces(range) => self.draw_surfaces( - &scene.surfaces[range.clone()], - range.start, - instance_bindings, - viewport_size, - command_encoder, - ), - PrimitiveBatch::SubpixelSprites { .. } => unreachable!(), - } - } - - command_encoder.end_encoding(); - - Ok(command_buffer.to_owned()) - } - - fn draw_paths_to_intermediate( - &self, - paths: &[Path], - writer: &mut InstanceBufferWriter, - viewport_size: Size, - command_buffer: &metal::CommandBufferRef, - clips: &InstanceBinding, - ) -> Result { - if paths.is_empty() { - return Ok(false); - } - let intermediate_texture = self - .path_intermediate_texture - .as_ref() - .context("missing path intermediate texture")?; - - let mut vertices = Vec::new(); - for path in paths { - vertices.extend(path.vertices.iter().map(|v| PathRasterizationVertex { - xy_position: v.xy_position, - st_position: v.st_position, - color: path.color, - bounds: path.bounds.intersect(&path.content_mask.bounds), - content_mask: path.content_mask, - })); - } - let vertex_instance_bindings = writer.write(&vertices)?; - - let render_pass_descriptor = metal::RenderPassDescriptor::new(); - let color_attachment = render_pass_descriptor - .color_attachments() - .object_at(0) - .unwrap(); - color_attachment.set_load_action(metal::MTLLoadAction::Clear); - color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., 0.)); - - if let Some(msaa_texture) = &self.path_intermediate_msaa_texture { - color_attachment.set_texture(Some(msaa_texture)); - color_attachment.set_resolve_texture(Some(intermediate_texture)); - color_attachment.set_store_action(metal::MTLStoreAction::MultisampleResolve); - } else { - color_attachment.set_texture(Some(intermediate_texture)); - color_attachment.set_store_action(metal::MTLStoreAction::Store); - } - - let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor); - command_encoder.set_fragment_buffer(8, Some(&clips.buffer), clips.offset as u64); - command_encoder.set_render_pipeline_state(&self.paths_rasterization_pipeline_state); - command_encoder.set_vertex_buffer( - PathRasterizationInputIndex::Vertices as u64, - Some(&vertex_instance_bindings.buffer), - vertex_instance_bindings.offset as u64, - ); - command_encoder.set_vertex_bytes( - PathRasterizationInputIndex::ViewportSize as u64, - mem::size_of_val(&viewport_size) as u64, - &viewport_size as *const Size as *const _, - ); - command_encoder.set_fragment_buffer( - PathRasterizationInputIndex::Vertices as u64, - Some(&vertex_instance_bindings.buffer), - vertex_instance_bindings.offset as u64, - ); - command_encoder.draw_primitives( - metal::MTLPrimitiveType::Triangle, - 0, - vertices.len() as u64, - ); - - command_encoder.end_encoding(); - Ok(true) - } - - fn draw_shadows( - &self, - shadows: Range, - instance_bindings: &InstanceBindings, - viewport_size: Size, - command_encoder: &metal::RenderCommandEncoderRef, - ) { - if shadows.is_empty() { - return; - } - - command_encoder.set_render_pipeline_state(&self.shadows_pipeline_state); - command_encoder.set_vertex_buffer( - ShadowInputIndex::Vertices as u64, - Some(&self.unit_vertices), - 0, - ); - command_encoder.set_vertex_buffer( - ShadowInputIndex::Shadows as u64, - Some(&instance_bindings.shadows.buffer), - instance_bindings.shadows.offset as u64, - ); - command_encoder.set_fragment_buffer( - ShadowInputIndex::Shadows as u64, - Some(&instance_bindings.shadows.buffer), - instance_bindings.shadows.offset as u64, - ); - command_encoder.set_vertex_bytes( - ShadowInputIndex::ViewportSize as u64, - mem::size_of_val(&viewport_size) as u64, - &viewport_size as *const Size as *const _, - ); - - command_encoder.draw_primitives_instanced_base_instance( - metal::MTLPrimitiveType::Triangle, - 0, - 6, - shadows.len() as u64, - shadows.start as u64, - ); - } - - fn draw_quads( - &self, - quads: Range, - instance_bindings: &InstanceBindings, - viewport_size: Size, - command_encoder: &metal::RenderCommandEncoderRef, - ) { - if quads.is_empty() { - return; - } - - command_encoder.set_render_pipeline_state(&self.quads_pipeline_state); - command_encoder.set_vertex_buffer( - QuadInputIndex::Vertices as u64, - Some(&self.unit_vertices), - 0, - ); - command_encoder.set_vertex_buffer( - QuadInputIndex::Quads as u64, - Some(&instance_bindings.quads.buffer), - instance_bindings.quads.offset as u64, - ); - command_encoder.set_fragment_buffer( - QuadInputIndex::Quads as u64, - Some(&instance_bindings.quads.buffer), - instance_bindings.quads.offset as u64, - ); - command_encoder.set_vertex_bytes( - QuadInputIndex::ViewportSize as u64, - mem::size_of_val(&viewport_size) as u64, - &viewport_size as *const Size as *const _, - ); - - command_encoder.draw_primitives_instanced_base_instance( - metal::MTLPrimitiveType::Triangle, - 0, - 6, - quads.len() as u64, - quads.start as u64, - ); - } - - fn draw_paths_from_intermediate( - &self, - paths: &[Path], - writer: &mut InstanceBufferWriter, - viewport_size: Size, - command_encoder: &metal::RenderCommandEncoderRef, - ) -> Result<()> { - let Some(first_path) = paths.first() else { - return Ok(()); - }; - let intermediate_texture = self - .path_intermediate_texture - .as_ref() - .context("missing path intermediate texture")?; - - command_encoder.set_render_pipeline_state(&self.path_sprites_pipeline_state); - command_encoder.set_vertex_buffer( - SpriteInputIndex::Vertices as u64, - Some(&self.unit_vertices), - 0, - ); - command_encoder.set_vertex_bytes( - SpriteInputIndex::ViewportSize as u64, - mem::size_of_val(&viewport_size) as u64, - &viewport_size as *const Size as *const _, - ); - - command_encoder.set_fragment_texture( - SpriteInputIndex::AtlasTexture as u64, - Some(intermediate_texture), - ); - - // When copying paths from the intermediate texture to the drawable, - // each pixel must only be copied once, in case of transparent paths. - // - // If all paths have the same draw order, then their bounds are all - // disjoint, so we can copy each path's bounds individually. If this - // batch combines different draw orders, we perform a single copy - // for a minimal spanning rect. - let sprites; - if paths.last().unwrap().order == first_path.order { - sprites = paths - .iter() - .map(|path| PathSprite { - bounds: path.clipped_bounds(), - }) - .collect(); - } else { - let mut bounds = first_path.clipped_bounds(); - for path in paths.iter().skip(1) { - bounds = bounds.union(&path.clipped_bounds()); - } - sprites = vec![PathSprite { bounds }]; - } - - let sprite_instance_bindings = writer.write(&sprites)?; - command_encoder.set_vertex_buffer( - SpriteInputIndex::Sprites as u64, - Some(&sprite_instance_bindings.buffer), - sprite_instance_bindings.offset as u64, - ); - - command_encoder.draw_primitives_instanced( - metal::MTLPrimitiveType::Triangle, - 0, - 6, - sprites.len() as u64, - ); - Ok(()) - } - - fn draw_underlines( - &self, - underlines: Range, - instance_bindings: &InstanceBindings, - viewport_size: Size, - command_encoder: &metal::RenderCommandEncoderRef, - ) { - if underlines.is_empty() { - return; - } - - command_encoder.set_render_pipeline_state(&self.underlines_pipeline_state); - command_encoder.set_vertex_buffer( - UnderlineInputIndex::Vertices as u64, - Some(&self.unit_vertices), - 0, - ); - command_encoder.set_vertex_buffer( - UnderlineInputIndex::Underlines as u64, - Some(&instance_bindings.underlines.buffer), - instance_bindings.underlines.offset as u64, - ); - command_encoder.set_fragment_buffer( - UnderlineInputIndex::Underlines as u64, - Some(&instance_bindings.underlines.buffer), - instance_bindings.underlines.offset as u64, - ); - command_encoder.set_vertex_bytes( - UnderlineInputIndex::ViewportSize as u64, - mem::size_of_val(&viewport_size) as u64, - &viewport_size as *const Size as *const _, - ); - - command_encoder.draw_primitives_instanced_base_instance( - metal::MTLPrimitiveType::Triangle, - 0, - 6, - underlines.len() as u64, - underlines.start as u64, - ); - } - - fn draw_monochrome_sprites( - &self, - texture_id: AtlasTextureId, - sprites: Range, - instance_bindings: &InstanceBindings, - viewport_size: Size, - command_encoder: &metal::RenderCommandEncoderRef, - ) { - if sprites.is_empty() { - return; - } - - let texture = self.sprite_atlas.metal_texture(texture_id); - let texture_size = size( - DevicePixels(texture.width() as i32), - DevicePixels(texture.height() as i32), - ); - command_encoder.set_render_pipeline_state(&self.monochrome_sprites_pipeline_state); - command_encoder.set_vertex_buffer( - SpriteInputIndex::Vertices as u64, - Some(&self.unit_vertices), - 0, - ); - command_encoder.set_vertex_buffer( - SpriteInputIndex::Sprites as u64, - Some(&instance_bindings.monochrome_sprites.buffer), - instance_bindings.monochrome_sprites.offset as u64, - ); - command_encoder.set_vertex_bytes( - SpriteInputIndex::ViewportSize as u64, - mem::size_of_val(&viewport_size) as u64, - &viewport_size as *const Size as *const _, - ); - command_encoder.set_vertex_bytes( - SpriteInputIndex::AtlasTextureSize as u64, - mem::size_of_val(&texture_size) as u64, - &texture_size as *const Size as *const _, - ); - command_encoder.set_fragment_buffer( - SpriteInputIndex::Sprites as u64, - Some(&instance_bindings.monochrome_sprites.buffer), - instance_bindings.monochrome_sprites.offset as u64, - ); - command_encoder.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture)); - - command_encoder.draw_primitives_instanced_base_instance( - metal::MTLPrimitiveType::Triangle, - 0, - 6, - sprites.len() as u64, - sprites.start as u64, - ); - } - - fn draw_polychrome_sprites( - &self, - texture_id: AtlasTextureId, - sprites: Range, - instance_bindings: &InstanceBindings, - viewport_size: Size, - command_encoder: &metal::RenderCommandEncoderRef, - ) { - if sprites.is_empty() { - return; - } - - let texture = self.sprite_atlas.metal_texture(texture_id); - let texture_size = size( - DevicePixels(texture.width() as i32), - DevicePixels(texture.height() as i32), - ); - command_encoder.set_render_pipeline_state(&self.polychrome_sprites_pipeline_state); - command_encoder.set_vertex_buffer( - SpriteInputIndex::Vertices as u64, - Some(&self.unit_vertices), - 0, - ); - command_encoder.set_vertex_buffer( - SpriteInputIndex::Sprites as u64, - Some(&instance_bindings.polychrome_sprites.buffer), - instance_bindings.polychrome_sprites.offset as u64, - ); - command_encoder.set_vertex_bytes( - SpriteInputIndex::ViewportSize as u64, - mem::size_of_val(&viewport_size) as u64, - &viewport_size as *const Size as *const _, - ); - command_encoder.set_vertex_bytes( - SpriteInputIndex::AtlasTextureSize as u64, - mem::size_of_val(&texture_size) as u64, - &texture_size as *const Size as *const _, - ); - command_encoder.set_fragment_buffer( - SpriteInputIndex::Sprites as u64, - Some(&instance_bindings.polychrome_sprites.buffer), - instance_bindings.polychrome_sprites.offset as u64, - ); - command_encoder.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture)); - - command_encoder.draw_primitives_instanced_base_instance( - metal::MTLPrimitiveType::Triangle, - 0, - 6, - sprites.len() as u64, - sprites.start as u64, - ); - } - - fn draw_surfaces( - &mut self, - surfaces: &[PaintSurface], - first_surface: usize, - instance_bindings: &InstanceBindings, - viewport_size: Size, - command_encoder: &metal::RenderCommandEncoderRef, - ) { - if surfaces.is_empty() { - return; - } - - command_encoder.set_render_pipeline_state(&self.surfaces_pipeline_state); - command_encoder.set_vertex_buffer( - SurfaceInputIndex::Vertices as u64, - Some(&self.unit_vertices), - 0, - ); - command_encoder.set_vertex_buffer( - SurfaceInputIndex::Surfaces as u64, - Some(&instance_bindings.surfaces.buffer), - instance_bindings.surfaces.offset as u64, - ); - command_encoder.set_vertex_bytes( - SurfaceInputIndex::ViewportSize as u64, - mem::size_of_val(&viewport_size) as u64, - &viewport_size as *const Size as *const _, - ); - - for (index, surface) in surfaces.iter().enumerate() { - let texture_size = size( - DevicePixels::from(surface.image_buffer.get_width() as i32), - DevicePixels::from(surface.image_buffer.get_height() as i32), - ); - - assert_eq!( - surface.image_buffer.get_pixel_format(), - kCVPixelFormatType_420YpCbCr8BiPlanarFullRange - ); - - let y_texture = self - .core_video_texture_cache - .create_texture_from_image( - surface.image_buffer.as_concrete_TypeRef(), - None, - MTLPixelFormat::R8Unorm, - surface.image_buffer.get_width_of_plane(0), - surface.image_buffer.get_height_of_plane(0), - 0, - ) - .unwrap(); - let cb_cr_texture = self - .core_video_texture_cache - .create_texture_from_image( - surface.image_buffer.as_concrete_TypeRef(), - None, - MTLPixelFormat::RG8Unorm, - surface.image_buffer.get_width_of_plane(1), - surface.image_buffer.get_height_of_plane(1), - 1, - ) - .unwrap(); - - command_encoder.set_vertex_bytes( - SurfaceInputIndex::TextureSize as u64, - mem::size_of_val(&texture_size) as u64, - &texture_size as *const Size as *const _, - ); - // let y_texture = y_texture.get_texture().unwrap(). - command_encoder.set_fragment_texture(SurfaceInputIndex::YTexture as u64, unsafe { - let texture = CVMetalTextureGetTexture(y_texture.as_concrete_TypeRef()); - Some(metal::TextureRef::from_ptr(texture as *mut _)) - }); - command_encoder.set_fragment_texture(SurfaceInputIndex::CbCrTexture as u64, unsafe { - let texture = CVMetalTextureGetTexture(cb_cr_texture.as_concrete_TypeRef()); - Some(metal::TextureRef::from_ptr(texture as *mut _)) - }); - - command_encoder.draw_primitives_instanced_base_instance( - metal::MTLPrimitiveType::Triangle, - 0, - 6, - 1, - (first_surface + index) as u64, - ); - } - } -} - -fn new_command_encoder_for_texture<'a>( - command_buffer: &'a metal::CommandBufferRef, - texture: &'a metal::TextureRef, - viewport_size: Size, - clear_color: Option, -) -> &'a metal::RenderCommandEncoderRef { - let render_pass_descriptor = metal::RenderPassDescriptor::new(); - let color_attachment = render_pass_descriptor - .color_attachments() - .object_at(0) - .unwrap(); - color_attachment.set_texture(Some(texture)); - color_attachment.set_store_action(metal::MTLStoreAction::Store); - if let Some(clear_color) = clear_color { - color_attachment.set_load_action(metal::MTLLoadAction::Clear); - color_attachment.set_clear_color(clear_color); - } else { - color_attachment.set_load_action(metal::MTLLoadAction::Load); - } - - 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: i32::from(viewport_size.width) as f64, - height: i32::from(viewport_size.height) as f64, - znear: 0.0, - zfar: 1.0, - }); - command_encoder -} - -#[cfg(any(test, feature = "bench-support", feature = "test-support"))] -fn read_texture_to_image(texture: &metal::TextureRef) -> Result { - let width = texture.width() as u32; - let height = texture.height() as u32; - let bytes_per_row = width as usize * 4; - let mut pixels = vec![0u8; height as usize * bytes_per_row]; - - let region = metal::MTLRegion { - origin: metal::MTLOrigin { x: 0, y: 0, z: 0 }, - size: metal::MTLSize { - width: width as u64, - height: height as u64, - depth: 1, - }, - }; - texture.get_bytes( - pixels.as_mut_ptr() as *mut std::ffi::c_void, - bytes_per_row as u64, - region, - 0, - ); - - // Convert BGRA to RGBA (swap B and R channels) - for chunk in pixels.chunks_exact_mut(4) { - chunk.swap(0, 2); - } - - RgbaImage::from_raw(width, height, pixels).context("failed to create RgbaImage from pixel data") -} - -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_sprite_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::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_rasterization_pipeline_state( - device: &metal::DeviceRef, - library: &metal::LibraryRef, - label: &str, - vertex_fn_name: &str, - fragment_fn_name: &str, - pixel_format: metal::MTLPixelFormat, - path_sample_count: u32, -) -> 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())); - if path_sample_count > 1 { - descriptor.set_raster_sample_count(path_sample_count as _); - descriptor.set_alpha_to_coverage_enabled(false); - } - 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::OneMinusSourceAlpha); - color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha); - - device - .new_render_pipeline_state(&descriptor) - .expect("could not create render pipeline state") -} - -#[derive(Clone)] -struct InstanceBinding { - buffer: metal::Buffer, - offset: usize, -} - -struct InstanceBindings { - clips: InstanceBinding, - quads: InstanceBinding, - shadows: InstanceBinding, - underlines: InstanceBinding, - monochrome_sprites: InstanceBinding, - polychrome_sprites: InstanceBinding, - surfaces: InstanceBinding, -} - -fn write_instances(scene: &Scene, writer: &mut InstanceBufferWriter) -> Result { - let empty_clip = gpui::RoundedClip::::default(); - let clips = if scene.rounded_clips.is_empty() { - std::slice::from_ref(&empty_clip) - } else { - &scene.rounded_clips - }; - Ok(InstanceBindings { - clips: writer.write(clips)?, - quads: writer.write(&scene.quads)?, - shadows: writer.write(&scene.shadows)?, - underlines: writer.write(&scene.underlines)?, - monochrome_sprites: writer.write(&scene.monochrome_sprites)?, - polychrome_sprites: writer.write(&scene.polychrome_sprites)?, - surfaces: writer.write_iter(scene.surfaces.iter().map(|surface| SurfaceBounds { - bounds: surface.bounds, - content_mask: surface.content_mask, - }))?, - }) -} - -struct InstanceBufferWriter { - device: metal::Device, - pool: Arc>, - unified_memory: bool, - filled: Vec<(InstanceBuffer, usize)>, - current: InstanceBuffer, - offset: usize, -} - -impl InstanceBufferWriter { - fn new( - device: &metal::Device, - pool: &Arc>, - unified_memory: bool, - ) -> Self { - let current = pool.lock().acquire(device, unified_memory); - Self { - device: device.clone(), - pool: pool.clone(), - unified_memory, - filled: Vec::new(), - current, - offset: 0, - } - } - - fn allocate(&mut self, count: usize) -> Result<(InstanceBinding, &mut [MaybeUninit])> { - let size = mem::size_of::() * count; - let mut offset = self.offset.next_multiple_of(INSTANCE_BUFFER_ALIGNMENT); - if offset + size > self.current.size { - self.grow(size)?; - offset = 0; - } - self.offset = offset + size; - - let binding = InstanceBinding { - buffer: self.current.metal_buffer.clone(), - offset, - }; - // Safety: the reservation lies within a buffer this frame owns - // exclusively, and never overlaps one handed out earlier. - let values = unsafe { - let start = (self.current.metal_buffer.contents() as *mut u8).add(offset); - slice::from_raw_parts_mut(start.cast::>(), count) - }; - Ok((binding, values)) - } - - fn write(&mut self, values: &[T]) -> Result { - let (binding, destination) = self.allocate::(values.len())?; - unsafe { - ptr::copy_nonoverlapping( - values.as_ptr(), - destination.as_mut_ptr().cast::(), - values.len(), - ); - } - Ok(binding) - } - - fn write_iter( - &mut self, - values: impl ExactSizeIterator, - ) -> Result { - let (binding, destination) = self.allocate::(values.len())?; - for (slot, value) in destination.iter_mut().zip(values) { - slot.write(value); - } - Ok(binding) - } - - fn grow(&mut self, required: usize) -> Result<()> { - let mut pool = self.pool.lock(); - let buffer_size = (pool.buffer_size * 2) - .max(required.next_power_of_two()) - .min(MAX_INSTANCE_BUFFER_SIZE); - anyhow::ensure!( - buffer_size >= required, - "instance buffer needs {required} bytes, above the maximum of {MAX_INSTANCE_BUFFER_SIZE}" - ); - anyhow::ensure!( - buffer_size > self.current.size, - "frame instance data exceeds the {MAX_INSTANCE_BUFFER_SIZE}-byte maximum" - ); - if buffer_size != pool.buffer_size { - log::info!("increased instance buffer size to {buffer_size}"); - pool.reset(buffer_size); - } - let buffer = pool.acquire(&self.device, self.unified_memory); - drop(pool); - - let filled = mem::replace(&mut self.current, buffer); - self.filled.push((filled, self.offset)); - self.offset = 0; - Ok(()) - } - - fn finish(self) -> InstanceBuffer { - let Self { - unified_memory, - filled, - current, - offset, - .. - } = self; - - if !unified_memory { - for (buffer, written) in &filled { - if *written == 0 { - continue; - } - buffer.metal_buffer.did_modify_range(NSRange { - location: 0, - length: *written as NSUInteger, - }); - } - if offset > 0 { - current.metal_buffer.did_modify_range(NSRange { - location: 0, - length: offset as NSUInteger, - }); - } - } - - // Metal retains encoded resources until the command buffer completes. - // Only the final, largest buffer is worth keeping in the pool. - drop(filled); - current - } -} - -#[repr(C)] -enum ShadowInputIndex { - Vertices = 0, - Shadows = 1, - ViewportSize = 2, -} - -#[repr(C)] -enum QuadInputIndex { - Vertices = 0, - Quads = 1, - ViewportSize = 2, -} - -#[repr(C)] -enum UnderlineInputIndex { - Vertices = 0, - Underlines = 1, - ViewportSize = 2, -} - -#[repr(C)] -enum SpriteInputIndex { - Vertices = 0, - Sprites = 1, - ViewportSize = 2, - AtlasTextureSize = 3, - AtlasTexture = 4, -} - -#[repr(C)] -enum SurfaceInputIndex { - Vertices = 0, - Surfaces = 1, - ViewportSize = 2, - TextureSize = 3, - YTexture = 4, - CbCrTexture = 5, -} - -#[repr(C)] -enum PathRasterizationInputIndex { - Vertices = 0, - ViewportSize = 1, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -#[repr(C)] -pub struct PathSprite { - pub bounds: Bounds, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -#[repr(C)] -pub struct SurfaceBounds { - pub bounds: Bounds, - pub content_mask: ContentMask, -} - -#[cfg(any(test, feature = "bench-support", feature = "test-support"))] -pub struct MetalHeadlessRenderer { - renderer: MetalRenderer, -} - -#[cfg(any(test, feature = "bench-support", feature = "test-support"))] -impl MetalHeadlessRenderer { - pub fn new() -> Self { - let instance_buffer_pool = Arc::new(Mutex::new(InstanceBufferPool::default())); - let renderer = MetalRenderer::new_headless(instance_buffer_pool); - Self { renderer } - } -} - -#[cfg(any(test, feature = "bench-support", feature = "test-support"))] -impl gpui::PlatformHeadlessRenderer for MetalHeadlessRenderer { - fn render_scene_to_image( - &mut self, - scene: &Scene, - size: Size, - ) -> anyhow::Result { - self.renderer.render_scene_to_image(scene, size) - } - - fn render_scene(&mut self, scene: &Scene, size: Size) -> anyhow::Result<()> { - self.renderer.render_scene(scene, size) - } - - fn sprite_atlas(&self) -> Arc { - self.renderer.sprite_atlas().clone() - } -} diff --git a/crates/gpui_pre_apple/src/shaders.metal b/crates/gpui_pre_apple/src/shaders.metal deleted file mode 100644 index 9fd9701a..00000000 --- a/crates/gpui_pre_apple/src/shaders.metal +++ /dev/null @@ -1,1384 +0,0 @@ -#include -#include - -using namespace metal; - -float content_mask_coverage(float2 position, ContentMask_ScaledPixels mask, - constant RoundedClip_ScaledPixels *clips); -float4 hsla_to_rgba(Hsla hsla); -float3 srgb_to_linear(float3 color); -float3 linear_to_srgb(float3 color); -float4 srgb_to_oklab(float4 color); -float4 oklab_to_srgb(float4 color); -float4 to_device_position(float2 unit_vertex, Bounds_ScaledPixels bounds, - constant Size_DevicePixels *viewport_size); -float4 to_device_position_transformed(float2 unit_vertex, Bounds_ScaledPixels bounds, - TransformationMatrix transformation, - constant Size_DevicePixels *input_viewport_size); - -float2 to_tile_position(float2 unit_vertex, AtlasTile tile, - constant Size_DevicePixels *atlas_size); -float4 distance_from_clip_rect(float2 unit_vertex, Bounds_ScaledPixels bounds, - ContentMask_ScaledPixels mask); -float4 distance_from_clip_rect_transformed(float2 unit_vertex, Bounds_ScaledPixels bounds, - ContentMask_ScaledPixels mask, TransformationMatrix transformation); -float corner_dash_velocity(float dv1, float dv2); -float dash_alpha(float t, float period, float length, float dash_velocity, - float antialias_threshold); -float quarter_ellipse_sdf(float2 point, float2 radii); -float pick_corner_radius(float2 center_to_point, Corners_ScaledPixels corner_radii); -float quad_sdf(float2 point, Bounds_ScaledPixels bounds, - Corners_ScaledPixels corner_radii); -float quad_sdf_impl(float2 center_to_point, float corner_radius); -float gaussian(float x, float sigma); -float2 erf(float2 x); -float blur_along_x(float x, float y, float sigma, float corner, - float2 half_size); -float4 over(float4 below, float4 above); -float radians(float degrees); -float4 fill_color(Background background, float2 position, Bounds_ScaledPixels bounds, - float4 solid_color, float4 color0, float4 color1); - -struct GradientColor { - float4 solid; - float4 color0; - float4 color1; -}; -GradientColor prepare_fill_color(uint tag, uint color_space, Hsla solid, Hsla color0, Hsla color1); - -struct QuadVertexOutput { - uint quad_id [[flat]]; - float4 position [[position]]; - float4 border_color [[flat]]; - float4 background_solid [[flat]]; - float4 background_color0 [[flat]]; - float4 background_color1 [[flat]]; - float clip_distance [[clip_distance]][4]; -}; - -struct QuadFragmentInput { - uint quad_id [[flat]]; - float4 position [[position]]; - float4 border_color [[flat]]; - float4 background_solid [[flat]]; - float4 background_color0 [[flat]]; - float4 background_color1 [[flat]]; -}; - -vertex QuadVertexOutput quad_vertex(uint unit_vertex_id [[vertex_id]], - uint quad_id [[instance_id]], - constant float2 *unit_vertices - [[buffer(QuadInputIndex_Vertices)]], - constant Quad *quads - [[buffer(QuadInputIndex_Quads)]], - constant Size_DevicePixels *viewport_size - [[buffer(QuadInputIndex_ViewportSize)]]) { - float2 unit_vertex = unit_vertices[unit_vertex_id]; - Quad quad = quads[quad_id]; - float4 device_position = - to_device_position(unit_vertex, quad.bounds, viewport_size); - float4 clip_distance = distance_from_clip_rect(unit_vertex, quad.bounds, - quad.content_mask); - float4 border_color = hsla_to_rgba(quad.border_color); - - GradientColor gradient = prepare_fill_color( - quad.background.tag, - quad.background.color_space, - quad.background.solid, - quad.background.colors[0].color, - quad.background.colors[1].color - ); - - return QuadVertexOutput{ - quad_id, - device_position, - border_color, - gradient.solid, - gradient.color0, - gradient.color1, - {clip_distance.x, clip_distance.y, clip_distance.z, clip_distance.w}}; -} - -fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]], - constant Quad *quads - [[buffer(QuadInputIndex_Quads)]], - constant RoundedClip_ScaledPixels *clips [[buffer(8)]]) { - Quad quad = quads[input.quad_id]; - float coverage = content_mask_coverage(input.position.xy, quad.content_mask, clips); - if (coverage <= 0.0) { - return float4(0.0); - } - float4 background_color = fill_color(quad.background, input.position.xy, quad.bounds, - input.background_solid, input.background_color0, input.background_color1); - - bool unrounded = quad.corner_radii.top_left == 0.0 && - quad.corner_radii.bottom_left == 0.0 && - quad.corner_radii.top_right == 0.0 && - quad.corner_radii.bottom_right == 0.0; - - // Fast path when the quad is not rounded and doesn't have any border - if (quad.border_widths.top == 0.0 && - quad.border_widths.left == 0.0 && - quad.border_widths.right == 0.0 && - quad.border_widths.bottom == 0.0 && - unrounded) { - return background_color * float4(1.0, 1.0, 1.0, coverage); - } - - float2 size = float2(quad.bounds.size.width, quad.bounds.size.height); - float2 half_size = size / 2.0; - float2 point = input.position.xy - float2(quad.bounds.origin.x, quad.bounds.origin.y); - float2 center_to_point = point - half_size; - - // Signed distance field threshold for inclusion of pixels. 0.5 is the - // minimum distance between the center of the pixel and the edge. - const float antialias_threshold = 0.5; - - // Radius of the nearest corner - float corner_radius = pick_corner_radius(center_to_point, quad.corner_radii); - - // Width of the nearest borders - float2 border = float2( - center_to_point.x < 0.0 ? quad.border_widths.left : quad.border_widths.right, - center_to_point.y < 0.0 ? quad.border_widths.top : quad.border_widths.bottom - ); - - // 0-width borders are reduced so that `inner_sdf >= antialias_threshold`. - // The purpose of this is to not draw antialiasing pixels in this case. - float2 reduced_border = float2( - border.x == 0.0 ? -antialias_threshold : border.x, - border.y == 0.0 ? -antialias_threshold : border.y); - - // Vector from the corner of the quad bounds to the point, after mirroring - // the point into the bottom right quadrant. Both components are <= 0. - float2 corner_to_point = fabs(center_to_point) - half_size; - - // Vector from the point to the center of the rounded corner's circle, also - // mirrored into bottom right quadrant. - float2 corner_center_to_point = corner_to_point + corner_radius; - - // Whether the nearest point on the border is rounded - bool is_near_rounded_corner = - corner_center_to_point.x >= 0.0 && - corner_center_to_point.y >= 0.0; - - // Vector from straight border inner corner to point. - // - // 0-width borders are turned into width -1 so that inner_sdf is > 1.0 near - // the border. Without this, antialiasing pixels would be drawn. - float2 straight_border_inner_corner_to_point = corner_to_point + reduced_border; - - // Whether the point is beyond the inner edge of the straight border - bool is_beyond_inner_straight_border = - straight_border_inner_corner_to_point.x > 0.0 || - straight_border_inner_corner_to_point.y > 0.0; - - - // Whether the point is far enough inside the quad, such that the pixels are - // not affected by the straight border. - bool is_within_inner_straight_border = - straight_border_inner_corner_to_point.x < -antialias_threshold && - straight_border_inner_corner_to_point.y < -antialias_threshold; - - // Fast path for points that must be part of the background - if (is_within_inner_straight_border && !is_near_rounded_corner) { - return background_color * float4(1.0, 1.0, 1.0, coverage); - } - - // Signed distance of the point to the outside edge of the quad's border - float outer_sdf = quad_sdf_impl(corner_center_to_point, corner_radius); - - // Approximate signed distance of the point to the inside edge of the quad's - // border. It is negative outside this edge (within the border), and - // positive inside. - // - // This is not always an accurate signed distance: - // * The rounded portions with varying border width use an approximation of - // nearest-point-on-ellipse. - // * When it is quickly known to be outside the edge, -1.0 is used. - float inner_sdf = 0.0; - if (corner_center_to_point.x <= 0.0 || corner_center_to_point.y <= 0.0) { - // Fast paths for straight borders - inner_sdf = -max(straight_border_inner_corner_to_point.x, - straight_border_inner_corner_to_point.y); - } else if (is_beyond_inner_straight_border) { - // Fast path for points that must be outside the inner edge - inner_sdf = -1.0; - } else if (reduced_border.x == reduced_border.y) { - // Fast path for circular inner edge. - inner_sdf = -(outer_sdf + reduced_border.x); - } else { - float2 ellipse_radii = max(float2(0.0), float2(corner_radius) - reduced_border); - inner_sdf = quarter_ellipse_sdf(corner_center_to_point, ellipse_radii); - } - - // Negative when inside the border - float border_sdf = max(inner_sdf, outer_sdf); - - float4 color = background_color; - if (border_sdf < antialias_threshold) { - float4 border_color = input.border_color; - - // Dashed border logic when border_style == 1 - if (quad.border_style == 1) { - // Position along the perimeter in "dash space", where each dash - // period has length 1 - float t = 0.0; - - // Total number of dash periods, so that the dash spacing can be - // adjusted to evenly divide it - float max_t = 0.0; - - // Border width is proportional to dash size. This is the behavior - // used by browsers, but also avoids dashes from different segments - // overlapping when dash size is smaller than the border width. - // - // Dash pattern: (2 * border width) dash, (1 * border width) gap - const float dash_length_per_width = 2.0; - const float dash_gap_per_width = 1.0; - const float dash_period_per_width = dash_length_per_width + dash_gap_per_width; - - // Since the dash size is determined by border width, the density of - // dashes varies. Multiplying a pixel distance by this returns a - // position in dash space - it has units (dash period / pixels). So - // a dash velocity of (1 / 10) is 1 dash every 10 pixels. - float dash_velocity = 0.0; - - // Dividing this by the border width gives the dash velocity - const float dv_numerator = 1.0 / dash_period_per_width; - - if (unrounded) { - // When corners aren't rounded, the dashes are separately laid - // out on each straight line, rather than around the whole - // perimeter. This way each line starts and ends with a dash. - bool is_horizontal = corner_center_to_point.x < corner_center_to_point.y; - - // Choosing the right border width for dashed borders. - // TODO: A better solution exists taking a look at the whole file. - // this does not fix single dashed borders at the corners - float2 dashed_border = float2( - fmax(quad.border_widths.bottom, quad.border_widths.top), - fmax(quad.border_widths.right, quad.border_widths.left)); - - float border_width = is_horizontal ? dashed_border.x : dashed_border.y; - dash_velocity = dv_numerator / border_width; - t = is_horizontal ? point.x : point.y; - t *= dash_velocity; - max_t = is_horizontal ? size.x : size.y; - max_t *= dash_velocity; - } else { - // When corners are rounded, the dashes are laid out clockwise - // around the whole perimeter. - - float r_tr = quad.corner_radii.top_right; - float r_br = quad.corner_radii.bottom_right; - float r_bl = quad.corner_radii.bottom_left; - float r_tl = quad.corner_radii.top_left; - - float w_t = quad.border_widths.top; - float w_r = quad.border_widths.right; - float w_b = quad.border_widths.bottom; - float w_l = quad.border_widths.left; - - // Straight side dash velocities - float dv_t = w_t <= 0.0 ? 0.0 : dv_numerator / w_t; - float dv_r = w_r <= 0.0 ? 0.0 : dv_numerator / w_r; - float dv_b = w_b <= 0.0 ? 0.0 : dv_numerator / w_b; - float dv_l = w_l <= 0.0 ? 0.0 : dv_numerator / w_l; - - // Straight side lengths in dash space - float s_t = (size.x - r_tl - r_tr) * dv_t; - float s_r = (size.y - r_tr - r_br) * dv_r; - float s_b = (size.x - r_br - r_bl) * dv_b; - float s_l = (size.y - r_bl - r_tl) * dv_l; - - float corner_dash_velocity_tr = corner_dash_velocity(dv_t, dv_r); - float corner_dash_velocity_br = corner_dash_velocity(dv_b, dv_r); - float corner_dash_velocity_bl = corner_dash_velocity(dv_b, dv_l); - float corner_dash_velocity_tl = corner_dash_velocity(dv_t, dv_l); - - // Corner lengths in dash space - float c_tr = r_tr * (M_PI_F / 2.0) * corner_dash_velocity_tr; - float c_br = r_br * (M_PI_F / 2.0) * corner_dash_velocity_br; - float c_bl = r_bl * (M_PI_F / 2.0) * corner_dash_velocity_bl; - float c_tl = r_tl * (M_PI_F / 2.0) * corner_dash_velocity_tl; - - // Cumulative dash space upto each segment - float upto_tr = s_t; - float upto_r = upto_tr + c_tr; - float upto_br = upto_r + s_r; - float upto_b = upto_br + c_br; - float upto_bl = upto_b + s_b; - float upto_l = upto_bl + c_bl; - float upto_tl = upto_l + s_l; - max_t = upto_tl + c_tl; - - if (is_near_rounded_corner) { - float radians = atan2(corner_center_to_point.y, corner_center_to_point.x); - float corner_t = radians * corner_radius; - - if (center_to_point.x >= 0.0) { - if (center_to_point.y < 0.0) { - dash_velocity = corner_dash_velocity_tr; - // Subtracted because radians is pi/2 to 0 when - // going clockwise around the top right corner, - // since the y axis has been flipped - t = upto_r - corner_t * dash_velocity; - } else { - dash_velocity = corner_dash_velocity_br; - // Added because radians is 0 to pi/2 when going - // clockwise around the bottom-right corner - t = upto_br + corner_t * dash_velocity; - } - } else { - if (center_to_point.y >= 0.0) { - dash_velocity = corner_dash_velocity_bl; - // Subtracted because radians is pi/1 to 0 when - // going clockwise around the bottom-left corner, - // since the x axis has been flipped - t = upto_l - corner_t * dash_velocity; - } else { - dash_velocity = corner_dash_velocity_tl; - // Added because radians is 0 to pi/2 when going - // clockwise around the top-left corner, since both - // axis were flipped - t = upto_tl + corner_t * dash_velocity; - } - } - } else { - // Straight borders - bool is_horizontal = corner_center_to_point.x < corner_center_to_point.y; - if (is_horizontal) { - if (center_to_point.y < 0.0) { - dash_velocity = dv_t; - t = (point.x - r_tl) * dash_velocity; - } else { - dash_velocity = dv_b; - t = upto_bl - (point.x - r_bl) * dash_velocity; - } - } else { - if (center_to_point.x < 0.0) { - dash_velocity = dv_l; - t = upto_tl - (point.y - r_tl) * dash_velocity; - } else { - dash_velocity = dv_r; - t = upto_r + (point.y - r_tr) * dash_velocity; - } - } - } - } - - float dash_length = dash_length_per_width / dash_period_per_width; - float desired_dash_gap = dash_gap_per_width / dash_period_per_width; - - // Straight borders should start and end with a dash, so max_t is - // reduced to cause this. - max_t -= unrounded ? dash_length : 0.0; - if (max_t >= 1.0) { - // Adjust dash gap to evenly divide max_t - float dash_count = floor(max_t); - float dash_period = max_t / dash_count; - border_color.a *= dash_alpha(t, dash_period, dash_length, dash_velocity, - antialias_threshold); - } else if (unrounded) { - // When there isn't enough space for the full gap between the - // two start / end dashes of a straight border, reduce gap to - // make them fit. - float dash_gap = max_t - dash_length; - if (dash_gap > 0.0) { - float dash_period = dash_length + dash_gap; - border_color.a *= dash_alpha(t, dash_period, dash_length, dash_velocity, - antialias_threshold); - } - } - } - - // Blend the border on top of the background and then linearly interpolate - // between the two as we slide inside the background. - float4 blended_border = over(background_color, border_color); - color = mix(background_color, blended_border, - saturate(antialias_threshold - inner_sdf)); - } - - return color * float4(1.0, 1.0, 1.0, min(coverage, saturate(antialias_threshold - outer_sdf))); -} - -// Returns the dash velocity of a corner given the dash velocity of the two -// sides, by returning the slower velocity (larger dashes). -// -// Since 0 is used for dash velocity when the border width is 0 (instead of -// +inf), this returns the other dash velocity in that case. -// -// An alternative to this might be to appropriately interpolate the dash -// velocity around the corner, but that seems overcomplicated. -float corner_dash_velocity(float dv1, float dv2) { - if (dv1 == 0.0) { - return dv2; - } else if (dv2 == 0.0) { - return dv1; - } else { - return min(dv1, dv2); - } -} - -// Returns alpha used to render antialiased dashes. -// `t` is within the dash when `fmod(t, period) < length`. -float dash_alpha( - float t, float period, float length, float dash_velocity, - float antialias_threshold) { - float half_period = period / 2.0; - float half_length = length / 2.0; - // Value in [-half_period, half_period] - // The dash is in [-half_length, half_length] - float centered = fmod(t + half_period - half_length, period) - half_period; - // Signed distance for the dash, negative values are inside the dash - float signed_distance = abs(centered) - half_length; - // Antialiased alpha based on the signed distance - return saturate(antialias_threshold - signed_distance / dash_velocity); -} - -// This approximates distance to the nearest point to a quarter ellipse in a way -// that is sufficient for anti-aliasing when the ellipse is not very eccentric. -// The components of `point` are expected to be positive. -// -// Negative on the outside and positive on the inside. -float quarter_ellipse_sdf(float2 point, float2 radii) { - // Scale the space to treat the ellipse like a unit circle - float2 circle_vec = point / radii; - float unit_circle_sdf = length(circle_vec) - 1.0; - // Approximate up-scaling of the length by using the average of the radii. - // - // TODO: A better solution would be to use the gradient of the implicit - // function for an ellipse to approximate a scaling factor. - return unit_circle_sdf * (radii.x + radii.y) * -0.5; -} - -struct ShadowVertexOutput { - float4 position [[position]]; - float4 color [[flat]]; - uint shadow_id [[flat]]; - float clip_distance [[clip_distance]][4]; -}; - -struct ShadowFragmentInput { - float4 position [[position]]; - float4 color [[flat]]; - uint shadow_id [[flat]]; -}; - -vertex ShadowVertexOutput shadow_vertex( - uint unit_vertex_id [[vertex_id]], uint shadow_id [[instance_id]], - constant float2 *unit_vertices [[buffer(ShadowInputIndex_Vertices)]], - constant Shadow *shadows [[buffer(ShadowInputIndex_Shadows)]], - constant Size_DevicePixels *viewport_size - [[buffer(ShadowInputIndex_ViewportSize)]]) { - float2 unit_vertex = unit_vertices[unit_vertex_id]; - Shadow shadow = shadows[shadow_id]; - - Bounds_ScaledPixels bounds; - if (shadow.inset != 0u) { - bounds = shadow.element_bounds; - } else { - // Leave room for the gaussian tail outside the shadow rect. - float margin = 3. * shadow.blur_radius; - bounds = shadow.bounds; - bounds.origin.x -= margin; - bounds.origin.y -= margin; - bounds.size.width += 2. * margin; - bounds.size.height += 2. * margin; - } - - float4 device_position = - to_device_position(unit_vertex, bounds, viewport_size); - float4 clip_distance = - distance_from_clip_rect(unit_vertex, bounds, shadow.content_mask); - float4 color = hsla_to_rgba(shadow.color); - - return ShadowVertexOutput{ - device_position, - color, - shadow_id, - {clip_distance.x, clip_distance.y, clip_distance.z, clip_distance.w}}; -} - -fragment float4 shadow_fragment(ShadowFragmentInput input [[stage_in]], - constant Shadow *shadows - [[buffer(ShadowInputIndex_Shadows)]], - constant RoundedClip_ScaledPixels *clips [[buffer(8)]]) { - Shadow shadow = shadows[input.shadow_id]; - float coverage = content_mask_coverage(input.position.xy, shadow.content_mask, clips); - if (coverage <= 0.0) { - return float4(0.0); - } - - float2 origin = float2(shadow.bounds.origin.x, shadow.bounds.origin.y); - float2 size = float2(shadow.bounds.size.width, shadow.bounds.size.height); - float2 half_size = size / 2.; - float2 center = origin + half_size; - float2 point = input.position.xy - center; - float corner_radius; - if (point.x < 0.) { - if (point.y < 0.) { - corner_radius = shadow.corner_radii.top_left; - } else { - corner_radius = shadow.corner_radii.bottom_left; - } - } else { - if (point.y < 0.) { - corner_radius = shadow.corner_radii.top_right; - } else { - corner_radius = shadow.corner_radii.bottom_right; - } - } - - float alpha; - if (shadow.blur_radius == 0.) { - float distance = quad_sdf(input.position.xy, shadow.bounds, shadow.corner_radii); - alpha = saturate(0.5 - distance); - } else { - // 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. * shadow.blur_radius, low, high); - float end = clamp(3. * shadow.blur_radius, low, high); - - // Accumulate samples (we can get away with surprisingly few samples) - float step = (end - start) / 4.; - float y = start + step * 0.5; - alpha = 0.; - for (int i = 0; i < 4; i++) { - alpha += blur_along_x(point.x, point.y - y, shadow.blur_radius, - corner_radius, half_size) * - gaussian(y, shadow.blur_radius) * step; - y += step; - } - } - - if (shadow.inset != 0u) { - // The inset shadow is the complement of the (blurred) hole rect, clipped to the element. - // `saturate(0.5 - d)` gives a 1-pixel antialiased edge: d <= -0.5 -> 1, d >= 0.5 -> 0. - alpha = 1. - alpha; - float element_distance = quad_sdf(input.position.xy, shadow.element_bounds, - shadow.element_corner_radii); - alpha *= saturate(0.5 - element_distance); - } - - return input.color * float4(1., 1., 1., alpha * coverage); -} - -struct UnderlineVertexOutput { - float4 position [[position]]; - float4 color [[flat]]; - uint underline_id [[flat]]; - float clip_distance [[clip_distance]][4]; -}; - -struct UnderlineFragmentInput { - float4 position [[position]]; - float4 color [[flat]]; - uint underline_id [[flat]]; -}; - -vertex UnderlineVertexOutput underline_vertex( - uint unit_vertex_id [[vertex_id]], uint underline_id [[instance_id]], - constant float2 *unit_vertices [[buffer(UnderlineInputIndex_Vertices)]], - constant Underline *underlines [[buffer(UnderlineInputIndex_Underlines)]], - constant Size_DevicePixels *viewport_size - [[buffer(ShadowInputIndex_ViewportSize)]]) { - float2 unit_vertex = unit_vertices[unit_vertex_id]; - Underline underline = underlines[underline_id]; - float4 device_position = - to_device_position(unit_vertex, underline.bounds, viewport_size); - float4 clip_distance = distance_from_clip_rect(unit_vertex, underline.bounds, - underline.content_mask); - float4 color = hsla_to_rgba(underline.color); - return UnderlineVertexOutput{ - device_position, - color, - underline_id, - {clip_distance.x, clip_distance.y, clip_distance.z, clip_distance.w}}; -} - -fragment float4 underline_fragment(UnderlineFragmentInput input [[stage_in]], - constant Underline *underlines - [[buffer(UnderlineInputIndex_Underlines)]], - constant RoundedClip_ScaledPixels *clips [[buffer(8)]]) { - const float WAVE_FREQUENCY = 2.0; - const float WAVE_HEIGHT_RATIO = 0.8; - - Underline underline = underlines[input.underline_id]; - float coverage = content_mask_coverage(input.position.xy, underline.content_mask, clips); - if (coverage <= 0.0) { - return float4(0.0); - } - if (underline.wavy) { - float half_thickness = underline.thickness * 0.5; - float2 origin = - float2(underline.bounds.origin.x, underline.bounds.origin.y); - - float2 st = ((input.position.xy - origin) / underline.bounds.size.height) - - float2(0., 0.5); - float frequency = (M_PI_F * WAVE_FREQUENCY * underline.thickness) / underline.bounds.size.height; - float amplitude = (underline.thickness * WAVE_HEIGHT_RATIO) / underline.bounds.size.height; - - 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 * underline.bounds.size.height; - 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 * coverage); - } else { - return input.color * float4(1., 1., 1., coverage); - } -} - -struct MonochromeSpriteVertexOutput { - float4 position [[position]]; - float2 tile_position; - float4 color [[flat]]; - uint sprite_id [[flat]]; - float4 clip_distance; -}; - -struct MonochromeSpriteFragmentInput { - float4 position [[position]]; - float2 tile_position; - float4 color [[flat]]; - uint sprite_id [[flat]]; - float4 clip_distance; -}; - -vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex( - uint unit_vertex_id [[vertex_id]], uint sprite_id [[instance_id]], - constant float2 *unit_vertices [[buffer(SpriteInputIndex_Vertices)]], - constant MonochromeSprite *sprites [[buffer(SpriteInputIndex_Sprites)]], - constant Size_DevicePixels *viewport_size - [[buffer(SpriteInputIndex_ViewportSize)]], - constant Size_DevicePixels *atlas_size - [[buffer(SpriteInputIndex_AtlasTextureSize)]]) { - float2 unit_vertex = unit_vertices[unit_vertex_id]; - MonochromeSprite sprite = sprites[sprite_id]; - float4 device_position = - to_device_position_transformed(unit_vertex, sprite.bounds, sprite.transformation, viewport_size); - float4 clip_distance = distance_from_clip_rect_transformed(unit_vertex, sprite.bounds, - sprite.content_mask, sprite.transformation); - float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size); - float4 color = hsla_to_rgba(sprite.color); - return MonochromeSpriteVertexOutput{ - device_position, - tile_position, - color, - sprite_id, - {clip_distance.x, clip_distance.y, clip_distance.z, clip_distance.w}}; -} - -fragment float4 monochrome_sprite_fragment( - MonochromeSpriteFragmentInput input [[stage_in]], - constant MonochromeSprite *sprites [[buffer(SpriteInputIndex_Sprites)]], - texture2d atlas_texture [[texture(SpriteInputIndex_AtlasTexture)]], - constant RoundedClip_ScaledPixels *clips [[buffer(8)]]) { - if (any(input.clip_distance < float4(0.0))) { - return float4(0.0); - } - - MonochromeSprite sprite = sprites[input.sprite_id]; - float coverage = content_mask_coverage(input.position.xy, sprite.content_mask, clips); - if (coverage <= 0.0) { - return float4(0.0); - } - - constexpr sampler atlas_texture_sampler(mag_filter::linear, - min_filter::linear); - float4 sample = - atlas_texture.sample(atlas_texture_sampler, input.tile_position); - float4 color = input.color; - color.a *= sample.a * coverage; - return color; -} - -struct PolychromeSpriteVertexOutput { - float4 position [[position]]; - float2 tile_position; - uint sprite_id [[flat]]; - float clip_distance [[clip_distance]][4]; -}; - -struct PolychromeSpriteFragmentInput { - float4 position [[position]]; - float2 tile_position; - uint sprite_id [[flat]]; -}; - -vertex PolychromeSpriteVertexOutput polychrome_sprite_vertex( - uint unit_vertex_id [[vertex_id]], uint sprite_id [[instance_id]], - constant float2 *unit_vertices [[buffer(SpriteInputIndex_Vertices)]], - constant PolychromeSprite *sprites [[buffer(SpriteInputIndex_Sprites)]], - constant Size_DevicePixels *viewport_size - [[buffer(SpriteInputIndex_ViewportSize)]], - constant Size_DevicePixels *atlas_size - [[buffer(SpriteInputIndex_AtlasTextureSize)]]) { - - float2 unit_vertex = unit_vertices[unit_vertex_id]; - PolychromeSprite sprite = sprites[sprite_id]; - float4 device_position = - to_device_position(unit_vertex, sprite.bounds, viewport_size); - float4 clip_distance = distance_from_clip_rect(unit_vertex, sprite.bounds, - sprite.content_mask); - float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size); - return PolychromeSpriteVertexOutput{ - device_position, - tile_position, - sprite_id, - {clip_distance.x, clip_distance.y, clip_distance.z, clip_distance.w}}; -} - -fragment float4 polychrome_sprite_fragment( - PolychromeSpriteFragmentInput input [[stage_in]], - constant PolychromeSprite *sprites [[buffer(SpriteInputIndex_Sprites)]], - texture2d atlas_texture [[texture(SpriteInputIndex_AtlasTexture)]], - constant RoundedClip_ScaledPixels *clips [[buffer(8)]]) { - PolychromeSprite sprite = sprites[input.sprite_id]; - float coverage = content_mask_coverage(input.position.xy, sprite.content_mask, clips); - if (coverage <= 0.0) { - return float4(0.0); - } - constexpr sampler atlas_texture_sampler(mag_filter::linear, - min_filter::linear); - float4 sample = - atlas_texture.sample(atlas_texture_sampler, input.tile_position); - float distance = - quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii); - - float4 color = sample; - if (sprite.grayscale) { - float grayscale = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b; - color.r = grayscale; - color.g = grayscale; - color.b = grayscale; - } - color.a *= sprite.opacity * min(coverage, saturate(0.5 - distance)); - return color; -} - -struct PathRasterizationVertexOutput { - float4 position [[position]]; - float2 st_position; - uint vertex_id [[flat]]; - float clip_rect_distance [[clip_distance]][4]; -}; - -struct PathRasterizationFragmentInput { - float4 position [[position]]; - float2 st_position; - uint vertex_id [[flat]]; -}; - -vertex PathRasterizationVertexOutput path_rasterization_vertex( - uint vertex_id [[vertex_id]], - constant PathRasterizationVertex *vertices [[buffer(PathRasterizationInputIndex_Vertices)]], - constant Size_DevicePixels *atlas_size [[buffer(PathRasterizationInputIndex_ViewportSize)]] -) { - PathRasterizationVertex v = vertices[vertex_id]; - float2 vertex_position = float2(v.xy_position.x, v.xy_position.y); - float4 position = float4( - vertex_position * float2(2. / atlas_size->width, -2. / atlas_size->height) + float2(-1., 1.), - 0., - 1. - ); - return PathRasterizationVertexOutput{ - position, - float2(v.st_position.x, v.st_position.y), - vertex_id, - { - v.xy_position.x - v.bounds.origin.x, - v.bounds.origin.x + v.bounds.size.width - v.xy_position.x, - v.xy_position.y - v.bounds.origin.y, - v.bounds.origin.y + v.bounds.size.height - v.xy_position.y - } - }; -} - -fragment float4 path_rasterization_fragment( - PathRasterizationFragmentInput input [[stage_in]], - constant PathRasterizationVertex *vertices [[buffer(PathRasterizationInputIndex_Vertices)]] -, - constant RoundedClip_ScaledPixels *clips [[buffer(8)]]) { - float2 dx = dfdx(input.st_position); - float2 dy = dfdy(input.st_position); - - PathRasterizationVertex v = vertices[input.vertex_id]; - float coverage = content_mask_coverage(input.position.xy, v.content_mask, clips); - if (coverage <= 0.0) { - return float4(0.0); - } - Background background = v.color; - Bounds_ScaledPixels path_bounds = v.bounds; - float alpha; - if (length(float2(dx.x, dy.x)) < 0.001) { - alpha = 1.0; - } else { - 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); - alpha = saturate(0.5 - distance); - } - - GradientColor gradient_color = prepare_fill_color( - background.tag, - background.color_space, - background.solid, - background.colors[0].color, - background.colors[1].color - ); - - float4 color = fill_color( - background, - input.position.xy, - path_bounds, - gradient_color.solid, - gradient_color.color0, - gradient_color.color1 - ); - return float4(color.rgb * color.a * alpha, alpha * color.a) * coverage; -} - -struct PathSpriteVertexOutput { - float4 position [[position]]; - float2 texture_coords; -}; - -vertex PathSpriteVertexOutput path_sprite_vertex( - uint unit_vertex_id [[vertex_id]], - uint sprite_id [[instance_id]], - constant float2 *unit_vertices [[buffer(SpriteInputIndex_Vertices)]], - constant PathSprite *sprites [[buffer(SpriteInputIndex_Sprites)]], - constant Size_DevicePixels *viewport_size [[buffer(SpriteInputIndex_ViewportSize)]] -) { - float2 unit_vertex = unit_vertices[unit_vertex_id]; - PathSprite sprite = sprites[sprite_id]; - // Don't apply content mask because it was already accounted for when - // rasterizing the path. - float4 device_position = - to_device_position(unit_vertex, sprite.bounds, viewport_size); - - float2 screen_position = float2(sprite.bounds.origin.x, sprite.bounds.origin.y) + unit_vertex * float2(sprite.bounds.size.width, sprite.bounds.size.height); - float2 texture_coords = screen_position / float2(viewport_size->width, viewport_size->height); - - return PathSpriteVertexOutput{ - device_position, - texture_coords - }; -} - -fragment float4 path_sprite_fragment( - PathSpriteVertexOutput input [[stage_in]], - texture2d intermediate_texture [[texture(SpriteInputIndex_AtlasTexture)]] -) { - constexpr sampler intermediate_texture_sampler(mag_filter::linear, min_filter::linear); - return intermediate_texture.sample(intermediate_texture_sampler, input.texture_coords); -} - -struct SurfaceVertexOutput { - float4 position [[position]]; - float2 texture_position; - float clip_distance [[clip_distance]][4]; - float4 clip_mask_bounds [[flat]]; - float4 clip_mask_radii [[flat]]; - uint clip_index [[flat]]; -}; - -struct SurfaceFragmentInput { - float4 position [[position]]; - float2 texture_position; - float4 clip_mask_bounds [[flat]]; - float4 clip_mask_radii [[flat]]; - uint clip_index [[flat]]; -}; - -vertex SurfaceVertexOutput surface_vertex( - uint unit_vertex_id [[vertex_id]], uint surface_id [[instance_id]], - constant float2 *unit_vertices [[buffer(SurfaceInputIndex_Vertices)]], - constant SurfaceBounds *surfaces [[buffer(SurfaceInputIndex_Surfaces)]], - constant Size_DevicePixels *viewport_size - [[buffer(SurfaceInputIndex_ViewportSize)]], - constant Size_DevicePixels *texture_size - [[buffer(SurfaceInputIndex_TextureSize)]]) { - float2 unit_vertex = unit_vertices[unit_vertex_id]; - SurfaceBounds surface = surfaces[surface_id]; - float4 device_position = - to_device_position(unit_vertex, surface.bounds, viewport_size); - float4 clip_distance = distance_from_clip_rect(unit_vertex, surface.bounds, - surface.content_mask); - // We are going to copy the whole texture, so the texture position corresponds - // to the current vertex of the unit triangle. - float2 texture_position = unit_vertex; - return SurfaceVertexOutput{ - device_position, - texture_position, - {clip_distance.x, clip_distance.y, clip_distance.z, clip_distance.w}, - float4(surface.content_mask.bounds.origin.x, surface.content_mask.bounds.origin.y, - surface.content_mask.bounds.size.width, surface.content_mask.bounds.size.height), - float4(surface.content_mask.corner_radii.top_left, - surface.content_mask.corner_radii.top_right, - surface.content_mask.corner_radii.bottom_right, - surface.content_mask.corner_radii.bottom_left), - surface.content_mask.clip_index}; -} - -fragment float4 surface_fragment(SurfaceFragmentInput input [[stage_in]], - texture2d y_texture - [[texture(SurfaceInputIndex_YTexture)]], - texture2d cb_cr_texture - [[texture(SurfaceInputIndex_CbCrTexture)]], - constant RoundedClip_ScaledPixels *clips [[buffer(8)]]) { - Bounds_ScaledPixels clip_bounds = Bounds_ScaledPixels{ - {input.clip_mask_bounds.x, input.clip_mask_bounds.y}, - {input.clip_mask_bounds.z, input.clip_mask_bounds.w}}; - Corners_ScaledPixels clip_radii = Corners_ScaledPixels{ - input.clip_mask_radii.x, input.clip_mask_radii.y, - input.clip_mask_radii.z, input.clip_mask_radii.w}; - ContentMask_ScaledPixels mask = {clip_bounds, clip_radii, input.clip_index, 0}; - float coverage = content_mask_coverage(input.position.xy, mask, clips); - if (coverage <= 0.0) { - return float4(0.0); - } - constexpr sampler texture_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_texture.sample(texture_sampler, input.texture_position).r, - cb_cr_texture.sample(texture_sampler, input.texture_position).rg, 1.0); - - return (ycbcrToRGBTransform * ycbcr) * float4(1., 1., 1., coverage); -} - -float4 hsla_to_rgba(Hsla hsla) { - float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range - float s = hsla.s; - float l = hsla.l; - float a = hsla.a; - - float c = (1.0 - fabs(2.0 * l - 1.0)) * s; - float x = c * (1.0 - fabs(fmod(h, 2.0) - 1.0)); - float m = l - c / 2.0; - - float r = 0.0; - float g = 0.0; - float b = 0.0; - - if (h >= 0.0 && h < 1.0) { - r = c; - g = x; - b = 0.0; - } else if (h >= 1.0 && h < 2.0) { - r = x; - g = c; - b = 0.0; - } else if (h >= 2.0 && h < 3.0) { - r = 0.0; - g = c; - b = x; - } else if (h >= 3.0 && h < 4.0) { - r = 0.0; - g = x; - b = c; - } else if (h >= 4.0 && h < 5.0) { - r = x; - g = 0.0; - b = c; - } else { - r = c; - g = 0.0; - b = x; - } - - float4 rgba; - rgba.x = (r + m); - rgba.y = (g + m); - rgba.z = (b + m); - rgba.w = a; - return rgba; -} - -float3 srgb_to_linear(float3 color) { - return pow(color, float3(2.2)); -} - -float3 linear_to_srgb(float3 color) { - return pow(color, float3(1.0 / 2.2)); -} - -// Converts a sRGB color to the Oklab color space. -// Reference: https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab -float4 srgb_to_oklab(float4 color) { - // Convert non-linear sRGB to linear sRGB - color = float4(srgb_to_linear(color.rgb), color.a); - - float l = 0.4122214708 * color.r + 0.5363325363 * color.g + 0.0514459929 * color.b; - float m = 0.2119034982 * color.r + 0.6806995451 * color.g + 0.1073969566 * color.b; - float s = 0.0883024619 * color.r + 0.2817188376 * color.g + 0.6299787005 * color.b; - - float l_ = pow(l, 1.0/3.0); - float m_ = pow(m, 1.0/3.0); - float s_ = pow(s, 1.0/3.0); - - return float4( - 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_, - 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_, - 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_, - color.a - ); -} - -// Converts an Oklab color to the sRGB color space. -float4 oklab_to_srgb(float4 color) { - float l_ = color.r + 0.3963377774 * color.g + 0.2158037573 * color.b; - float m_ = color.r - 0.1055613458 * color.g - 0.0638541728 * color.b; - float s_ = color.r - 0.0894841775 * color.g - 1.2914855480 * color.b; - - float l = l_ * l_ * l_; - float m = m_ * m_ * m_; - float s = s_ * s_ * s_; - - float3 linear_rgb = float3( - 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s, - -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s, - -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s - ); - - // Convert linear sRGB to non-linear sRGB - return float4(linear_to_srgb(linear_rgb), color.a); -} - -float4 to_device_position(float2 unit_vertex, Bounds_ScaledPixels bounds, - constant Size_DevicePixels *input_viewport_size) { - float2 position = - unit_vertex * float2(bounds.size.width, bounds.size.height) + - float2(bounds.origin.x, bounds.origin.y); - float2 viewport_size = float2((float)input_viewport_size->width, - (float)input_viewport_size->height); - float2 device_position = - position / viewport_size * float2(2., -2.) + float2(-1., 1.); - return float4(device_position, 0., 1.); -} - -float4 to_device_position_transformed(float2 unit_vertex, Bounds_ScaledPixels bounds, - TransformationMatrix transformation, - constant Size_DevicePixels *input_viewport_size) { - float2 position = - unit_vertex * float2(bounds.size.width, bounds.size.height) + - float2(bounds.origin.x, bounds.origin.y); - - // Apply the transformation matrix to the position via matrix multiplication. - float2 transformed_position = float2(0, 0); - transformed_position[0] = position[0] * transformation.rotation_scale[0][0] + position[1] * transformation.rotation_scale[0][1]; - transformed_position[1] = position[0] * transformation.rotation_scale[1][0] + position[1] * transformation.rotation_scale[1][1]; - - // Add in the translation component of the transformation matrix. - transformed_position[0] += transformation.translation[0]; - transformed_position[1] += transformation.translation[1]; - - float2 viewport_size = float2((float)input_viewport_size->width, - (float)input_viewport_size->height); - float2 device_position = - transformed_position / viewport_size * float2(2., -2.) + float2(-1., 1.); - return float4(device_position, 0., 1.); -} - - -float2 to_tile_position(float2 unit_vertex, AtlasTile tile, - constant Size_DevicePixels *atlas_size) { - float2 tile_origin = float2(tile.bounds.origin.x, tile.bounds.origin.y); - float2 tile_size = float2(tile.bounds.size.width, tile.bounds.size.height); - return (tile_origin + unit_vertex * tile_size) / - float2((float)atlas_size->width, (float)atlas_size->height); -} - -// Selects corner radius based on quadrant. -float pick_corner_radius(float2 center_to_point, Corners_ScaledPixels corner_radii) { - if (center_to_point.x < 0.) { - if (center_to_point.y < 0.) { - return corner_radii.top_left; - } else { - return corner_radii.bottom_left; - } - } else { - if (center_to_point.y < 0.) { - return corner_radii.top_right; - } else { - return corner_radii.bottom_right; - } - } -} - -// Signed distance of the point to the quad's border - positive outside the -// border, and negative inside. -float quad_sdf(float2 point, Bounds_ScaledPixels bounds, - Corners_ScaledPixels corner_radii) { - float2 half_size = float2(bounds.size.width, bounds.size.height) / 2.0; - float2 center = float2(bounds.origin.x, bounds.origin.y) + half_size; - float2 center_to_point = point - center; - float corner_radius = pick_corner_radius(center_to_point, corner_radii); - float2 corner_to_point = fabs(center_to_point) - half_size; - float2 corner_center_to_point = corner_to_point + corner_radius; - return quad_sdf_impl(corner_center_to_point, corner_radius); -} - -// Implementation of quad signed distance field -float quad_sdf_impl(float2 corner_center_to_point, float corner_radius) { - if (corner_radius == 0.0) { - // Fast path for unrounded corners - return max(corner_center_to_point.x, corner_center_to_point.y); - } else { - // Signed distance of the point from a quad that is inset by corner_radius - // It is negative inside this quad, and positive outside - float signed_distance_to_inset_quad = - // 0 inside the inset quad, and positive outside - length(max(float2(0.0), corner_center_to_point)) + - // 0 outside the inset quad, and negative inside - min(0.0, max(corner_center_to_point.x, corner_center_to_point.y)); - - return signed_distance_to_inset_quad - corner_radius; - } -} - -// 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); - float2 r1 = 1. + (0.278393 + (0.230389 + (0.000972 + 0.078108 * a) * a) * a) * a; - float2 r2 = r1 * r1; - return s - s / (r2 * r2); -} - -float blur_along_x(float x, float y, float sigma, float corner, - float2 half_size) { - float delta = min(half_size.y - corner - abs(y), 0.); - float curved = - half_size.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; -} - -float4 distance_from_clip_rect(float2 unit_vertex, Bounds_ScaledPixels bounds, - ContentMask_ScaledPixels mask) { - float2 position = - unit_vertex * float2(bounds.size.width, bounds.size.height) + - float2(bounds.origin.x, bounds.origin.y); - float4 rect_distance = float4( - position.x - mask.bounds.origin.x, - mask.bounds.origin.x + mask.bounds.size.width - position.x, - position.y - mask.bounds.origin.y, - mask.bounds.origin.y + mask.bounds.size.height - position.y); - return rect_distance; -} - -float4 distance_from_clip_rect_transformed(float2 unit_vertex, Bounds_ScaledPixels bounds, - ContentMask_ScaledPixels mask, TransformationMatrix transformation) { - float2 position = - unit_vertex * float2(bounds.size.width, bounds.size.height) + - float2(bounds.origin.x, bounds.origin.y); - float2 transformed_position = float2(0, 0); - transformed_position[0] = position[0] * transformation.rotation_scale[0][0] + position[1] * transformation.rotation_scale[0][1]; - transformed_position[1] = position[0] * transformation.rotation_scale[1][0] + position[1] * transformation.rotation_scale[1][1]; - transformed_position[0] += transformation.translation[0]; - transformed_position[1] += transformation.translation[1]; - - float4 rect_distance = float4( - transformed_position.x - mask.bounds.origin.x, - mask.bounds.origin.x + mask.bounds.size.width - transformed_position.x, - transformed_position.y - mask.bounds.origin.y, - mask.bounds.origin.y + mask.bounds.size.height - transformed_position.y); - return rect_distance; -} - -float4 over(float4 below, float4 above) { - float4 result; - float alpha = above.a + below.a * (1.0 - above.a); - result.rgb = - (above.rgb * above.a + below.rgb * below.a * (1.0 - above.a)) / alpha; - result.a = alpha; - return result; -} - -GradientColor prepare_fill_color(uint tag, uint color_space, Hsla solid, - Hsla color0, Hsla color1) { - GradientColor out; - if (tag == 0 || tag == 2 || tag == 3) { - out.solid = hsla_to_rgba(solid); - } else if (tag == 1) { - out.color0 = hsla_to_rgba(color0); - out.color1 = hsla_to_rgba(color1); - - // Prepare color space in vertex for avoid conversion - // in fragment shader for performance reasons - if (color_space == 1) { - // Oklab - out.color0 = srgb_to_oklab(out.color0); - out.color1 = srgb_to_oklab(out.color1); - } - } - - return out; -} - -float2x2 rotate2d(float angle) { - float s = sin(angle); - float c = cos(angle); - return float2x2(c, -s, s, c); -} - -float4 fill_color(Background background, - float2 position, - Bounds_ScaledPixels bounds, - float4 solid_color, float4 color0, float4 color1) { - float4 color; - - switch (background.tag) { - case 0: - color = solid_color; - break; - case 1: { - // -90 degrees to match the CSS gradient angle. - float gradient_angle = background.gradient_angle_or_pattern_height; - float radians = (fmod(gradient_angle, 360.0) - 90.0) * (M_PI_F / 180.0); - float2 direction = float2(cos(radians), sin(radians)); - - // Expand the short side to be the same as the long side - if (bounds.size.width > bounds.size.height) { - direction.y *= bounds.size.height / bounds.size.width; - } else { - direction.x *= bounds.size.width / bounds.size.height; - } - - // Get the t value for the linear gradient with the color stop percentages. - float2 half_size = float2(bounds.size.width, bounds.size.height) / 2.; - float2 center = float2(bounds.origin.x, bounds.origin.y) + half_size; - float2 center_to_point = position - center; - float t = dot(center_to_point, direction) / length(direction); - // Check the direction to determine whether to use x or y - if (abs(direction.x) > abs(direction.y)) { - t = (t + half_size.x) / bounds.size.width; - } else { - t = (t + half_size.y) / bounds.size.height; - } - - // Adjust t based on the stop percentages - t = (t - background.colors[0].percentage) - / (background.colors[1].percentage - - background.colors[0].percentage); - t = clamp(t, 0.0, 1.0); - - switch (background.color_space) { - case 0: - color = mix(color0, color1, t); - break; - case 1: { - float4 oklab_color = mix(color0, color1, t); - color = oklab_to_srgb(oklab_color); - break; - } - } - - // Dither to reduce banding in gradients (especially dark/alpha). - // Triangular-distributed noise breaks up 8-bit quantization steps. - // ±2/255 for RGB (enough for dark-on-dark compositing), - // ±3/255 for alpha (needs more because alpha × dark color = tiny steps). - { - float2 seed = position * 0.6180339887; // golden ratio spread - float r1 = fract(sin(dot(seed, float2(12.9898, 78.233))) * 43758.5453); - float r2 = fract(sin(dot(seed, float2(39.3460, 11.135))) * 24634.6345); - float tri = r1 + r2 - 1.0; // triangular PDF, range [-1, +1] - color.rgb += tri * 2.0 / 255.0; - color.a += tri * 3.0 / 255.0; - } - - break; - } - case 2: { - float gradient_angle_or_pattern_height = background.gradient_angle_or_pattern_height; - float pattern_width = (gradient_angle_or_pattern_height / 65535.0f) / 255.0f; - float pattern_interval = fmod(gradient_angle_or_pattern_height, 65535.0f) / 255.0f; - float pattern_height = pattern_width + pattern_interval; - float stripe_angle = M_PI_F / 4.0; - float pattern_period = pattern_height * sin(stripe_angle); - float2x2 rotation = rotate2d(stripe_angle); - float2 relative_position = position - float2(bounds.origin.x, bounds.origin.y); - float2 rotated_point = rotation * relative_position; - float pattern = fmod(rotated_point.x, pattern_period); - float distance = min(pattern, pattern_period - pattern) - pattern_period * (pattern_width / pattern_height) / 2.0f; - color = solid_color; - color.a *= saturate(0.5 - distance); - break; - } - case 3: { - // checkerboard - float size = background.gradient_angle_or_pattern_height; - float2 relative_position = position - float2(bounds.origin.x, bounds.origin.y); - - float x_index = floor(relative_position.x / size); - float y_index = floor(relative_position.y / size); - float should_be_colored = fmod(x_index + y_index, 2.0); - - color = solid_color; - color.a *= saturate(should_be_colored); - break; - } - } - - return color; -} - - -// Distance uses each corner's own region, including elliptical border insets. -float clip_corner_distance(float2 point, float2 radii) { - if (any(radii <= float2(0.0)) || any(point >= radii)) return -1e20; - float2 p = point - radii; - float k0 = length(p / radii); - float k1 = length(p / (radii * radii)); - if (k1 == 0.0) return -min(radii.x, radii.y); - return k0 * (k0 - 1.0) / k1; -} - -float rounded_clip_distance(float2 position, RoundedClip_ScaledPixels clip) { - float2 tl = position - float2(clip.bounds.origin.x, clip.bounds.origin.y); - float2 br = float2(clip.bounds.size.width, clip.bounds.size.height) - tl; - float distance = max(max(-tl.x, -tl.y), max(-br.x, -br.y)); - distance = max(distance, clip_corner_distance(tl, float2(clip.radii_x.top_left, clip.radii_y.top_left))); - distance = max(distance, clip_corner_distance(float2(br.x, tl.y), float2(clip.radii_x.top_right, clip.radii_y.top_right))); - distance = max(distance, clip_corner_distance(br, float2(clip.radii_x.bottom_right, clip.radii_y.bottom_right))); - distance = max(distance, clip_corner_distance(float2(tl.x, br.y), float2(clip.radii_x.bottom_left, clip.radii_y.bottom_left))); - return distance; -} - -float content_mask_coverage(float2 position, ContentMask_ScaledPixels mask, - constant RoundedClip_ScaledPixels *clips) { - float distance = -1e20; - if (mask.clip_index == 0) { - RoundedClip_ScaledPixels clip = {mask.bounds, mask.corner_radii, mask.corner_radii, 0, 0}; - distance = rounded_clip_distance(position, clip); - } else { - uint index = mask.clip_index; - do { - RoundedClip_ScaledPixels clip = clips[index - 1]; - distance = max(distance, rounded_clip_distance(position, clip)); - index = clip.parent; - } while (index != 0); - } - return saturate(0.5 - distance); -} diff --git a/crates/gpui_pre_apple/vendor/gpui/src/clip.rs b/crates/gpui_pre_apple/vendor/gpui/src/clip.rs deleted file mode 100644 index 4bb79d9d..00000000 --- a/crates/gpui_pre_apple/vendor/gpui/src/clip.rs +++ /dev/null @@ -1,188 +0,0 @@ -//! Rounded clipping keeps each ancestor's geometry separate from culling bounds. - -use crate::{px, Bounds, ContentMask, Corners, Pixels, Point, ScaledPixels}; -use smallvec::SmallVec; -use std::fmt::Debug; - -/// One immutable rounded rectangle in a scene's clip chain. -/// Separate horizontal and vertical radii preserve corners inset by unequal borders. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -#[repr(C)] -pub struct RoundedClip { - /// Original geometry, never replaced by an intersection's bounding box. - pub bounds: Bounds

D<*l8k7GLmfb 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�Sa+?ULCmQ6G|d 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