Compare commits

...
Sign in to create a new pull request.

42 commits

Author SHA1 Message Date
Antonio Scandurra
ce45e1cb4c Panic
- Go to diagnostics view
- Select all occurrences of x_for_display_point via cmd-d
- Park the cursors at the start of the parenthesis
- Hit ctrl-m
2024-03-04 20:11:40 +01:00
Antonio Scandurra
78aaaa8b1e WIP 2024-03-04 19:16:29 +01:00
Antonio Scandurra
2045c83099 Move defer_draw to ElementContext 2024-03-04 15:59:25 +01:00
Antonio Scandurra
f57f6344b4 Introduce AnyElement::deferred_draw and use it in Overlay 2024-03-04 14:33:48 +01:00
Antonio Scandurra
022fada0f2 Fix registration of mouse event listeners by using hitbox 2024-03-02 19:19:12 +01:00
Antonio Scandurra
ba45a04045 Move element states for reused views correctly 2024-03-02 14:08:36 +01:00
Antonio Scandurra
f52d9f8c24 Checkpoint
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
2024-03-01 19:15:08 +01:00
Antonio Scandurra
8847f73af5 WIP 2024-03-01 18:21:34 +01:00
Antonio Scandurra
3a7b4de0f5 WIP 2024-03-01 17:12:12 +01:00
Antonio Scandurra
82ec8405d6 WIP 2024-03-01 16:19:33 +01:00
Antonio Scandurra
7b7a046b94 Make occlusion optional in Interactivity 2024-03-01 15:48:54 +01:00
Antonio Scandurra
1ba395a33a Checkpoint
Co-Authored-By: Thorsten <thorsten@zed.dev>
2024-03-01 11:41:52 +01:00
Nathan Sobo
a165e997ba WIP: Compare occlusion ids instead of bounds in mouse listeners 2024-02-29 22:34:17 -07:00
Nathan Sobo
8444b11e76 Checkpoint 2024-02-29 20:31:12 -07:00
Nathan Sobo
609370f9d6 Merge branch 'fix-todo-comments' into fix-hover 2024-02-29 18:15:51 -07:00
Nathan Sobo
2108c764ad Remove ! from todo!() in comments
This practice makes it difficult to locate todo!s in my code when I'm working.
Let's take out the bang if we want to keep doing this.
2024-02-29 17:42:24 -07:00
Nathan Sobo
bdedeab7af Get GPUI compiling with some todos 2024-02-29 17:34:36 -07:00
Antonio Scandurra
542fb5c89a WIP 2024-02-29 18:00:03 +01:00
Antonio Scandurra
0fde56909c WIP 2024-02-29 16:27:19 +01:00
Antonio Scandurra
d75ef8e62d Simplify control flow in view caching 2024-02-29 10:17:11 +01:00
Antonio Scandurra
f5cd8247d1 WIP 2024-02-28 19:59:49 +01:00
Antonio Scandurra
821960bf14 Rename commit_root to layout
Co-Authored-By: Nathan <nathan@zed.dev>
2024-02-28 19:27:12 +01:00
Antonio Scandurra
0ab1e6f451 WIP 2024-02-28 14:29:59 +01:00
Nathan Sobo
17d736b23d WIP: Move constructor of cursor names to after_layout 2024-02-27 21:18:09 -07:00
Nathan Sobo
6006b171f7 WIP 2024-02-27 20:26:12 -07:00
Nathan Sobo
5790d9ba27 WIP: Rename layout phase methods and give each a state
Shred mode. I don't want to force things to be optional just to account for both phases though.
2024-02-27 20:08:35 -07:00
Nathan Sobo
d5766dc69f Rename element methods 2024-02-27 13:30:40 -07:00
Nathan Sobo
3e5c11dc41 Eliminate with_z_index 2024-02-27 13:19:29 -07:00
Antonio Scandurra
ed1a256f99 Add a new commit_root method 2024-02-27 19:42:15 +01:00
Antonio Scandurra
28e795f2fd WIP: start removing draw as it won't work with the two passes 2024-02-27 18:14:56 +01:00
Antonio Scandurra
98374a70d3 Introduce a new Element::commit_bounds method 2024-02-27 16:09:32 +01:00
Antonio Scandurra
33abbcb535 Simplify DrawableElement and expose AnyElement::downcast_mut 2024-02-27 14:01:44 +01:00
Antonio Scandurra
7481c0d556 Remove Element::element_id method from trait 2024-02-27 13:52:57 +01:00
Antonio Scandurra
a1d5249b8e Fix regression in Div and simplify painting 2024-02-27 13:39:07 +01:00
Antonio Scandurra
21bde9b653 💄 2024-02-27 11:07:39 +01:00
Antonio Scandurra
5183dbb5be Fix remaining compile errors and runtime panics 2024-02-27 10:33:20 +01:00
Nathan Sobo
7c6e6971da WIP: Separate ElementState from FrameState 2024-02-26 23:16:23 -07:00
Nathan Sobo
81b5c74b0e Rename Element::State -> FrameState 2024-02-26 20:42:03 -07:00
Nathan Sobo
47e48e20d3 Checkpoint 2024-02-26 20:27:57 -07:00
Nathan Sobo
4584c5e2b3 WIP 2024-02-26 17:46:56 -07:00
Nathan Sobo
e07c53c27f Checkpoint 2024-02-26 15:44:07 -07:00
Nathan Sobo
e917d1d251 Cherry-pick BoundsTree from a previous attempt 2024-02-26 15:35:09 -07:00
89 changed files with 5566 additions and 5242 deletions

View file

@ -99,7 +99,7 @@ jobs:
- name: Build other binaries and features - name: Build other binaries and features
run: cargo build --workspace --bins --all-features; cargo check -p gpui --features "macos-blade" run: cargo build --workspace --bins --all-features; cargo check -p gpui --features "macos-blade"
# todo!(linux): Actually run the tests # todo(linux): Actually run the tests
linux_tests: linux_tests:
name: (Linux) Run Clippy and tests name: (Linux) Run Clippy and tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -126,7 +126,7 @@ jobs:
- name: Build Zed - name: Build Zed
run: cargo build -p zed run: cargo build -p zed
# todo!(windows): Actually run the tests # todo(windows): Actually run the tests
windows_tests: windows_tests:
name: (Windows) Run Clippy and tests name: (Windows) Run Clippy and tests
runs-on: windows-latest runs-on: windows-latest

114
Cargo.lock generated
View file

@ -404,9 +404,9 @@ dependencies = [
[[package]] [[package]]
name = "async-compression" name = "async-compression"
version = "0.4.6" version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a116f46a969224200a0a97f29cfd4c50e7534e4b4826bd23ea2c3c533039c82c" checksum = "bc2d0cfb2a7388d34f590e76686704c494ed7aaceed62ee1ba35cbf363abc2a5"
dependencies = [ dependencies = [
"flate2", "flate2",
"futures-core", "futures-core",
@ -681,9 +681,9 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799"
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.77" version = "0.1.73"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2447,18 +2447,18 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-bforest" name = "cranelift-bforest"
version = "0.105.1" version = "0.105.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29a6391a9172a93f413370fa561c6bca786e06c89cf85f23f02f6345b1c8ee34" checksum = "9515fcc42b6cb5137f76b84c1a6f819782d0cf12473d145d3bc5cd67eedc8bc2"
dependencies = [ dependencies = [
"cranelift-entity", "cranelift-entity",
] ]
[[package]] [[package]]
name = "cranelift-codegen" name = "cranelift-codegen"
version = "0.105.1" version = "0.105.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "409c6cbb326604a53ec47eb6341fc85128f24c81012a014b4c728ed24f6e9350" checksum = "1ad827c6071bfe6d22de1bc331296a29f9ddc506ff926d8415b435ec6a6efce0"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"cranelift-bforest", "cranelift-bforest",
@ -2477,33 +2477,33 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-codegen-meta" name = "cranelift-codegen-meta"
version = "0.105.1" version = "0.105.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fff55e100130995b9ad9ac6b03a24ed5da3c1a1261dcdeb8a7a0292656994fb3" checksum = "10e6b36237a9ca2ce2fb4cc7741d418a080afa1327402138412ef85d5367bef1"
dependencies = [ dependencies = [
"cranelift-codegen-shared", "cranelift-codegen-shared",
] ]
[[package]] [[package]]
name = "cranelift-codegen-shared" name = "cranelift-codegen-shared"
version = "0.105.1" version = "0.105.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1446e2eb395fc7b3019a36dccb7eccea923f6caf581b903c8e7e751b6d214a7" checksum = "c36bf4bfb86898a94ccfa773a1f86e8a5346b1983ff72059bdd2db4600325251"
[[package]] [[package]]
name = "cranelift-control" name = "cranelift-control"
version = "0.105.1" version = "0.105.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24076ecf69cbf8b9e1e532ae8e7ac01d850a1c2e127058a26eb3245f9d5b89d1" checksum = "7cbf36560e7a6bd1409ca91e7b43b2cc7ed8429f343d7605eadf9046e8fac0d0"
dependencies = [ dependencies = [
"arbitrary", "arbitrary",
] ]
[[package]] [[package]]
name = "cranelift-entity" name = "cranelift-entity"
version = "0.105.1" version = "0.105.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f40df95180ad317c60459bb90dd87803d35e538f4c54376d8b26c851f6f0a1b" checksum = "a71e11061a75b1184c09bea97c026a88f08b59ade96a7bb1f259d4ea0df2e942"
dependencies = [ dependencies = [
"serde", "serde",
"serde_derive", "serde_derive",
@ -2511,9 +2511,9 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-frontend" name = "cranelift-frontend"
version = "0.105.1" version = "0.105.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c3974cc665b699b626742775dae1c1cdea5170f5028ab1f3eb61a7a9a6e2979" checksum = "af5d4da63143ee3485c7bcedde0a818727d737d1083484a0ceedb8950c89e495"
dependencies = [ dependencies = [
"cranelift-codegen", "cranelift-codegen",
"log", "log",
@ -2523,15 +2523,15 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-isle" name = "cranelift-isle"
version = "0.105.1" version = "0.105.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99543f92b9c361f3c54a29e945adb5b9ef1318feaa5944453cabbfcb3c495919" checksum = "457a9832b089e26f5eea70dcf49bed8ec6edafed630ce7c83161f24d46ab8085"
[[package]] [[package]]
name = "cranelift-native" name = "cranelift-native"
version = "0.105.1" version = "0.105.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c0d84dc7d9b3f73ad565eacc4ab36525c407ef5150893b4b94d5f5f904eb48a" checksum = "9b490d579df1ce365e1ea359e24ed86d82289fa785153327c2f6a69a59a731e4"
dependencies = [ dependencies = [
"cranelift-codegen", "cranelift-codegen",
"libc", "libc",
@ -2540,9 +2540,9 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-wasm" name = "cranelift-wasm"
version = "0.105.1" version = "0.105.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53781039219944d59c6d3ec57e6cae31a1a33db71573a945d84ba6d875d0a743" checksum = "8cd747ed7f9a461dda9c388415392f6bb95d1a6ef3b7694d17e0817eb74b7798"
dependencies = [ dependencies = [
"cranelift-codegen", "cranelift-codegen",
"cranelift-entity", "cranelift-entity",
@ -7599,9 +7599,9 @@ dependencies = [
[[package]] [[package]]
name = "rust-embed" name = "rust-embed"
version = "8.2.0" version = "8.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a82c0bbc10308ed323529fd3c1dce8badda635aa319a5ff0e6466f33b8101e3f" checksum = "fb78f46d0066053d16d4ca7b898e9343bc3530f71c61d5ad84cd404ada068745"
dependencies = [ dependencies = [
"rust-embed-impl", "rust-embed-impl",
"rust-embed-utils", "rust-embed-utils",
@ -7610,9 +7610,9 @@ dependencies = [
[[package]] [[package]]
name = "rust-embed-impl" name = "rust-embed-impl"
version = "8.2.0" version = "8.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6227c01b1783cdfee1bcf844eb44594cd16ec71c35305bf1c9fb5aade2735e16" checksum = "b91ac2a3c6c0520a3fb3dd89321177c3c692937c4eb21893378219da10c44fc8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -7623,9 +7623,9 @@ dependencies = [
[[package]] [[package]]
name = "rust-embed-utils" name = "rust-embed-utils"
version = "8.2.0" version = "8.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cb0a25bfbb2d4b4402179c2cf030387d9990857ce08a32592c6238db9fa8665" checksum = "86f69089032567ffff4eada41c573fc43ff466c7db7c5688b2e7969584345581"
dependencies = [ dependencies = [
"globset", "globset",
"sha2 0.10.7", "sha2 0.10.7",
@ -8346,9 +8346,9 @@ dependencies = [
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
[[package]] [[package]]
name = "signal-hook" name = "signal-hook"
@ -9922,7 +9922,7 @@ dependencies = [
[[package]] [[package]]
name = "tree-sitter-gitcommit" name = "tree-sitter-gitcommit"
version = "0.3.3" version = "0.3.3"
source = "git+https://github.com/gbprod/tree-sitter-gitcommit#7c01af8d227b5344f62aade2ff00f19bd0c458ca" source = "git+https://github.com/gbprod/tree-sitter-gitcommit#e8d9eda4e5ea0b08aa39d48dab0f6553058fbe0f"
dependencies = [ dependencies = [
"cc", "cc",
"tree-sitter", "tree-sitter",
@ -10798,9 +10798,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime" name = "wasmtime"
version = "18.0.1" version = "18.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06f80b13fdeba0ea5267813d0f06af822309f7125fc8db6094bcd485f0a4ae7" checksum = "4c843b8bc4dd4f3a76173ba93405c71111d570af0d90ea5f6299c705d0c2add2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -10828,18 +10828,18 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-asm-macros" name = "wasmtime-asm-macros"
version = "18.0.1" version = "18.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d7395b475c6f858c7edfce375f00d8282a32fbf5d1ebc93eddfac5c2458a52" checksum = "86b9d329c718b3a18412a6a017c912b539baa8fe1210d21b651f6b4dbafed743"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
] ]
[[package]] [[package]]
name = "wasmtime-c-api-impl" name = "wasmtime-c-api-impl"
version = "18.0.1" version = "18.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c09ac0c18464f8ef0b554c12defc94e3fc082b62309a3da229de60d47cf75a" checksum = "cc93587c24d8e3cb28912eb7abf95f7e350380656faccc46cff04c0821ec58c2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"log", "log",
@ -10851,9 +10851,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-c-api-macros" name = "wasmtime-c-api-macros"
version = "18.0.1" version = "18.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864c4a337294fe690f02b39f2b3f45414447d9321d0ed24d3dc7696bf291e789" checksum = "2e571a71eba52dfe81ef653a3a336888141f00fc2208a9962722e036fe2a34be"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -10861,9 +10861,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-cranelift" name = "wasmtime-cranelift"
version = "18.0.1" version = "18.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "974d9455611e26c97d31705e19545de58fa8867416592bd93b7a54a7fc37cedb" checksum = "31ca62f519225492bd555d0ec85a2dacb0c10315db3418c8b9aeb3824bf54a24"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cfg-if 1.0.0", "cfg-if 1.0.0",
@ -10886,9 +10886,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-cranelift-shared" name = "wasmtime-cranelift-shared"
version = "18.0.1" version = "18.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40667ba458634db703aea3bd960e80bc9352c21d5e765b69f43e3b0c964eb611" checksum = "fd5f2071f42e61490bf7cb95b9acdbe6a29dd577a398019304a960585f28b844"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cranelift-codegen", "cranelift-codegen",
@ -10902,9 +10902,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-environ" name = "wasmtime-environ"
version = "18.0.1" version = "18.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8da991421528c2767053cb0cfa70b5d28279100dbcf70ed7f74b51abe1656ef" checksum = "82bf1a47f384610da19f58b0fd392ca6a3b720974315c08afb0392c0f3951fed"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -10923,9 +10923,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-jit-icache-coherence" name = "wasmtime-jit-icache-coherence"
version = "18.0.1" version = "18.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3346431a41fbb0c5af0081c2322361b00289f2902e54ee7b115e9b2ad32b156b" checksum = "33f4121cb29dda08139b2824a734dd095d83ce843f2d613a84eb580b9cfc17ac"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"libc", "libc",
@ -10934,9 +10934,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-runtime" name = "wasmtime-runtime"
version = "18.0.1" version = "18.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a489353aa297b46a66cde8da48cab8e1e967e7f4b0ae3d9889a0550bf274810b" checksum = "4e517f2b996bb3b0e34a82a2bce194f850d9bcfc25c08328ef5fb71b071066b8"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cc", "cc",
@ -10961,9 +10961,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-types" name = "wasmtime-types"
version = "18.0.1" version = "18.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12c56e31fd7fa707fbd7720b2b29ac42ccfb092fe9d85c98f1d3988f9a1d4558" checksum = "54a327d7a0ef57bd52a507d28b4561a74126c7a8535a2fc6f2025716bc6a52e8"
dependencies = [ dependencies = [
"cranelift-entity", "cranelift-entity",
"serde", "serde",
@ -10974,9 +10974,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-versioned-export-macros" name = "wasmtime-versioned-export-macros"
version = "18.0.1" version = "18.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b0300976c36a9427d184e3ecf7c121c2cb3f030844faf9fcb767821e9d4c382" checksum = "8ef32eea9fc7035a55159a679d1e89b43ece5ae45d24eed4808e6a92c99a0da4"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -10985,9 +10985,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-wmemcheck" name = "wasmtime-wmemcheck"
version = "18.0.1" version = "18.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdf5b8da6ebf7549dad0cd32ca4a3a0461449ef4feec9d0d8450d8da9f51f9b" checksum = "7f4cbfb052d66f03603a9b77f18171ea245c7805714caad370a549a6344bf86b"
[[package]] [[package]]
name = "wayland-backend" name = "wayland-backend"

View file

@ -310,7 +310,7 @@ pathfinder_simd = { git = "https://github.com/servo/pathfinder.git", rev = "e4fc
split-debuginfo = "unpacked" split-debuginfo = "unpacked"
debug = "limited" debug = "limited"
# todo!(linux) - Remove this # todo(linux) - Remove this
[profile.dev.package.blade-graphics] [profile.dev.package.blade-graphics]
split-debuginfo = "off" split-debuginfo = "off"
debug = "full" debug = "full"

View file

@ -73,7 +73,7 @@
"ctrl-n": "editor::MoveDown", "ctrl-n": "editor::MoveDown",
"ctrl-b": "editor::MoveLeft", "ctrl-b": "editor::MoveLeft",
"ctrl-f": "editor::MoveRight", "ctrl-f": "editor::MoveRight",
"ctrl-shift-l": "editor::NextScreen", // todo!(linux): What is this "ctrl-shift-l": "editor::NextScreen", // todo(linux): What is this
"alt-left": "editor::MoveToPreviousWordStart", "alt-left": "editor::MoveToPreviousWordStart",
"alt-b": "editor::MoveToPreviousWordStart", "alt-b": "editor::MoveToPreviousWordStart",
"alt-right": "editor::MoveToNextWordEnd", "alt-right": "editor::MoveToNextWordEnd",
@ -388,7 +388,7 @@
} }
}, },
// Bindings from Sublime Text // Bindings from Sublime Text
// todo!(linux) make sure these match linux bindings or remove above comment? // todo(linux) make sure these match linux bindings or remove above comment?
{ {
"context": "Editor", "context": "Editor",
"bindings": { "bindings": {
@ -412,7 +412,7 @@
} }
}, },
// Bindings from Atom // Bindings from Atom
// todo!(linux) make sure these match linux bindings or remove above comment? // todo(linux) make sure these match linux bindings or remove above comment?
{ {
"context": "Pane", "context": "Pane",
"bindings": { "bindings": {

View file

@ -31,9 +31,9 @@ use fs::Fs;
use futures::StreamExt; use futures::StreamExt;
use gpui::{ use gpui::{
canvas, div, point, relative, rems, uniform_list, Action, AnyElement, AppContext, canvas, div, point, relative, rems, uniform_list, Action, AnyElement, AppContext,
AsyncAppContext, AsyncWindowContext, AvailableSpace, ClipboardItem, Context, EventEmitter, AsyncAppContext, AsyncWindowContext, ClipboardItem, Context, EventEmitter, FocusHandle,
FocusHandle, FocusableView, FontStyle, FontWeight, HighlightStyle, InteractiveElement, FocusableView, FontStyle, FontWeight, HighlightStyle, InteractiveElement, IntoElement, Model,
IntoElement, Model, ModelContext, ParentElement, Pixels, PromptLevel, Render, SharedString, ModelContext, ParentElement, Pixels, PromptLevel, Render, SharedString,
StatefulInteractiveElement, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, StatefulInteractiveElement, Styled, Subscription, Task, TextStyle, UniformListScrollHandle,
View, ViewContext, VisualContext, WeakModel, WeakView, WhiteSpace, WindowContext, View, ViewContext, VisualContext, WeakModel, WeakView, WhiteSpace, WindowContext,
}; };
@ -774,7 +774,7 @@ impl AssistantPanel {
} else { } else {
editor.highlight_background::<PendingInlineAssist>( editor.highlight_background::<PendingInlineAssist>(
background_ranges, background_ranges,
|theme| theme.editor_active_line_background, // todo!("use the appropriate color") |theme| theme.editor_active_line_background, // todo("use the appropriate color")
cx, cx,
); );
} }
@ -1277,25 +1277,25 @@ impl Render for AssistantPanel {
let view = cx.view().clone(); let view = cx.view().clone();
let scroll_handle = self.saved_conversations_scroll_handle.clone(); let scroll_handle = self.saved_conversations_scroll_handle.clone();
let conversation_count = self.saved_conversations.len(); let conversation_count = self.saved_conversations.len();
canvas(move |bounds, cx| { canvas(
uniform_list( move |bounds, cx| {
view, let mut list = uniform_list(
"saved_conversations", view,
conversation_count, "saved_conversations",
|this, range, cx| { conversation_count,
range |this, range, cx| {
.map(|ix| this.render_saved_conversation(ix, cx)) range
.collect() .map(|ix| this.render_saved_conversation(ix, cx))
}, .collect()
) },
.track_scroll(scroll_handle) )
.into_any_element() .track_scroll(scroll_handle)
.draw( .into_any_element();
bounds.origin, list.layout(bounds.origin, bounds.size.into(), cx);
bounds.size.map(AvailableSpace::Definite), list
cx, },
); |_bounds, mut list, cx| list.paint(cx),
}) )
.size_full() .size_full()
.into_any_element() .into_any_element()
}), }),

View file

@ -156,7 +156,7 @@ mod linux {
} }
} }
// todo!("windows") // todo("windows")
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
mod windows { mod windows {
use std::path::Path; use std::path::Path;

View file

@ -130,7 +130,7 @@ async fn main() -> Result<()> {
}) })
.await?; .await?;
// todo!("windows") // todo("windows")
#[cfg(windows)] #[cfg(windows)]
unimplemented!(); unimplemented!();
} }

View file

@ -551,7 +551,6 @@ impl ChatPanel {
.child( .child(
div() div()
.absolute() .absolute()
.z_index(1)
.right_0() .right_0()
.w_6() .w_6()
.bg(background) .bg(background)
@ -788,7 +787,7 @@ impl Render for ChatPanel {
.size_full() .size_full()
.on_action(cx.listener(Self::send)) .on_action(cx.listener(Self::send))
.child( .child(
h_flex().z_index(1).child( h_flex().child(
TabBar::new("chat_header").child( TabBar::new("chat_header").child(
h_flex() h_flex()
.w_full() .w_full()

View file

@ -993,7 +993,6 @@ impl CollabPanel {
.children(has_channel_buffer_changed.then(|| { .children(has_channel_buffer_changed.then(|| {
div() div()
.w_1p5() .w_1p5()
.z_index(1)
.absolute() .absolute()
.right(px(2.)) .right(px(2.))
.top(px(2.)) .top(px(2.))
@ -1026,7 +1025,6 @@ impl CollabPanel {
.children(has_messages_notification.then(|| { .children(has_messages_notification.then(|| {
div() div()
.w_1p5() .w_1p5()
.z_index(1)
.absolute() .absolute()
.right(px(2.)) .right(px(2.))
.top(px(4.)) .top(px(4.))
@ -1052,7 +1050,7 @@ impl CollabPanel {
.indent_step_size(px(20.)) .indent_step_size(px(20.))
.selected(is_selected) .selected(is_selected)
.on_click(cx.listener(move |_this, _, _cx| { .on_click(cx.listener(move |_this, _, _cx| {
// todo!() // todo()
})) }))
.start_slot( .start_slot(
h_flex() h_flex()
@ -1531,7 +1529,7 @@ impl CollabPanel {
id: _id, id: _id,
name: _name, name: _name,
} => { } => {
// todo!() // todo()
} }
ListEntry::OutgoingRequest(_) => {} ListEntry::OutgoingRequest(_) => {}
@ -2614,7 +2612,6 @@ impl CollabPanel {
.children(has_notes_notification.then(|| { .children(has_notes_notification.then(|| {
div() div()
.w_1p5() .w_1p5()
.z_index(1)
.absolute() .absolute()
.right(px(-1.)) .right(px(-1.))
.top(px(-1.)) .top(px(-1.))
@ -2629,49 +2626,44 @@ impl CollabPanel {
), ),
) )
.child( .child(
h_flex() h_flex().absolute().right(rems(0.)).h_full().child(
.absolute() h_flex()
.right(rems(0.)) .h_full()
.z_index(1) .gap_1()
.h_full() .px_1()
.child( .child(
h_flex() IconButton::new("channel_chat", IconName::MessageBubbles)
.h_full() .style(ButtonStyle::Filled)
.gap_1() .shape(ui::IconButtonShape::Square)
.px_1() .icon_size(IconSize::Small)
.child( .icon_color(if has_messages_notification {
IconButton::new("channel_chat", IconName::MessageBubbles) Color::Default
.style(ButtonStyle::Filled) } else {
.shape(ui::IconButtonShape::Square) Color::Muted
.icon_size(IconSize::Small) })
.icon_color(if has_messages_notification { .on_click(cx.listener(move |this, _, cx| {
Color::Default this.join_channel_chat(channel_id, cx)
} else { }))
Color::Muted .tooltip(|cx| Tooltip::text("Open channel chat", cx))
}) .visible_on_hover(""),
.on_click(cx.listener(move |this, _, cx| { )
this.join_channel_chat(channel_id, cx) .child(
})) IconButton::new("channel_notes", IconName::File)
.tooltip(|cx| Tooltip::text("Open channel chat", cx)) .style(ButtonStyle::Filled)
.visible_on_hover(""), .shape(ui::IconButtonShape::Square)
) .icon_size(IconSize::Small)
.child( .icon_color(if has_notes_notification {
IconButton::new("channel_notes", IconName::File) Color::Default
.style(ButtonStyle::Filled) } else {
.shape(ui::IconButtonShape::Square) Color::Muted
.icon_size(IconSize::Small) })
.icon_color(if has_notes_notification { .on_click(cx.listener(move |this, _, cx| {
Color::Default this.open_channel_notes(channel_id, cx)
} else { }))
Color::Muted .tooltip(|cx| Tooltip::text("Open channel notes", cx))
}) .visible_on_hover(""),
.on_click(cx.listener(move |this, _, cx| { ),
this.open_channel_notes(channel_id, cx) ),
}))
.tooltip(|cx| Tooltip::text("Open channel notes", cx))
.visible_on_hover(""),
),
),
) )
.tooltip({ .tooltip({
let channel_store = self.channel_store.clone(); let channel_store = self.channel_store.clone();
@ -2717,31 +2709,34 @@ fn render_tree_branch(is_last: bool, overdraw: bool, cx: &mut WindowContext) ->
let thickness = px(1.); let thickness = px(1.);
let color = cx.theme().colors().text; let color = cx.theme().colors().text;
canvas(move |bounds, cx| { canvas(
let start_x = (bounds.left() + bounds.right() - thickness) / 2.; |_, _| {},
let start_y = (bounds.top() + bounds.bottom() - thickness) / 2.; move |bounds, _, cx| {
let right = bounds.right(); let start_x = (bounds.left() + bounds.right() - thickness) / 2.;
let top = bounds.top(); let start_y = (bounds.top() + bounds.bottom() - thickness) / 2.;
let right = bounds.right();
let top = bounds.top();
cx.paint_quad(fill( cx.paint_quad(fill(
Bounds::from_corners( Bounds::from_corners(
point(start_x, top), point(start_x, top),
point( point(
start_x + thickness, start_x + thickness,
if is_last { if is_last {
start_y start_y
} else { } else {
bounds.bottom() + if overdraw { px(1.) } else { px(0.) } bounds.bottom() + if overdraw { px(1.) } else { px(0.) }
}, },
),
), ),
), color,
color, ));
)); cx.paint_quad(fill(
cx.paint_quad(fill( Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)), color,
color, ));
)); },
}) )
.w(width) .w(width)
.h(line_height) .h(line_height)
} }

View file

@ -329,24 +329,27 @@ impl Render for CollabTitlebarItem {
} }
} }
fn render_color_ribbon(color: Hsla) -> gpui::Canvas { fn render_color_ribbon(color: Hsla) -> impl Element {
canvas(move |bounds, cx| { canvas(
let height = bounds.size.height; move |_, _| {},
let horizontal_offset = height; move |bounds, _, cx| {
let vertical_offset = px(height.0 / 2.0); let height = bounds.size.height;
let mut path = Path::new(bounds.lower_left()); let horizontal_offset = height;
path.curve_to( let vertical_offset = px(height.0 / 2.0);
bounds.origin + point(horizontal_offset, vertical_offset), let mut path = Path::new(bounds.lower_left());
bounds.origin + point(px(0.0), vertical_offset), path.curve_to(
); bounds.origin + point(horizontal_offset, vertical_offset),
path.line_to(bounds.upper_right() + point(-horizontal_offset, vertical_offset)); bounds.origin + point(px(0.0), vertical_offset),
path.curve_to( );
bounds.lower_right(), path.line_to(bounds.upper_right() + point(-horizontal_offset, vertical_offset));
bounds.upper_right() + point(px(0.0), vertical_offset), path.curve_to(
); bounds.lower_right(),
path.line_to(bounds.lower_left()); bounds.upper_right() + point(px(0.0), vertical_offset),
cx.paint_path(path, color); );
}) path.line_to(bounds.lower_left());
cx.paint_path(path, color);
},
)
.h_1() .h_1()
.w_full() .w_full()
} }

View file

@ -27,10 +27,7 @@ impl RenderOnce for FacePile {
let player_list = self.faces.into_iter().enumerate().map(|(ix, player)| { let player_list = self.faces.into_iter().enumerate().map(|(ix, player)| {
let isnt_last = ix < player_count - 1; let isnt_last = ix < player_count - 1;
div() div().when(isnt_last, |div| div.neg_mr_1()).child(player)
.z_index((player_count - ix) as u16)
.when(isnt_last, |div| div.neg_mr_1())
.child(player)
}); });
self.base.children(player_list) self.base.children(player_list)
} }

View file

@ -197,7 +197,7 @@ pub struct ColorStates {
/// Returns a set of colors for different states of an element. /// Returns a set of colors for different states of an element.
/// ///
/// todo!("This should take a theme and use appropriate colors from it") /// todo("This should take a theme and use appropriate colors from it")
pub fn states_for_color(color: RGBAColor, is_light: bool) -> ColorStates { pub fn states_for_color(color: RGBAColor, is_light: bool) -> ColorStates {
let adjustment_factor = if is_light { 0.1 } else { -0.1 }; let adjustment_factor = if is_light { 0.1 } else { -0.1 };
let hover_adjustment = 1.0 - adjustment_factor; let hover_adjustment = 1.0 - adjustment_factor;

View file

@ -1593,20 +1593,18 @@ mod tests {
let name: SharedString = match block { let name: SharedString = match block {
TransformBlock::Custom(block) => cx.with_element_context({ TransformBlock::Custom(block) => cx.with_element_context({
|cx| -> Option<SharedString> { |cx| -> Option<SharedString> {
block let mut element = block.render(&mut BlockContext {
.render(&mut BlockContext { context: cx,
context: cx, anchor_x: px(0.),
anchor_x: px(0.), gutter_dimensions: &GutterDimensions::default(),
gutter_dimensions: &GutterDimensions::default(), line_height: px(0.),
line_height: px(0.), em_width: px(0.),
em_width: px(0.), max_width: px(0.),
max_width: px(0.), block_id: ix,
block_id: ix, editor_style: &editor::EditorStyle::default(),
editor_style: &editor::EditorStyle::default(), });
}) let element = element.downcast_mut::<Div>().unwrap();
.inner_id()? element.interactivity().element_id.clone()?.try_into().ok()
.try_into()
.ok()
} }
})?, })?,

View file

@ -28,7 +28,10 @@ use crate::{hover_links::InlayHighlight, movement::TextLayoutDetails, InlayId};
pub use block_map::{BlockMap, BlockPoint}; pub use block_map::{BlockMap, BlockPoint};
use collections::{BTreeMap, HashMap, HashSet}; use collections::{BTreeMap, HashMap, HashSet};
use fold_map::FoldMap; use fold_map::FoldMap;
use gpui::{Font, HighlightStyle, Hsla, LineLayout, Model, ModelContext, Pixels, UnderlineStyle}; use gpui::{
Font, HighlightStyle, Hsla, LineLayout, Model, ModelContext, Pixels, UnderlineStyle,
WindowContext,
};
use inlay_map::InlayMap; use inlay_map::InlayMap;
use language::{ use language::{
language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription, language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
@ -593,13 +596,13 @@ impl DisplaySnapshot {
&self, &self,
display_row: u32, display_row: u32,
TextLayoutDetails { TextLayoutDetails {
text_system,
editor_style, editor_style,
rem_size, rem_size,
scroll_anchor: _, scroll_anchor: _,
visible_rows: _, visible_rows: _,
vertical_scroll_margin: _, vertical_scroll_margin: _,
}: &TextLayoutDetails, }: &TextLayoutDetails,
cx: &WindowContext,
) -> Arc<LineLayout> { ) -> Arc<LineLayout> {
let mut runs = Vec::new(); let mut runs = Vec::new();
let mut line = String::new(); let mut line = String::new();
@ -628,7 +631,7 @@ impl DisplaySnapshot {
} }
let font_size = editor_style.text.font_size.to_pixels(*rem_size); let font_size = editor_style.text.font_size.to_pixels(*rem_size);
text_system cx.text_system()
.layout_line(&line, font_size, &runs) .layout_line(&line, font_size, &runs)
.expect("we expect the font to be loaded because it's rendered by the editor") .expect("we expect the font to be loaded because it's rendered by the editor")
} }
@ -637,8 +640,9 @@ impl DisplaySnapshot {
&self, &self,
display_point: DisplayPoint, display_point: DisplayPoint,
text_layout_details: &TextLayoutDetails, text_layout_details: &TextLayoutDetails,
cx: &WindowContext,
) -> Pixels { ) -> Pixels {
let line = self.layout_row(display_point.row(), text_layout_details); let line = self.layout_row(display_point.row(), text_layout_details, cx);
line.x_for_index(display_point.column() as usize) line.x_for_index(display_point.column() as usize)
} }
@ -647,8 +651,9 @@ impl DisplaySnapshot {
display_row: u32, display_row: u32,
x: Pixels, x: Pixels,
details: &TextLayoutDetails, details: &TextLayoutDetails,
cx: &WindowContext,
) -> u32 { ) -> u32 {
let layout_line = self.layout_row(display_row, details); let layout_line = self.layout_row(display_row, details, cx);
layout_line.closest_index_for_x(x) as u32 layout_line.closest_index_for_x(x) as u32
} }
@ -1336,7 +1341,8 @@ pub mod tests {
DisplayPoint::new(0, 7) DisplayPoint::new(0, 7)
); );
let x = snapshot.x_for_display_point(DisplayPoint::new(1, 10), &text_layout_details); let x =
snapshot.x_for_display_point(DisplayPoint::new(1, 10), &text_layout_details, cx);
assert_eq!( assert_eq!(
movement::up( movement::up(
&snapshot, &snapshot,

View file

@ -51,7 +51,7 @@ pub use display_map::DisplayPoint;
use display_map::*; use display_map::*;
pub use editor_settings::EditorSettings; pub use editor_settings::EditorSettings;
use element::LineWithInvisibles; use element::LineWithInvisibles;
pub use element::{Cursor, EditorElement, HighlightedRange, HighlightedRangeLine}; pub use element::{CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine};
use futures::FutureExt; use futures::FutureExt;
use fuzzy::{StringMatch, StringMatchCandidate}; use fuzzy::{StringMatch, StringMatchCandidate};
use git::diff_hunk_to_display; use git::diff_hunk_to_display;
@ -3173,7 +3173,6 @@ impl Editor {
pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails { pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
TextLayoutDetails { TextLayoutDetails {
text_system: cx.text_system().clone(),
editor_style: self.style.clone().unwrap(), editor_style: self.style.clone().unwrap(),
rem_size: cx.rem_size(), rem_size: cx.rem_size(),
scroll_anchor: self.scroll_manager.anchor(), scroll_anchor: self.scroll_manager.anchor(),
@ -4099,7 +4098,7 @@ impl Editor {
_line_height: Pixels, _line_height: Pixels,
_gutter_margin: Pixels, _gutter_margin: Pixels,
editor_view: View<Editor>, editor_view: View<Editor>,
) -> Vec<Option<IconButton>> { ) -> Vec<Option<AnyElement>> {
fold_data fold_data
.iter() .iter()
.enumerate() .enumerate()
@ -4126,6 +4125,7 @@ impl Editor {
.selected(fold_status == FoldStatus::Folded) .selected(fold_status == FoldStatus::Folded)
.selected_icon(ui::IconName::ChevronRight) .selected_icon(ui::IconName::ChevronRight)
.size(ui::ButtonSize::None) .size(ui::ButtonSize::None)
.into_any_element()
}) })
}) })
.flatten() .flatten()
@ -5258,7 +5258,7 @@ impl Editor {
head = display_map.clip_point(head, Bias::Right); head = display_map.clip_point(head, Bias::Right);
let goal = SelectionGoal::HorizontalPosition( let goal = SelectionGoal::HorizontalPosition(
display_map display_map
.x_for_display_point(head, &text_layout_details) .x_for_display_point(head, &text_layout_details, cx)
.into(), .into(),
); );
selection.collapse_to(head, goal); selection.collapse_to(head, goal);
@ -6297,8 +6297,8 @@ impl Editor {
let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone(); let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
let range = oldest_selection.display_range(&display_map).sorted(); let range = oldest_selection.display_range(&display_map).sorted();
let start_x = display_map.x_for_display_point(range.start, &text_layout_details); let start_x = display_map.x_for_display_point(range.start, &text_layout_details, cx);
let end_x = display_map.x_for_display_point(range.end, &text_layout_details); let end_x = display_map.x_for_display_point(range.end, &text_layout_details, cx);
let positions = start_x.min(end_x)..start_x.max(end_x); let positions = start_x.min(end_x)..start_x.max(end_x);
selections.clear(); selections.clear();
@ -6337,16 +6337,17 @@ impl Editor {
let range = selection.display_range(&display_map).sorted(); let range = selection.display_range(&display_map).sorted();
debug_assert_eq!(range.start.row(), range.end.row()); debug_assert_eq!(range.start.row(), range.end.row());
let mut row = range.start.row(); let mut row = range.start.row();
let positions = let positions = if let SelectionGoal::HorizontalRange { start, end } =
if let SelectionGoal::HorizontalRange { start, end } = selection.goal { selection.goal
px(start)..px(end) {
} else { px(start)..px(end)
let start_x = } else {
display_map.x_for_display_point(range.start, &text_layout_details); let start_x =
let end_x = display_map.x_for_display_point(range.start, &text_layout_details, cx);
display_map.x_for_display_point(range.end, &text_layout_details); let end_x =
start_x.min(end_x)..start_x.max(end_x) display_map.x_for_display_point(range.end, &text_layout_details, cx);
}; start_x.min(end_x)..start_x.max(end_x)
};
while row != end_row { while row != end_row {
if above { if above {
@ -7033,7 +7034,7 @@ impl Editor {
let display_point = point.to_display_point(display_snapshot); let display_point = point.to_display_point(display_snapshot);
let goal = SelectionGoal::HorizontalPosition( let goal = SelectionGoal::HorizontalPosition(
display_snapshot display_snapshot
.x_for_display_point(display_point, &text_layout_details) .x_for_display_point(display_point, &text_layout_details, cx)
.into(), .into(),
); );
(display_point, goal) (display_point, goal)
@ -10137,7 +10138,7 @@ impl ViewInputHandler for Editor {
let scroll_left = scroll_position.x * em_width; let scroll_left = scroll_position.x * em_width;
let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot); let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left let x = snapshot.x_for_display_point(start, &text_layout_details, cx) - scroll_left
+ self.gutter_width; + self.gutter_width;
let y = line_height * (start.row() as f32 - scroll_position.y); let y = line_height * (start.row() as f32 - scroll_position.y);

File diff suppressed because it is too large Load diff

View file

@ -117,7 +117,7 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie
// Highlight the selected symbol using a background highlight // Highlight the selected symbol using a background highlight
this.highlight_inlay_background::<HoverState>( this.highlight_inlay_background::<HoverState>(
vec![inlay_hover.range], vec![inlay_hover.range],
|theme| theme.element_hover, // todo!("use a proper background here") |theme| theme.element_hover, // todo("use a proper background here")
cx, cx,
); );
this.hover_state.info_popover = Some(hover_popover); this.hover_state.info_popover = Some(hover_popover);
@ -332,7 +332,7 @@ fn show_hover(
// Highlight the selected symbol using a background highlight // Highlight the selected symbol using a background highlight
this.highlight_background::<HoverState>( this.highlight_background::<HoverState>(
vec![symbol_range], vec![symbol_range],
|theme| theme.element_hover, // todo! update theme |theme| theme.element_hover, // todo update theme
cx, cx,
); );
} else { } else {

View file

@ -3,11 +3,11 @@
use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint}; use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
use crate::{char_kind, scroll::ScrollAnchor, CharKind, EditorStyle, ToOffset, ToPoint}; use crate::{char_kind, scroll::ScrollAnchor, CharKind, EditorStyle, ToOffset, ToPoint};
use gpui::{px, Pixels, WindowTextSystem}; use gpui::{px, Pixels};
use language::Point; use language::Point;
use multi_buffer::MultiBufferSnapshot; use multi_buffer::MultiBufferSnapshot;
use std::{ops::Range, sync::Arc}; use std::ops::Range;
/// Defines search strategy for items in `movement` module. /// Defines search strategy for items in `movement` module.
/// `FindRange::SingeLine` only looks for a match on a single line at a time, whereas /// `FindRange::SingeLine` only looks for a match on a single line at a time, whereas
@ -21,7 +21,6 @@ pub enum FindRange {
/// TextLayoutDetails encompasses everything we need to move vertically /// TextLayoutDetails encompasses everything we need to move vertically
/// taking into account variable width characters. /// taking into account variable width characters.
pub struct TextLayoutDetails { pub struct TextLayoutDetails {
pub(crate) text_system: Arc<WindowTextSystem>,
pub(crate) editor_style: EditorStyle, pub(crate) editor_style: EditorStyle,
pub(crate) rem_size: Pixels, pub(crate) rem_size: Pixels,
pub scroll_anchor: ScrollAnchor, pub scroll_anchor: ScrollAnchor,

View file

@ -537,13 +537,13 @@ impl Render for ExtensionsPage {
return this.py_4().child(self.render_empty_state(cx)); return this.py_4().child(self.render_empty_state(cx));
} }
let view = cx.view().clone();
let scroll_handle = self.list.clone();
let item_count = entries.len();
this.child( this.child(
canvas({ canvas(
let view = cx.view().clone();
let scroll_handle = self.list.clone();
let item_count = entries.len();
move |bounds, cx| { move |bounds, cx| {
uniform_list::<_, Div, _>( let mut list = uniform_list::<_, Div, _>(
view, view,
"entries", "entries",
item_count, item_count,
@ -552,14 +552,12 @@ impl Render for ExtensionsPage {
.size_full() .size_full()
.pb_4() .pb_4()
.track_scroll(scroll_handle) .track_scroll(scroll_handle)
.into_any_element() .into_any_element();
.draw( list.layout(bounds.origin, bounds.size.into(), cx);
bounds.origin, list
bounds.size.map(AvailableSpace::Definite), },
cx, |_bounds, mut list, cx| list.paint(cx),
) )
}
})
.size_full(), .size_full(),
) )
})) }))

View file

@ -1343,7 +1343,7 @@ pub fn copy_recursive<'a>(
.boxed() .boxed()
} }
// todo!(windows) // todo(windows)
// can we get file id not open the file twice? // can we get file id not open the file twice?
// https://github.com/rust-lang/rust/issues/63010 // https://github.com/rust-lang/rust/issues/63010
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]

View file

@ -96,7 +96,7 @@ objc = "0.2"
flume = "0.11" flume = "0.11"
open = "5.0.1" open = "5.0.1"
ashpd = "0.7.0" ashpd = "0.7.0"
# todo!(linux) - Technically do not use `randr`, but it doesn't compile otherwise # todo(linux) - Technically do not use `randr`, but it doesn't compile otherwise
xcb = { version = "1.3", features = ["as-raw-xcb-connection", "present", "randr", "xkb"] } xcb = { version = "1.3", features = ["as-raw-xcb-connection", "present", "randr", "xkb"] }
wayland-client= { version = "0.31.2" } wayland-client= { version = "0.31.2" }
wayland-protocols = { version = "0.31.2", features = ["client", "staging", "unstable"] } wayland-protocols = { version = "0.31.2", features = ["client", "staging", "unstable"] }

View file

@ -674,17 +674,10 @@ impl<'a> VisualTestContext {
f: impl FnOnce(&mut WindowContext) -> AnyElement, f: impl FnOnce(&mut WindowContext) -> AnyElement,
) { ) {
self.update(|cx| { self.update(|cx| {
let entity_id = cx
.window
.root_view
.as_ref()
.expect("Can't draw to this window without a root view")
.entity_id();
cx.with_element_context(|cx| { cx.with_element_context(|cx| {
cx.with_view_id(entity_id, |cx| { let mut element = f(cx);
f(cx).draw(origin, space, cx); element.layout(origin, space, cx);
}) element.paint(cx);
}); });
cx.refresh(); cx.refresh();

View file

@ -0,0 +1,365 @@
#![allow(unused)]
use crate::{Bounds, Half, Point};
use std::{
cmp,
fmt::Debug,
ops::{Add, Sub},
};
#[derive(Debug)]
pub(crate) struct BoundsTree<U, T>
where
U: Default + Clone + Debug,
T: Clone + Debug,
{
root: Option<usize>,
nodes: Vec<Node<U, T>>,
stack: Vec<usize>,
}
impl<U, T> BoundsTree<U, T>
where
U: Clone + Debug + PartialOrd + Add<U, Output = U> + Sub<Output = U> + Half + Default,
T: Clone + Debug,
{
pub fn clear(&mut self) {
self.root = None;
self.nodes.clear();
self.stack.clear();
}
pub fn insert(&mut self, new_bounds: Bounds<U>, payload: T) -> u32 {
// If the tree is empty, make the root the new leaf.
if self.root.is_none() {
let new_node = self.push_leaf(new_bounds, payload, 1);
self.root = Some(new_node);
return 1;
}
// Search for the best place to add the new leaf based on heuristics.
let mut max_intersecting_ordering = 0;
let mut index = self.root.unwrap();
while let Node::Internal {
left,
right,
bounds: node_bounds,
..
} = &mut self.nodes[index]
{
let left = *left;
let right = *right;
*node_bounds = node_bounds.union(&new_bounds);
self.stack.push(index);
// Descend to the best-fit child, based on which one would increase
// the surface area the least. This attempts to keep the tree balanced
// in terms of surface area. If there is an intersection with the other child,
// add its keys to the intersections vector.
let left_cost = new_bounds
.union(&self.nodes[left].bounds())
.half_perimeter();
let right_cost = new_bounds
.union(&self.nodes[right].bounds())
.half_perimeter();
if left_cost < right_cost {
max_intersecting_ordering =
self.find_max_ordering(right, &new_bounds, max_intersecting_ordering);
index = left;
} else {
max_intersecting_ordering =
self.find_max_ordering(left, &new_bounds, max_intersecting_ordering);
index = right;
}
}
// We've found a leaf ('index' now refers to a leaf node).
// We'll insert a new parent node above the leaf and attach our new leaf to it.
let sibling = index;
// Check for collision with the located leaf node
let Node::Leaf {
bounds: sibling_bounds,
order: sibling_ordering,
..
} = &self.nodes[index]
else {
unreachable!();
};
if sibling_bounds.intersects(&new_bounds) {
max_intersecting_ordering = cmp::max(max_intersecting_ordering, *sibling_ordering);
}
let ordering = max_intersecting_ordering + 1;
let new_node = self.push_leaf(new_bounds, payload, ordering);
let new_parent = self.push_internal(sibling, new_node);
// If there was an old parent, we need to update its children indices.
if let Some(old_parent) = self.stack.last().copied() {
let Node::Internal { left, right, .. } = &mut self.nodes[old_parent] else {
unreachable!();
};
if *left == sibling {
*left = new_parent;
} else {
*right = new_parent;
}
} else {
// If the old parent was the root, the new parent is the new root.
self.root = Some(new_parent);
}
for node_index in self.stack.drain(..) {
let Node::Internal {
max_order: max_ordering,
..
} = &mut self.nodes[node_index]
else {
unreachable!()
};
*max_ordering = cmp::max(*max_ordering, ordering);
}
ordering
}
/// Finds all nodes whose bounds contain the given point and pushes their (bounds, payload) pairs onto the result vector.
pub(crate) fn find_containing(
&mut self,
point: &Point<U>,
result: &mut Vec<BoundsSearchResult<U, T>>,
) {
if let Some(mut index) = self.root {
self.stack.clear();
self.stack.push(index);
while let Some(current_index) = self.stack.pop() {
match &self.nodes[current_index] {
Node::Leaf {
bounds,
order,
data,
} => {
if bounds.contains(point) {
result.push(BoundsSearchResult {
bounds: bounds.clone(),
order: *order,
data: data.clone(),
});
}
}
Node::Internal {
left,
right,
bounds,
..
} => {
if bounds.contains(point) {
self.stack.push(*left);
self.stack.push(*right);
}
}
}
}
}
}
fn find_max_ordering(&self, index: usize, bounds: &Bounds<U>, mut max_ordering: u32) -> u32 {
match {
let this = &self;
&this.nodes[index]
} {
Node::Leaf {
bounds: node_bounds,
order: ordering,
..
} => {
if bounds.intersects(node_bounds) {
max_ordering = cmp::max(*ordering, max_ordering);
}
}
Node::Internal {
left,
right,
bounds: node_bounds,
max_order: node_max_ordering,
..
} => {
if bounds.intersects(node_bounds) && max_ordering < *node_max_ordering {
let left_max_ordering = self.nodes[*left].max_ordering();
let right_max_ordering = self.nodes[*right].max_ordering();
if left_max_ordering > right_max_ordering {
max_ordering = self.find_max_ordering(*left, bounds, max_ordering);
max_ordering = self.find_max_ordering(*right, bounds, max_ordering);
} else {
max_ordering = self.find_max_ordering(*right, bounds, max_ordering);
max_ordering = self.find_max_ordering(*left, bounds, max_ordering);
}
}
}
}
max_ordering
}
fn push_leaf(&mut self, bounds: Bounds<U>, payload: T, order: u32) -> usize {
self.nodes.push(Node::Leaf {
bounds,
data: payload,
order,
});
self.nodes.len() - 1
}
fn push_internal(&mut self, left: usize, right: usize) -> usize {
let left_node = &self.nodes[left];
let right_node = &self.nodes[right];
let new_bounds = left_node.bounds().union(right_node.bounds());
let max_ordering = cmp::max(left_node.max_ordering(), right_node.max_ordering());
self.nodes.push(Node::Internal {
bounds: new_bounds,
left,
right,
max_order: max_ordering,
});
self.nodes.len() - 1
}
}
impl<U, T> Default for BoundsTree<U, T>
where
U: Default + Clone + Debug,
T: Clone + Debug,
{
fn default() -> Self {
BoundsTree {
root: None,
nodes: Vec::new(),
stack: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
enum Node<U, T>
where
U: Clone + Default + Debug,
T: Clone + Debug,
{
Leaf {
bounds: Bounds<U>,
order: u32,
data: T,
},
Internal {
left: usize,
right: usize,
bounds: Bounds<U>,
max_order: u32,
},
}
impl<U, T> Node<U, T>
where
U: Clone + Default + Debug,
T: Clone + Debug,
{
fn bounds(&self) -> &Bounds<U> {
match self {
Node::Leaf { bounds, .. } => bounds,
Node::Internal { bounds, .. } => bounds,
}
}
fn max_ordering(&self) -> u32 {
match self {
Node::Leaf {
order: ordering, ..
} => *ordering,
Node::Internal {
max_order: max_ordering,
..
} => *max_ordering,
}
}
}
pub(crate) struct BoundsSearchResult<U: Clone + Default + Debug, T> {
pub bounds: Bounds<U>,
pub order: u32,
pub data: T,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Bounds, Point, Size};
#[test]
fn test_insert_and_find_containing() {
let mut tree = BoundsTree::<f32, String>::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 bounds into the tree
tree.insert(bounds1.clone(), "Payload 1".to_string());
tree.insert(bounds2.clone(), "Payload 2".to_string());
tree.insert(bounds3.clone(), "Payload 3".to_string());
// Points for testing
let point_inside_bounds1 = Point { x: 1.0, y: 1.0 };
let point_inside_bounds1_and_2 = Point { x: 6.0, y: 6.0 };
let point_inside_bounds2_and_3 = Point { x: 12.0, y: 12.0 };
let point_outside_all_bounds = Point { x: 21.0, y: 21.0 };
assert!(!bounds1.contains(&point_inside_bounds2_and_3));
assert!(!bounds1.contains(&point_outside_all_bounds));
assert!(bounds2.contains(&point_inside_bounds1_and_2));
assert!(bounds2.contains(&point_inside_bounds2_and_3));
assert!(!bounds2.contains(&point_outside_all_bounds));
assert!(!bounds3.contains(&point_inside_bounds1));
assert!(bounds3.contains(&point_inside_bounds2_and_3));
assert!(!bounds3.contains(&point_outside_all_bounds));
// Test find_containing for different points
let mut result = Vec::new();
tree.find_containing(&point_inside_bounds1, &mut result);
assert_eq!(result.len(), 1);
assert_eq!(result[0].data, "Payload 1");
result.clear();
tree.find_containing(&point_inside_bounds1_and_2, &mut result);
assert_eq!(result.len(), 2);
assert!(result.iter().any(|r| r.data == "Payload 1"));
assert!(result.iter().any(|r| r.data == "Payload 2"));
result.clear();
tree.find_containing(&point_inside_bounds2_and_3, &mut result);
assert_eq!(result.len(), 2);
assert!(result.iter().any(|r| r.data == "Payload 2"));
assert!(result.iter().any(|r| r.data == "Payload 3"));
result.clear();
tree.find_containing(&point_outside_all_bounds, &mut result);
assert_eq!(result.len(), 0);
}
}

View file

@ -15,9 +15,6 @@
//! //!
//! But some state is too simple and voluminous to store in every view that needs it, e.g. //! 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::State`], associated type. //! whether a hover has been started or not. For this, GPUI provides the [`Element::State`], associated type.
//! If an element returns an [`ElementId`] from [`IntoElement::element_id()`], and that element id
//! appears in the same place relative to other views and ElementIds in the frame, then the previous
//! frame's state will be passed to the element's layout and paint methods.
//! //!
//! # Implementing your own elements //! # Implementing your own elements
//! //!
@ -35,33 +32,48 @@
//! your own custom layout algorithm or rendering a code editor. //! your own custom layout algorithm or rendering a code editor.
use crate::{ use crate::{
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, ElementContext, ElementId, LayoutId, util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementContext,
Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA, ElementId, LayoutId, Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
}; };
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
pub(crate) use smallvec::SmallVec; pub(crate) use smallvec::SmallVec;
use std::{any::Any, fmt::Debug, ops::DerefMut}; use std::{any::Any, fmt::Debug, mem, ops::DerefMut};
/// Implemented by types that participate in laying out and painting the contents of a window. /// 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. /// 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 /// You can create custom elements by implementing this trait, see the module-level documentation
/// for more details. /// for more details.
pub trait Element: 'static + IntoElement { pub trait Element: 'static + IntoElement {
/// The type of state to store for this element between frames. See the module-level documentation /// The type of state returned from [`Element::before_layout`]. A mutable reference to this state is subsequently
/// for details. /// provided to [`Element::after_layout`] and [`Element::paint`].
type State: 'static; type BeforeLayout: 'static;
/// The type of state returned from [`Element::after_layout`]. A mutable reference to this state is subsequently
/// provided to [`Element::paint`].
type AfterLayout: 'static;
/// Before an element can be painted, we need to know where it's going to be and how big it is. /// 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. /// Use this method to request a layout from Taffy and initialize the element's state.
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout);
/// 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::before_layout()`].
fn after_layout(
&mut self, &mut self,
state: Option<Self::State>, bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) -> (LayoutId, Self::State); ) -> Self::AfterLayout;
/// Once layout has been completed, this method will be called to paint the element to the screen. /// 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()`]. /// The state argument is the same state that was returned from [`Element::before_layout()`].
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext); fn paint(
&mut self,
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
);
/// Convert this element into a dynamically-typed [`AnyElement`]. /// Convert this element into a dynamically-typed [`AnyElement`].
fn into_any(self) -> AnyElement { fn into_any(self) -> AnyElement {
@ -75,10 +87,6 @@ pub trait IntoElement: Sized {
/// Useful for converting other types into elements automatically, like Strings /// Useful for converting other types into elements automatically, like Strings
type Element: Element; type Element: Element;
/// The [`ElementId`] of self once converted into an [`Element`].
/// If present, the resulting element's state will be carried across frames.
fn element_id(&self) -> Option<ElementId>;
/// Convert self into a type that implements [`Element`]. /// Convert self into a type that implements [`Element`].
fn into_element(self) -> Self::Element; fn into_element(self) -> Self::Element;
@ -86,41 +94,6 @@ pub trait IntoElement: Sized {
fn into_any_element(self) -> AnyElement { fn into_any_element(self) -> AnyElement {
self.into_element().into_any() self.into_element().into_any()
} }
/// Convert into an element, then draw in the current window at the given origin.
/// The available space argument is provided to the layout engine to determine the size of the
// root element. Once the element is drawn, its associated element state is yielded to the
// given callback.
fn draw_and_update_state<T, R>(
self,
origin: Point<Pixels>,
available_space: Size<T>,
cx: &mut ElementContext,
f: impl FnOnce(&mut <Self::Element as Element>::State, &mut ElementContext) -> R,
) -> R
where
T: Clone + Default + Debug + Into<AvailableSpace>,
{
let element = self.into_element();
let element_id = element.element_id();
let element = DrawableElement {
element: Some(element),
phase: ElementDrawPhase::Start,
};
let frame_state =
DrawableElement::draw(element, origin, available_space.map(Into::into), cx);
if let Some(mut frame_state) = frame_state {
f(&mut frame_state, cx)
} else {
cx.with_element_state(element_id.unwrap(), |element_state, cx| {
let mut element_state = element_state.unwrap();
let result = f(&mut element_state, cx);
(result, element_state)
})
}
}
} }
impl<T: IntoElement> FluentBuilder for T {} impl<T: IntoElement> FluentBuilder for T {}
@ -188,24 +161,36 @@ impl<C: RenderOnce> Component<C> {
} }
impl<C: RenderOnce> Element for Component<C> { impl<C: RenderOnce> Element for Component<C> {
type State = AnyElement; type BeforeLayout = AnyElement;
type AfterLayout = ();
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
&mut self,
_: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
let mut element = self let mut element = self
.0 .0
.take() .take()
.unwrap() .unwrap()
.render(cx.deref_mut()) .render(cx.deref_mut())
.into_any_element(); .into_any_element();
let layout_id = element.request_layout(cx); let layout_id = element.before_layout(cx);
(layout_id, element) (layout_id, element)
} }
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) { fn after_layout(
&mut self,
_: Bounds<Pixels>,
element: &mut AnyElement,
cx: &mut ElementContext,
) {
element.after_layout(cx);
}
fn paint(
&mut self,
_: Bounds<Pixels>,
element: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
element.paint(cx) element.paint(cx)
} }
} }
@ -213,10 +198,6 @@ impl<C: RenderOnce> Element for Component<C> {
impl<C: RenderOnce> IntoElement for Component<C> { impl<C: RenderOnce> IntoElement for Component<C> {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
@ -227,9 +208,11 @@ impl<C: RenderOnce> IntoElement for Component<C> {
pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>); pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
trait ElementObject { trait ElementObject {
fn element_id(&self) -> Option<ElementId>; fn inner_element(&mut self) -> &mut dyn Any;
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId; fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
fn after_layout(&mut self, cx: &mut ElementContext);
fn paint(&mut self, cx: &mut ElementContext); fn paint(&mut self, cx: &mut ElementContext);
@ -238,110 +221,103 @@ trait ElementObject {
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
cx: &mut ElementContext, cx: &mut ElementContext,
) -> Size<Pixels>; ) -> Size<Pixels>;
fn draw(
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
);
} }
/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window. /// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
pub(crate) struct DrawableElement<E: Element> { pub struct Drawable<E: Element> {
element: Option<E>, /// The drawn element.
phase: ElementDrawPhase<E::State>, pub element: E,
phase: ElementDrawPhase<E::BeforeLayout, E::AfterLayout>,
} }
#[derive(Default)] #[derive(Default)]
enum ElementDrawPhase<S> { enum ElementDrawPhase<BeforeLayout, AfterLayout> {
#[default] #[default]
Start, Start,
LayoutRequested { BeforeLayout {
layout_id: LayoutId, layout_id: LayoutId,
frame_state: Option<S>, before_layout: BeforeLayout,
}, },
LayoutComputed { LayoutComputed {
layout_id: LayoutId, layout_id: LayoutId,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
frame_state: Option<S>, before_layout: BeforeLayout,
}, },
AfterLayout {
node_id: DispatchNodeId,
bounds: Bounds<Pixels>,
before_layout: BeforeLayout,
after_layout: AfterLayout,
},
Painted,
} }
/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window. /// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
impl<E: Element> DrawableElement<E> { impl<E: Element> Drawable<E> {
fn new(element: E) -> Self { fn new(element: E) -> Self {
DrawableElement { Drawable {
element: Some(element), element,
phase: ElementDrawPhase::Start, phase: ElementDrawPhase::Start,
} }
} }
fn element_id(&self) -> Option<ElementId> { fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
self.element.as_ref()?.element_id() match mem::take(&mut self.phase) {
ElementDrawPhase::Start => {
let (layout_id, before_layout) = self.element.before_layout(cx);
self.phase = ElementDrawPhase::BeforeLayout {
layout_id,
before_layout,
};
layout_id
}
_ => panic!("must call before_layout only once"),
}
} }
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId { fn after_layout(&mut self, cx: &mut ElementContext) {
let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id() match mem::take(&mut self.phase) {
{ ElementDrawPhase::BeforeLayout {
let layout_id = cx.with_element_state(id, |element_state, cx| {
self.element
.as_mut()
.unwrap()
.request_layout(element_state, cx)
});
(layout_id, None)
} else {
let (layout_id, frame_state) = self.element.as_mut().unwrap().request_layout(None, cx);
(layout_id, Some(frame_state))
};
self.phase = ElementDrawPhase::LayoutRequested {
layout_id,
frame_state,
};
layout_id
}
fn paint(mut self, cx: &mut ElementContext) -> Option<E::State> {
match self.phase {
ElementDrawPhase::LayoutRequested {
layout_id, layout_id,
frame_state, mut before_layout,
} }
| ElementDrawPhase::LayoutComputed { | ElementDrawPhase::LayoutComputed {
layout_id, layout_id,
frame_state, mut before_layout,
.. ..
} => { } => {
let bounds = cx.layout_bounds(layout_id); let bounds = cx.layout_bounds(layout_id);
cx.layout_element(|node_id, cx| {
if let Some(mut frame_state) = frame_state { let after_layout = self.element.after_layout(bounds, &mut before_layout, cx);
self.element self.phase = ElementDrawPhase::AfterLayout {
.take() node_id,
.unwrap() bounds,
.paint(bounds, &mut frame_state, cx); before_layout,
Some(frame_state) after_layout,
} else { };
let element_id = self });
.element
.as_ref()
.unwrap()
.element_id()
.expect("if we don't have frame state, we should have element state");
cx.with_element_state(element_id, |element_state, cx| {
let mut element_state = element_state.unwrap();
self.element
.take()
.unwrap()
.paint(bounds, &mut element_state, cx);
((), element_state)
});
None
}
} }
_ => panic!("must call before_layout before after_layout"),
}
}
_ => panic!("must call layout before paint"), fn paint(&mut self, cx: &mut ElementContext) -> E::BeforeLayout {
match mem::take(&mut self.phase) {
ElementDrawPhase::AfterLayout {
node_id,
bounds,
mut before_layout,
mut after_layout,
..
} => {
cx.paint_element(node_id, |cx| {
self.element
.paint(bounds, &mut before_layout, &mut after_layout, cx);
});
self.phase = ElementDrawPhase::Painted;
before_layout
}
_ => panic!("must call after_layout before paint"),
} }
} }
@ -351,66 +327,63 @@ impl<E: Element> DrawableElement<E> {
cx: &mut ElementContext, cx: &mut ElementContext,
) -> Size<Pixels> { ) -> Size<Pixels> {
if matches!(&self.phase, ElementDrawPhase::Start) { if matches!(&self.phase, ElementDrawPhase::Start) {
self.request_layout(cx); self.before_layout(cx);
} }
let layout_id = match &mut self.phase { let layout_id = match mem::take(&mut self.phase) {
ElementDrawPhase::LayoutRequested { ElementDrawPhase::BeforeLayout {
layout_id, layout_id,
frame_state, before_layout,
} => { } => {
cx.compute_layout(*layout_id, available_space); cx.compute_layout(layout_id, available_space);
let layout_id = *layout_id;
self.phase = ElementDrawPhase::LayoutComputed { self.phase = ElementDrawPhase::LayoutComputed {
layout_id, layout_id,
available_space, available_space,
frame_state: frame_state.take(), before_layout,
}; };
layout_id layout_id
} }
ElementDrawPhase::LayoutComputed { ElementDrawPhase::LayoutComputed {
layout_id, layout_id,
available_space: prev_available_space, available_space: prev_available_space,
.. before_layout,
} => { } => {
if available_space != *prev_available_space { if available_space != prev_available_space {
cx.compute_layout(*layout_id, available_space); cx.compute_layout(layout_id, available_space);
*prev_available_space = available_space;
} }
*layout_id self.phase = ElementDrawPhase::LayoutComputed {
layout_id,
available_space,
before_layout,
};
layout_id
} }
_ => panic!("cannot measure after painting"), _ => panic!("cannot measure after painting"),
}; };
cx.layout_bounds(layout_id).size cx.layout_bounds(layout_id).size
} }
fn draw(
mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Option<E::State> {
self.measure(available_space, cx);
cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
}
} }
impl<E> ElementObject for Option<DrawableElement<E>> impl<E> ElementObject for Drawable<E>
where where
E: Element, E: Element,
E::State: 'static, E::BeforeLayout: 'static,
{ {
fn element_id(&self) -> Option<ElementId> { fn inner_element(&mut self) -> &mut dyn Any {
self.as_ref().unwrap().element_id() &mut self.element
} }
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId { fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
DrawableElement::request_layout(self.as_mut().unwrap(), cx) Drawable::before_layout(self, cx)
}
fn after_layout(&mut self, cx: &mut ElementContext) {
Drawable::after_layout(self, cx);
} }
fn paint(&mut self, cx: &mut ElementContext) { fn paint(&mut self, cx: &mut ElementContext) {
DrawableElement::paint(self.take().unwrap(), cx); Drawable::paint(self, cx);
} }
fn measure( fn measure(
@ -418,16 +391,7 @@ where
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
cx: &mut ElementContext, cx: &mut ElementContext,
) -> Size<Pixels> { ) -> Size<Pixels> {
DrawableElement::measure(self.as_mut().unwrap(), available_space, cx) Drawable::measure(self, available_space, cx)
}
fn draw(
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) {
DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
} }
} }
@ -438,18 +402,28 @@ impl AnyElement {
pub(crate) fn new<E>(element: E) -> Self pub(crate) fn new<E>(element: E) -> Self
where where
E: 'static + Element, E: 'static + Element,
E::State: Any, E::BeforeLayout: Any,
{ {
let element = ELEMENT_ARENA let element = ELEMENT_ARENA
.with_borrow_mut(|arena| arena.alloc(|| Some(DrawableElement::new(element)))) .with_borrow_mut(|arena| arena.alloc(|| Drawable::new(element)))
.map(|element| element as &mut dyn ElementObject); .map(|element| element as &mut dyn ElementObject);
AnyElement(element) AnyElement(element)
} }
/// Attempt to downcast a reference to the boxed element to a specific type.
pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.0.inner_element().downcast_mut()
}
/// Request the layout ID of the element stored in this `AnyElement`. /// Request the layout ID of the element stored in this `AnyElement`.
/// Used for laying out child elements in a parent element. /// Used for laying out child elements in a parent element.
pub fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId { pub fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
self.0.request_layout(cx) self.0.before_layout(cx)
}
/// Commits the element bounds of this [AnyElement] for hitbox purposes.
pub fn after_layout(&mut self, cx: &mut ElementContext) {
self.0.after_layout(cx)
} }
/// Paints the element stored in this `AnyElement`. /// Paints the element stored in this `AnyElement`.
@ -466,35 +440,44 @@ impl AnyElement {
self.0.measure(available_space, cx) self.0.measure(available_space, cx)
} }
/// Initializes this element and performs layout in the available space, then paints it at the given origin. /// Initializes this element, performs layout if needed and commits its bounds.
pub fn draw( pub fn layout(
&mut self, &mut self,
origin: Point<Pixels>, absolute_offset: Point<Pixels>,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
cx: &mut ElementContext, cx: &mut ElementContext,
) { ) -> Size<Pixels> {
self.0.draw(origin, available_space, cx) let size = self.measure(available_space, cx);
} cx.with_absolute_element_offset(absolute_offset, |cx| self.after_layout(cx));
size
/// Returns the element ID of the element stored in this `AnyElement`, if any.
pub fn inner_id(&self) -> Option<ElementId> {
self.0.element_id()
} }
} }
impl Element for AnyElement { impl Element for AnyElement {
type State = (); type BeforeLayout = ();
type AfterLayout = ();
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
&mut self, let layout_id = self.before_layout(cx);
_: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
let layout_id = self.request_layout(cx);
(layout_id, ()) (layout_id, ())
} }
fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut ElementContext) { fn after_layout(
&mut self,
_: Bounds<Pixels>,
_: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) {
self.after_layout(cx)
}
fn paint(
&mut self,
_: Bounds<Pixels>,
_: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
self.paint(cx) self.paint(cx)
} }
} }
@ -502,10 +485,6 @@ impl Element for AnyElement {
impl IntoElement for AnyElement { impl IntoElement for AnyElement {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
@ -521,30 +500,32 @@ pub struct Empty;
impl IntoElement for Empty { impl IntoElement for Empty {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }
impl Element for Empty { impl Element for Empty {
type State = (); type BeforeLayout = ();
type AfterLayout = ();
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
&mut self,
_state: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
(cx.request_layout(&crate::Style::default(), None), ()) (cx.request_layout(&crate::Style::default(), None), ())
} }
fn after_layout(
&mut self,
_bounds: Bounds<Pixels>,
_state: &mut Self::BeforeLayout,
_cx: &mut ElementContext,
) {
}
fn paint( fn paint(
&mut self, &mut self,
_bounds: Bounds<Pixels>, _bounds: Bounds<Pixels>,
_state: &mut Self::State, _before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
_cx: &mut ElementContext, _cx: &mut ElementContext,
) { ) {
} }

View file

@ -4,54 +4,68 @@ use crate::{Bounds, Element, ElementContext, IntoElement, Pixels, Style, StyleRe
/// Construct a canvas element with the given paint callback. /// Construct a canvas element with the given paint callback.
/// Useful for adding short term custom drawing to a view. /// Useful for adding short term custom drawing to a view.
pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut ElementContext)) -> Canvas { pub fn canvas<T>(
after_layout: impl 'static + FnOnce(Bounds<Pixels>, &mut ElementContext) -> T,
paint: impl 'static + FnOnce(Bounds<Pixels>, T, &mut ElementContext),
) -> Canvas<T> {
Canvas { Canvas {
paint_callback: Some(Box::new(callback)), after_layout: Some(Box::new(after_layout)),
paint: Some(Box::new(paint)),
style: StyleRefinement::default(), style: StyleRefinement::default(),
} }
} }
/// A canvas element, meant for accessing the low level paint API without defining a whole /// A canvas element, meant for accessing the low level paint API without defining a whole
/// custom element /// custom element
pub struct Canvas { pub struct Canvas<T> {
paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut ElementContext)>>, after_layout: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut ElementContext) -> T>>,
paint: Option<Box<dyn FnOnce(Bounds<Pixels>, T, &mut ElementContext)>>,
style: StyleRefinement, style: StyleRefinement,
} }
impl IntoElement for Canvas { impl<T: 'static> IntoElement for Canvas<T> {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
None
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }
impl Element for Canvas { impl<T: 'static> Element for Canvas<T> {
type State = Style; type BeforeLayout = Style;
type AfterLayout = Option<T>;
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) {
&mut self,
_: Option<Self::State>,
cx: &mut ElementContext,
) -> (crate::LayoutId, Self::State) {
let mut style = Style::default(); let mut style = Style::default();
style.refine(&self.style); style.refine(&self.style);
let layout_id = cx.request_layout(&style, []); let layout_id = cx.request_layout(&style, []);
(layout_id, style) (layout_id, style)
} }
fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut ElementContext) { fn after_layout(
&mut self,
bounds: Bounds<Pixels>,
_before_layout: &mut Style,
cx: &mut ElementContext,
) -> Option<T> {
Some(self.after_layout.take().unwrap()(bounds, cx))
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
style: &mut Style,
after_layout: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
let after_layout = after_layout.take().unwrap();
style.paint(bounds, cx, |cx| { style.paint(bounds, cx, |cx| {
(self.paint_callback.take().unwrap())(&bounds, cx) (self.paint.take().unwrap())(bounds, after_layout, cx)
}); });
} }
} }
impl Styled for Canvas { impl<T> Styled for Canvas<T> {
fn style(&mut self) -> &mut crate::StyleRefinement { fn style(&mut self) -> &mut crate::StyleRefinement {
&mut self.style &mut self.style
} }

File diff suppressed because it is too large Load diff

View file

@ -2,8 +2,8 @@ use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use crate::{ use crate::{
point, size, Bounds, DevicePixels, Element, ElementContext, ImageData, InteractiveElement, point, size, Bounds, DevicePixels, Element, ElementContext, Hitbox, ImageData,
InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, SharedUri, Size, InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, SharedUri, Size,
StyleRefinement, Styled, UriOrPath, StyleRefinement, Styled, UriOrPath,
}; };
use futures::FutureExt; use futures::FutureExt;
@ -88,86 +88,85 @@ impl Img {
} }
impl Element for Img { impl Element for Img {
type State = InteractiveElementState; type BeforeLayout = ();
type AfterLayout = Option<Hitbox>;
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let layout_id = self
.interactivity
.before_layout(cx, |style, cx| cx.request_layout(&style, []));
(layout_id, ())
}
fn after_layout(
&mut self, &mut self,
element_state: Option<Self::State>, bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) -> (LayoutId, Self::State) { ) -> Option<Hitbox> {
self.interactivity self.interactivity
.layout(element_state, cx, |style, cx| cx.request_layout(&style, [])) .after_layout(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
} }
fn paint( fn paint(
&mut self, &mut self,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
element_state: &mut Self::State, _: &mut Self::BeforeLayout,
hitbox: &mut Self::AfterLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) { ) {
let source = self.source.clone(); let source = self.source.clone();
self.interactivity.paint( self.interactivity
bounds, .paint(bounds, hitbox.as_ref(), cx, |style, cx| {
bounds.size,
element_state,
cx,
|style, _scroll_offset, cx| {
let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size()); let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.with_z_index(1, |cx| { match source {
match source { ImageSource::Uri(_) | ImageSource::File(_) => {
ImageSource::Uri(_) | ImageSource::File(_) => { let uri_or_path: UriOrPath = match source {
let uri_or_path: UriOrPath = match source { ImageSource::Uri(uri) => uri.into(),
ImageSource::Uri(uri) => uri.into(), ImageSource::File(path) => path.into(),
ImageSource::File(path) => path.into(), _ => unreachable!(),
_ => unreachable!(), };
};
let image_future = cx.image_cache.get(uri_or_path.clone(), cx); let image_future = cx.image_cache.get(uri_or_path.clone(), cx);
if let Some(data) = image_future if let Some(data) = image_future
.clone() .clone()
.now_or_never() .now_or_never()
.and_then(|result| result.ok()) .and_then(|result| result.ok())
{ {
let new_bounds = preserve_aspect_ratio(bounds, data.size());
cx.paint_image(new_bounds, corner_radii, data, self.grayscale)
.log_err();
} else {
cx.spawn(|mut cx| async move {
if image_future.await.ok().is_some() {
cx.on_next_frame(|cx| cx.refresh());
}
})
.detach();
}
}
ImageSource::Data(data) => {
let new_bounds = preserve_aspect_ratio(bounds, data.size()); let new_bounds = preserve_aspect_ratio(bounds, data.size());
cx.paint_image(new_bounds, corner_radii, data, self.grayscale) cx.paint_image(new_bounds, corner_radii, data, self.grayscale)
.log_err(); .log_err();
} else {
cx.spawn(|mut cx| async move {
if image_future.await.ok().is_some() {
cx.on_next_frame(|cx| cx.refresh());
}
})
.detach();
} }
}
#[cfg(target_os = "macos")] ImageSource::Data(data) => {
ImageSource::Surface(surface) => { let new_bounds = preserve_aspect_ratio(bounds, data.size());
let size = size(surface.width().into(), surface.height().into()); cx.paint_image(new_bounds, corner_radii, data, self.grayscale)
let new_bounds = preserve_aspect_ratio(bounds, size); .log_err();
// TODO: Add support for corner_radii and grayscale. }
cx.paint_surface(new_bounds, surface);
} #[cfg(target_os = "macos")]
}; ImageSource::Surface(surface) => {
}); let size = size(surface.width().into(), surface.height().into());
}, let new_bounds = preserve_aspect_ratio(bounds, size);
) // TODO: Add support for corner_radii and grayscale.
cx.paint_surface(new_bounds, surface);
}
}
})
} }
} }
impl IntoElement for Img { impl IntoElement for Img {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
self.interactivity.element_id.clone()
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }

View file

@ -8,11 +8,12 @@
use crate::{ use crate::{
point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges, point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges,
Element, ElementContext, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style, Element, ElementContext, HitboxId, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style,
StyleRefinement, Styled, WindowContext, StyleRefinement, Styled, WindowContext,
}; };
use collections::VecDeque; use collections::VecDeque;
use refineable::Refineable as _; use refineable::Refineable as _;
use smallvec::SmallVec;
use std::{cell::RefCell, ops::Range, rc::Rc}; use std::{cell::RefCell, ops::Range, rc::Rc};
use sum_tree::{Bias, SumTree}; use sum_tree::{Bias, SumTree};
use taffy::style::Overflow; use taffy::style::Overflow;
@ -96,6 +97,13 @@ struct LayoutItemsResponse {
item_elements: VecDeque<AnyElement>, item_elements: VecDeque<AnyElement>,
} }
/// Frame state used by the [List] element.
#[derive(Default)]
pub struct ListFrameState {
scroll_top: ListOffset,
items: SmallVec<[AnyElement; 32]>,
}
#[derive(Clone)] #[derive(Clone)]
enum ListItem { enum ListItem {
Unrendered, Unrendered,
@ -302,7 +310,6 @@ impl StateInner {
height: Pixels, height: Pixels,
delta: Point<Pixels>, delta: Point<Pixels>,
cx: &mut WindowContext, cx: &mut WindowContext,
padding: Edges<Pixels>,
) { ) {
// Drop scroll events after a reset, since we can't calculate // Drop scroll events after a reset, since we can't calculate
// the new logical scroll top without the item heights // the new logical scroll top without the item heights
@ -310,6 +317,7 @@ impl StateInner {
return; return;
} }
let padding = self.last_padding.unwrap_or_default();
let scroll_max = let scroll_max =
(self.items.summary().height + padding.top + padding.bottom - height).max(px(0.)); (self.items.summary().height + padding.top + padding.bottom - height).max(px(0.));
let new_scroll_top = (self.scroll_top(scroll_top) - delta.y) let new_scroll_top = (self.scroll_top(scroll_top) - delta.y)
@ -516,13 +524,13 @@ pub struct ListOffset {
} }
impl Element for List { impl Element for List {
type State = (); type BeforeLayout = ListFrameState;
type AfterLayout = HitboxId;
fn request_layout( fn before_layout(
&mut self, &mut self,
_state: Option<Self::State>,
cx: &mut crate::ElementContext, cx: &mut crate::ElementContext,
) -> (crate::LayoutId, Self::State) { ) -> (crate::LayoutId, Self::BeforeLayout) {
let layout_id = match self.sizing_behavior { let layout_id = match self.sizing_behavior {
ListSizingBehavior::Infer => { ListSizingBehavior::Infer => {
let mut style = Style::default(); let mut style = Style::default();
@ -580,15 +588,15 @@ impl Element for List {
}) })
} }
}; };
(layout_id, ()) (layout_id, ListFrameState::default())
} }
fn paint( fn after_layout(
&mut self, &mut self,
bounds: Bounds<crate::Pixels>, bounds: Bounds<Pixels>,
_state: &mut Self::State, before_layout: &mut Self::BeforeLayout,
cx: &mut crate::ElementContext, cx: &mut ElementContext,
) { ) -> HitboxId {
let state = &mut *self.state.0.borrow_mut(); let state = &mut *self.state.0.borrow_mut();
state.reset = false; state.reset = false;
@ -615,12 +623,11 @@ impl Element for List {
cx.with_content_mask(Some(ContentMask { bounds }), |cx| { cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
let mut item_origin = bounds.origin + Point::new(px(0.), padding.top); let mut item_origin = bounds.origin + Point::new(px(0.), padding.top);
item_origin.y -= layout_response.scroll_top.offset_in_item; item_origin.y -= layout_response.scroll_top.offset_in_item;
for item_element in &mut layout_response.item_elements { for mut item_element in layout_response.item_elements {
let item_height = item_element let item_size =
.measure(layout_response.available_item_space, cx) item_element.layout(item_origin, layout_response.available_item_space, cx);
.height; before_layout.items.push(item_element);
item_element.draw(item_origin, layout_response.available_item_space, cx); item_origin.y += item_size.height;
item_origin.y += item_height;
} }
}); });
} }
@ -628,20 +635,33 @@ impl Element for List {
state.last_layout_bounds = Some(bounds); state.last_layout_bounds = Some(bounds);
state.last_padding = Some(padding); state.last_padding = Some(padding);
cx.insert_hitbox(bounds, false).id
}
fn paint(
&mut self,
bounds: Bounds<crate::Pixels>,
before_layout: &mut Self::BeforeLayout,
hitbox_id: &mut HitboxId,
cx: &mut crate::ElementContext,
) {
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
for item in &mut before_layout.items {
item.paint(cx);
}
});
let list_state = self.state.clone(); let list_state = self.state.clone();
let height = bounds.size.height; let height = bounds.size.height;
let scroll_top = before_layout.scroll_top;
let hitbox_id = *hitbox_id;
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| { cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
if phase == DispatchPhase::Bubble if phase == DispatchPhase::Bubble && hitbox_id.is_hovered(cx) {
&& bounds.contains(&event.position)
&& cx.was_top_layer(&event.position, cx.stacking_order())
{
list_state.0.borrow_mut().scroll( list_state.0.borrow_mut().scroll(
&layout_response.scroll_top, &scroll_top,
height, height,
event.delta.pixel_delta(px(20.)), event.delta.pixel_delta(px(20.)),
cx, cx,
padding,
) )
} }
}); });
@ -651,10 +671,6 @@ impl Element for List {
impl IntoElement for List { impl IntoElement for List {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
None
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
@ -761,7 +777,7 @@ mod test {
cx.draw( cx.draw(
point(px(0.), px(0.)), point(px(0.), px(0.)),
size(px(100.), px(20.)).into(), size(px(100.), px(20.)).into(),
|_| list(state.clone()).w_full().h_full().z_index(10).into_any(), |_| list(state.clone()).w_full().h_full().into_any(),
); );
// Reset // Reset

View file

@ -9,6 +9,7 @@ use crate::{
/// The state that the overlay element uses to track its children. /// The state that the overlay element uses to track its children.
pub struct OverlayState { pub struct OverlayState {
child_layout_ids: SmallVec<[LayoutId; 4]>, child_layout_ids: SmallVec<[LayoutId; 4]>,
offset: Point<Pixels>,
} }
/// An overlay element that can be used to display UI that /// An overlay element that can be used to display UI that
@ -69,17 +70,14 @@ impl ParentElement for Overlay {
} }
impl Element for Overlay { impl Element for Overlay {
type State = OverlayState; type BeforeLayout = OverlayState;
type AfterLayout = ();
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) {
&mut self,
_: Option<Self::State>,
cx: &mut ElementContext,
) -> (crate::LayoutId, Self::State) {
let child_layout_ids = self let child_layout_ids = self
.children .children
.iter_mut() .iter_mut()
.map(|child| child.request_layout(cx)) .map(|child| child.before_layout(cx))
.collect::<SmallVec<_>>(); .collect::<SmallVec<_>>();
let overlay_style = Style { let overlay_style = Style {
@ -90,22 +88,28 @@ impl Element for Overlay {
let layout_id = cx.request_layout(&overlay_style, child_layout_ids.iter().copied()); let layout_id = cx.request_layout(&overlay_style, child_layout_ids.iter().copied());
(layout_id, OverlayState { child_layout_ids }) (
layout_id,
OverlayState {
child_layout_ids,
offset: Point::default(),
},
)
} }
fn paint( fn after_layout(
&mut self, &mut self,
bounds: crate::Bounds<crate::Pixels>, bounds: Bounds<Pixels>,
element_state: &mut Self::State, before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) { ) {
if element_state.child_layout_ids.is_empty() { if before_layout.child_layout_ids.is_empty() {
return; return;
} }
let mut child_min = point(Pixels::MAX, Pixels::MAX); let mut child_min = point(Pixels::MAX, Pixels::MAX);
let mut child_max = Point::default(); let mut child_max = Point::default();
for child_layout_id in &element_state.child_layout_ids { for child_layout_id in &before_layout.child_layout_ids {
let child_bounds = cx.layout_bounds(*child_layout_id); let child_bounds = cx.layout_bounds(*child_layout_id);
child_min = child_min.min(&child_bounds.origin); child_min = child_min.min(&child_bounds.origin);
child_max = child_max.max(&child_bounds.lower_right()); child_max = child_max.max(&child_bounds.lower_right());
@ -165,25 +169,30 @@ impl Element for Overlay {
desired.origin.y = limits.origin.y; desired.origin.y = limits.origin.y;
} }
let mut offset = cx.element_offset() + desired.origin - bounds.origin; before_layout.offset = cx.element_offset() + desired.origin - bounds.origin;
offset = point(offset.x.round(), offset.y.round()); before_layout.offset = point(
cx.with_absolute_element_offset(offset, |cx| { before_layout.offset.x.round(),
cx.break_content_mask(|cx| { before_layout.offset.y.round(),
for child in &mut self.children { );
child.paint(cx);
} for child in self.children.drain(..) {
}) cx.defer_draw(child, before_layout.offset, 1);
}) }
}
fn paint(
&mut self,
_bounds: crate::Bounds<crate::Pixels>,
_before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
_cx: &mut ElementContext,
) {
} }
} }
impl IntoElement for Overlay { impl IntoElement for Overlay {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
None
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
Bounds, Element, ElementContext, ElementId, InteractiveElement, InteractiveElementState, Bounds, Element, ElementContext, Hitbox, InteractiveElement, Interactivity, IntoElement,
Interactivity, IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, LayoutId, Pixels, SharedString, StyleRefinement, Styled,
}; };
use util::ResultExt; use util::ResultExt;
@ -27,28 +27,37 @@ impl Svg {
} }
impl Element for Svg { impl Element for Svg {
type State = InteractiveElementState; type BeforeLayout = ();
type AfterLayout = Option<Hitbox>;
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let layout_id = self
.interactivity
.before_layout(cx, |style, cx| cx.request_layout(&style, None));
(layout_id, ())
}
fn after_layout(
&mut self, &mut self,
element_state: Option<Self::State>, bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) -> (LayoutId, Self::State) { ) -> Option<Hitbox> {
self.interactivity.layout(element_state, cx, |style, cx| { self.interactivity
cx.request_layout(&style, None) .after_layout(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
})
} }
fn paint( fn paint(
&mut self, &mut self,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
element_state: &mut Self::State, _before_layout: &mut Self::BeforeLayout,
hitbox: &mut Option<Hitbox>,
cx: &mut ElementContext, cx: &mut ElementContext,
) where ) where
Self: Sized, Self: Sized,
{ {
self.interactivity self.interactivity
.paint(bounds, bounds.size, element_state, cx, |style, _, cx| { .paint(bounds, hitbox.as_ref(), cx, |style, cx| {
if let Some((path, color)) = self.path.as_ref().zip(style.text.color) { if let Some((path, color)) = self.path.as_ref().zip(style.text.color) {
cx.paint_svg(bounds, path.clone(), color).log_err(); cx.paint_svg(bounds, path.clone(), color).log_err();
} }
@ -59,10 +68,6 @@ impl Element for Svg {
impl IntoElement for Svg { impl IntoElement for Svg {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> {
self.interactivity.element_id.clone()
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
ActiveTooltip, AnyTooltip, AnyView, Bounds, DispatchPhase, Element, ElementContext, ElementId, ActiveTooltip, AnyTooltip, AnyView, Bounds, DispatchPhase, Element, ElementContext, ElementId,
HighlightStyle, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, HighlightStyle, Hitbox, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
Point, SharedString, Size, TextRun, TextStyle, WhiteSpace, WindowContext, WrappedLine, Pixels, Point, SharedString, Size, TextRun, TextStyle, WhiteSpace, WindowContext, WrappedLine,
TOOLTIP_DELAY, TOOLTIP_DELAY,
}; };
use anyhow::anyhow; use anyhow::anyhow;
@ -17,30 +17,37 @@ use std::{
use util::ResultExt; use util::ResultExt;
impl Element for &'static str { impl Element for &'static str {
type State = TextState; type BeforeLayout = TextState;
type AfterLayout = ();
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
&mut self,
_: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
let mut state = TextState::default(); let mut state = TextState::default();
let layout_id = state.layout(SharedString::from(*self), None, cx); let layout_id = state.layout(SharedString::from(*self), None, cx);
(layout_id, state) (layout_id, state)
} }
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut ElementContext) { fn after_layout(
state.paint(bounds, self, cx) &mut self,
_bounds: Bounds<Pixels>,
_text_state: &mut Self::BeforeLayout,
_cx: &mut ElementContext,
) {
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
text_state: &mut TextState,
_: &mut (),
cx: &mut ElementContext,
) {
text_state.paint(bounds, self, cx)
} }
} }
impl IntoElement for &'static str { impl IntoElement for &'static str {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
@ -49,41 +56,44 @@ impl IntoElement for &'static str {
impl IntoElement for String { impl IntoElement for String {
type Element = SharedString; type Element = SharedString;
fn element_id(&self) -> Option<ElementId> {
None
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self.into() self.into()
} }
} }
impl Element for SharedString { impl Element for SharedString {
type State = TextState; type BeforeLayout = TextState;
type AfterLayout = ();
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
&mut self,
_: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
let mut state = TextState::default(); let mut state = TextState::default();
let layout_id = state.layout(self.clone(), None, cx); let layout_id = state.layout(self.clone(), None, cx);
(layout_id, state) (layout_id, state)
} }
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut ElementContext) { fn after_layout(
&mut self,
_bounds: Bounds<Pixels>,
_text_state: &mut Self::BeforeLayout,
_cx: &mut ElementContext,
) {
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
text_state: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
let text_str: &str = self.as_ref(); let text_str: &str = self.as_ref();
state.paint(bounds, text_str, cx) text_state.paint(bounds, text_str, cx)
} }
} }
impl IntoElement for SharedString { impl IntoElement for SharedString {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
@ -138,30 +148,37 @@ impl StyledText {
} }
impl Element for StyledText { impl Element for StyledText {
type State = TextState; type BeforeLayout = TextState;
type AfterLayout = ();
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
&mut self,
_: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
let mut state = TextState::default(); let mut state = TextState::default();
let layout_id = state.layout(self.text.clone(), self.runs.take(), cx); let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
(layout_id, state) (layout_id, state)
} }
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) { fn after_layout(
state.paint(bounds, &self.text, cx) &mut self,
_bounds: Bounds<Pixels>,
_state: &mut Self::BeforeLayout,
_cx: &mut ElementContext,
) {
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
text_state: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
text_state.paint(bounds, &self.text, cx)
} }
} }
impl IntoElement for StyledText { impl IntoElement for StyledText {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
None
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
@ -324,8 +341,8 @@ struct InteractiveTextClickEvent {
} }
#[doc(hidden)] #[doc(hidden)]
#[derive(Default)]
pub struct InteractiveTextState { pub struct InteractiveTextState {
text_state: TextState,
mouse_down_index: Rc<Cell<Option<usize>>>, mouse_down_index: Rc<Cell<Option<usize>>>,
hovered_index: Rc<Cell<Option<usize>>>, hovered_index: Rc<Cell<Option<usize>>>,
active_tooltip: Rc<RefCell<Option<ActiveTooltip>>>, active_tooltip: Rc<RefCell<Option<ActiveTooltip>>>,
@ -385,179 +402,181 @@ impl InteractiveText {
} }
impl Element for InteractiveText { impl Element for InteractiveText {
type State = InteractiveTextState; type BeforeLayout = TextState;
type AfterLayout = Hitbox;
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
&mut self, self.text.before_layout(cx)
state: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
if let Some(InteractiveTextState {
mouse_down_index,
hovered_index,
active_tooltip,
..
}) = state
{
let (layout_id, text_state) = self.text.request_layout(None, cx);
let element_state = InteractiveTextState {
text_state,
mouse_down_index,
hovered_index,
active_tooltip,
};
(layout_id, element_state)
} else {
let (layout_id, text_state) = self.text.request_layout(None, cx);
let element_state = InteractiveTextState {
text_state,
mouse_down_index: Rc::default(),
hovered_index: Rc::default(),
active_tooltip: Rc::default(),
};
(layout_id, element_state)
}
} }
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) { fn after_layout(
if let Some(click_listener) = self.click_listener.take() { &mut self,
let mouse_position = cx.mouse_position(); bounds: Bounds<Pixels>,
if let Some(ix) = state.text_state.index_for_position(bounds, mouse_position) { state: &mut Self::BeforeLayout,
if self cx: &mut ElementContext,
.clickable_ranges ) -> Hitbox {
.iter() self.text.after_layout(bounds, state, cx);
.any(|range| range.contains(&ix)) cx.insert_hitbox(bounds, false)
{ }
let stacking_order = cx.stacking_order().clone();
cx.set_cursor_style(crate::CursorStyle::PointingHand, stacking_order);
}
}
let text_state = state.text_state.clone(); fn paint(
let mouse_down = state.mouse_down_index.clone(); &mut self,
if let Some(mouse_down_index) = mouse_down.get() { bounds: Bounds<Pixels>,
let clickable_ranges = mem::take(&mut self.clickable_ranges); text_state: &mut Self::BeforeLayout,
cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| { hitbox: &mut Hitbox,
if phase == DispatchPhase::Bubble { cx: &mut ElementContext,
if let Some(mouse_up_index) = ) {
text_state.index_for_position(bounds, event.position) cx.with_element_state::<InteractiveTextState, _>(
Some(self.element_id.clone()),
|interactive_state, cx| {
let mut interactive_state = interactive_state.unwrap().unwrap_or_default();
if let Some(click_listener) = self.click_listener.take() {
let mouse_position = cx.mouse_position();
if let Some(ix) = text_state.index_for_position(bounds, mouse_position) {
if self
.clickable_ranges
.iter()
.any(|range| range.contains(&ix))
{ {
click_listener( cx.set_cursor_style(crate::CursorStyle::PointingHand, hitbox)
&clickable_ranges,
InteractiveTextClickEvent {
mouse_down_index,
mouse_up_index,
},
cx,
)
}
mouse_down.take();
cx.refresh();
}
});
} else {
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble {
if let Some(mouse_down_index) =
text_state.index_for_position(bounds, event.position)
{
mouse_down.set(Some(mouse_down_index));
cx.refresh();
} }
} }
});
}
}
if let Some(hover_listener) = self.hover_listener.take() {
let text_state = state.text_state.clone();
let hovered_index = state.hovered_index.clone();
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
if phase == DispatchPhase::Bubble {
let current = hovered_index.get();
let updated = text_state.index_for_position(bounds, event.position);
if current != updated {
hovered_index.set(updated);
hover_listener(updated, event.clone(), cx);
cx.refresh();
}
}
});
}
if let Some(tooltip_builder) = self.tooltip_builder.clone() {
let active_tooltip = state.active_tooltip.clone();
let pending_mouse_down = state.mouse_down_index.clone();
let text_state = state.text_state.clone();
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { let text_state = text_state.clone();
let position = text_state.index_for_position(bounds, event.position); let mouse_down = interactive_state.mouse_down_index.clone();
let is_hovered = position.is_some() && pending_mouse_down.get().is_none(); if let Some(mouse_down_index) = mouse_down.get() {
if !is_hovered { let hitbox = hitbox.clone();
active_tooltip.take(); let clickable_ranges = mem::take(&mut self.clickable_ranges);
return; cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
} if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
let position = position.unwrap(); if let Some(mouse_up_index) =
text_state.index_for_position(bounds, event.position)
{
click_listener(
&clickable_ranges,
InteractiveTextClickEvent {
mouse_down_index,
mouse_up_index,
},
cx,
)
}
if phase != DispatchPhase::Bubble { mouse_down.take();
return;
}
if active_tooltip.borrow().is_none() {
let task = cx.spawn({
let active_tooltip = active_tooltip.clone();
let tooltip_builder = tooltip_builder.clone();
move |mut cx| async move {
cx.background_executor().timer(TOOLTIP_DELAY).await;
cx.update(|cx| {
let new_tooltip =
tooltip_builder(position, cx).map(|tooltip| ActiveTooltip {
tooltip: Some(AnyTooltip {
view: tooltip,
cursor_offset: cx.mouse_position(),
}),
_task: None,
});
*active_tooltip.borrow_mut() = new_tooltip;
cx.refresh(); cx.refresh();
}) }
.ok(); });
} else {
let hitbox = hitbox.clone();
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
if let Some(mouse_down_index) =
text_state.index_for_position(bounds, event.position)
{
mouse_down.set(Some(mouse_down_index));
cx.refresh();
}
}
});
}
}
if let Some(hover_listener) = self.hover_listener.take() {
let hitbox = hitbox.clone();
let text_state = text_state.clone();
let hovered_index = interactive_state.hovered_index.clone();
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
if phase == DispatchPhase::Bubble && hitbox.is_hovered(cx) {
let current = hovered_index.get();
let updated = text_state.index_for_position(bounds, event.position);
if current != updated {
hovered_index.set(updated);
hover_listener(updated, event.clone(), cx);
cx.refresh();
}
} }
}); });
*active_tooltip.borrow_mut() = Some(ActiveTooltip {
tooltip: None,
_task: Some(task),
});
} }
});
let active_tooltip = state.active_tooltip.clone(); if let Some(tooltip_builder) = self.tooltip_builder.clone() {
cx.on_mouse_event(move |_: &MouseDownEvent, _, _| { let hitbox = hitbox.clone();
active_tooltip.take(); let active_tooltip = interactive_state.active_tooltip.clone();
}); let pending_mouse_down = interactive_state.mouse_down_index.clone();
let text_state = text_state.clone();
if let Some(tooltip) = state cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
.active_tooltip let position = text_state.index_for_position(bounds, event.position);
.clone() let is_hovered = position.is_some()
.borrow() && hitbox.is_hovered(cx)
.as_ref() && pending_mouse_down.get().is_none();
.and_then(|at| at.tooltip.clone()) if !is_hovered {
{ active_tooltip.take();
cx.set_tooltip(tooltip); return;
} }
} let position = position.unwrap();
self.text.paint(bounds, &mut state.text_state, cx) if phase != DispatchPhase::Bubble {
return;
}
if active_tooltip.borrow().is_none() {
let task = cx.spawn({
let active_tooltip = active_tooltip.clone();
let tooltip_builder = tooltip_builder.clone();
move |mut cx| async move {
cx.background_executor().timer(TOOLTIP_DELAY).await;
cx.update(|cx| {
let new_tooltip =
tooltip_builder(position, cx).map(|tooltip| {
ActiveTooltip {
tooltip: Some(AnyTooltip {
view: tooltip,
cursor_offset: cx.mouse_position(),
}),
_task: None,
}
});
*active_tooltip.borrow_mut() = new_tooltip;
cx.refresh();
})
.ok();
}
});
*active_tooltip.borrow_mut() = Some(ActiveTooltip {
tooltip: None,
_task: Some(task),
});
}
});
let active_tooltip = interactive_state.active_tooltip.clone();
cx.on_mouse_event(move |_: &MouseDownEvent, _, _| {
active_tooltip.take();
});
if let Some(tooltip) = interactive_state
.active_tooltip
.clone()
.borrow()
.as_ref()
.and_then(|at| at.tooltip.clone())
{
cx.set_tooltip(tooltip);
}
}
self.text.paint(bounds, text_state, &mut (), cx);
((), Some(interactive_state))
},
);
} }
} }
impl IntoElement for InteractiveText { impl IntoElement for InteractiveText {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> {
Some(self.element_id.clone())
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }

View file

@ -6,8 +6,8 @@
use crate::{ use crate::{
point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementContext, point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementContext,
ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId, ElementId, Hitbox, InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Render,
Pixels, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc}; use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@ -42,13 +42,13 @@ where
}; };
UniformList { UniformList {
id: id.clone(),
item_count, item_count,
item_to_measure_index: 0, item_to_measure_index: 0,
render_items: Box::new(render_range), render_items: Box::new(render_range),
interactivity: Interactivity { interactivity: Interactivity {
element_id: Some(id), element_id: Some(id),
base_style: Box::new(base_style), base_style: Box::new(base_style),
occlude_mouse: true,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
location: Some(*core::panic::Location::caller()), location: Some(*core::panic::Location::caller()),
@ -61,7 +61,6 @@ where
/// A list element for efficiently laying out and displaying a list of uniform-height elements. /// A list element for efficiently laying out and displaying a list of uniform-height elements.
pub struct UniformList { pub struct UniformList {
id: ElementId,
item_count: usize, item_count: usize,
item_to_measure_index: usize, item_to_measure_index: usize,
render_items: render_items:
@ -70,6 +69,12 @@ pub struct UniformList {
scroll_handle: Option<UniformListScrollHandle>, scroll_handle: Option<UniformListScrollHandle>,
} }
/// Frame state used by the [UniformList].
pub struct UniformListFrameState {
item_size: Size<Pixels>,
items: SmallVec<[AnyElement; 32]>,
}
/// A handle for controlling the scroll position of a uniform list. /// 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. /// This should be stored in your view and passed to the uniform_list on each frame.
#[derive(Clone, Default)] #[derive(Clone, Default)]
@ -97,72 +102,47 @@ impl Styled for UniformList {
} }
} }
#[doc(hidden)]
#[derive(Default)]
pub struct UniformListState {
interactive: InteractiveElementState,
item_size: Size<Pixels>,
}
impl Element for UniformList { impl Element for UniformList {
type State = UniformListState; type BeforeLayout = UniformListFrameState;
type AfterLayout = Option<Hitbox>;
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
&mut self,
state: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
let max_items = self.item_count; let max_items = self.item_count;
let item_size = state let item_size = self.measure_item(None, cx);
.as_ref() let layout_id = self.interactivity.before_layout(cx, |style, cx| {
.map(|s| s.item_size) cx.request_measured_layout(style, move |known_dimensions, available_space, _cx| {
.unwrap_or_else(|| self.measure_item(None, 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 (layout_id, interactive) = let height = match available_space.height {
self.interactivity AvailableSpace::Definite(height) => desired_height.min(height),
.layout(state.map(|s| s.interactive), cx, |style, cx| { AvailableSpace::MinContent | AvailableSpace::MaxContent => desired_height,
cx.request_measured_layout( };
style, size(width, height)
move |known_dimensions, available_space, _cx| { })
let desired_height = item_size.height * max_items; });
let width =
known_dimensions
.width
.unwrap_or(match available_space.width {
AvailableSpace::Definite(x) => x,
AvailableSpace::MinContent | AvailableSpace::MaxContent => {
item_size.width
}
});
let height = match available_space.height { (
AvailableSpace::Definite(height) => desired_height.min(height), layout_id,
AvailableSpace::MinContent | AvailableSpace::MaxContent => { UniformListFrameState {
desired_height item_size,
} items: SmallVec::new(),
}; },
size(width, height) )
},
)
});
let element_state = UniformListState {
interactive,
item_size,
};
(layout_id, element_state)
} }
fn paint( fn after_layout(
&mut self, &mut self,
bounds: Bounds<crate::Pixels>, bounds: Bounds<Pixels>,
element_state: &mut Self::State, before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) { ) -> Option<Hitbox> {
let style = let style = self.interactivity.compute_style(None, cx);
self.interactivity
.compute_style(Some(bounds), &mut element_state.interactive, cx);
let border = style.border_widths.to_pixels(cx.rem_size()); let border = style.border_widths.to_pixels(cx.rem_size());
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size()); let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
@ -172,17 +152,12 @@ impl Element for UniformList {
- point(border.right + padding.right, border.bottom + padding.bottom), - point(border.right + padding.right, border.bottom + padding.bottom),
); );
let item_size = element_state.item_size;
let content_size = Size { let content_size = Size {
width: padded_bounds.size.width, width: padded_bounds.size.width,
height: item_size.height * self.item_count + padding.top + padding.bottom, height: before_layout.item_size.height * self.item_count + padding.top + padding.bottom,
}; };
let shared_scroll_offset = element_state let shared_scroll_offset = self.interactivity.scroll_offset.clone().unwrap();
.interactive
.scroll_offset
.get_or_insert_with(Rc::default)
.clone();
let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height; let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height;
let shared_scroll_to_item = self let shared_scroll_to_item = self
@ -190,12 +165,11 @@ impl Element for UniformList {
.as_mut() .as_mut()
.and_then(|handle| handle.deferred_scroll_to_item.take()); .and_then(|handle| handle.deferred_scroll_to_item.take());
self.interactivity.paint( self.interactivity.after_layout(
bounds, bounds,
content_size, content_size,
&mut element_state.interactive,
cx, cx,
|style, mut scroll_offset, cx| { |style, mut scroll_offset, hitbox, cx| {
let border = style.border_widths.to_pixels(cx.rem_size()); let border = style.border_widths.to_pixels(cx.rem_size());
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size()); let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
@ -238,36 +212,45 @@ impl Element for UniformList {
..cmp::min(last_visible_element_ix, self.item_count); ..cmp::min(last_visible_element_ix, self.item_count);
let mut items = (self.render_items)(visible_range.clone(), cx); let mut items = (self.render_items)(visible_range.clone(), cx);
cx.with_z_index(1, |cx| { let content_mask = ContentMask { bounds };
let content_mask = ContentMask { bounds }; cx.with_content_mask(Some(content_mask), |cx| {
cx.with_content_mask(Some(content_mask), |cx| { for (mut item, ix) in items.into_iter().zip(visible_range) {
for (item, ix) in items.iter_mut().zip(visible_range) { let item_origin = padded_bounds.origin
let item_origin = padded_bounds.origin + point(px(0.), item_height * ix + scroll_offset.y + padding.top);
+ point( let available_space = size(
px(0.), AvailableSpace::Definite(padded_bounds.size.width),
item_height * ix + scroll_offset.y + padding.top, AvailableSpace::Definite(item_height),
); );
let available_space = size( item.layout(item_origin, available_space, cx);
AvailableSpace::Definite(padded_bounds.size.width), before_layout.items.push(item);
AvailableSpace::Definite(item_height), }
);
item.draw(item_origin, available_space, cx);
}
});
}); });
} }
hitbox
}, },
) )
} }
fn paint(
&mut self,
bounds: Bounds<crate::Pixels>,
before_layout: &mut Self::BeforeLayout,
hitbox: &mut Option<Hitbox>,
cx: &mut ElementContext,
) {
self.interactivity
.paint(bounds, hitbox.as_ref(), cx, |_, cx| {
for item in &mut before_layout.items {
item.paint(cx);
}
})
}
} }
impl IntoElement for UniformList { impl IntoElement for UniformList {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
Some(self.id.clone())
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }

View file

@ -828,6 +828,28 @@ where
y: self.origin.y.clone() + self.size.height.clone().half(), y: self.origin.y.clone() + self.size.height.clone().half(),
} }
} }
/// 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 zed::{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<T: Clone + Default + Debug + PartialOrd + Add<T, Output = T> + Sub<Output = T>> Bounds<T> { impl<T: Clone + Default + Debug + PartialOrd + Add<T, Output = T> + Sub<Output = T>> Bounds<T> {
@ -2617,6 +2639,12 @@ pub trait Half {
fn half(&self) -> Self; fn half(&self) -> Self;
} }
impl Half for i32 {
fn half(&self) -> Self {
self / 2
}
}
impl Half for f32 { impl Half for f32 {
fn half(&self) -> Self { fn half(&self) -> Self {
self / 2. self / 2.

View file

@ -70,6 +70,7 @@ mod app;
mod arena; mod arena;
mod assets; mod assets;
mod bounds_tree;
mod color; mod color;
mod element; mod element;
mod elements; mod elements;

View file

@ -54,11 +54,12 @@ use crate::{
KeyContext, Keymap, KeymatchResult, Keystroke, KeystrokeMatcher, WindowContext, KeyContext, Keymap, KeymatchResult, Keystroke, KeystrokeMatcher, WindowContext,
}; };
use collections::FxHashMap; use collections::FxHashMap;
use smallvec::{smallvec, SmallVec}; use smallvec::SmallVec;
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
cell::RefCell, cell::RefCell,
mem, mem,
ops::Range,
rc::Rc, rc::Rc,
}; };
@ -68,6 +69,7 @@ pub(crate) struct DispatchNodeId(usize);
pub(crate) struct DispatchTree { pub(crate) struct DispatchTree {
node_stack: Vec<DispatchNodeId>, node_stack: Vec<DispatchNodeId>,
pub(crate) context_stack: Vec<KeyContext>, pub(crate) context_stack: Vec<KeyContext>,
view_stack: Vec<EntityId>,
nodes: Vec<DispatchNode>, nodes: Vec<DispatchNode>,
focusable_node_ids: FxHashMap<FocusId, DispatchNodeId>, focusable_node_ids: FxHashMap<FocusId, DispatchNodeId>,
view_node_ids: FxHashMap<EntityId, DispatchNodeId>, view_node_ids: FxHashMap<EntityId, DispatchNodeId>,
@ -81,7 +83,7 @@ pub(crate) struct DispatchNode {
pub key_listeners: Vec<KeyListener>, pub key_listeners: Vec<KeyListener>,
pub action_listeners: Vec<DispatchActionListener>, pub action_listeners: Vec<DispatchActionListener>,
pub context: Option<KeyContext>, pub context: Option<KeyContext>,
focus_id: Option<FocusId>, pub focus_id: Option<FocusId>,
view_id: Option<EntityId>, view_id: Option<EntityId>,
parent: Option<DispatchNodeId>, parent: Option<DispatchNodeId>,
} }
@ -99,6 +101,7 @@ impl DispatchTree {
Self { Self {
node_stack: Vec::new(), node_stack: Vec::new(),
context_stack: Vec::new(), context_stack: Vec::new(),
view_stack: Vec::new(),
nodes: Vec::new(), nodes: Vec::new(),
focusable_node_ids: FxHashMap::default(), focusable_node_ids: FxHashMap::default(),
view_node_ids: FxHashMap::default(), view_node_ids: FxHashMap::default(),
@ -111,72 +114,113 @@ impl DispatchTree {
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.node_stack.clear(); self.node_stack.clear();
self.context_stack.clear(); self.context_stack.clear();
self.view_stack.clear();
self.nodes.clear(); self.nodes.clear();
self.focusable_node_ids.clear(); self.focusable_node_ids.clear();
self.view_node_ids.clear(); self.view_node_ids.clear();
self.keystroke_matchers.clear(); self.keystroke_matchers.clear();
} }
pub fn push_node( pub fn len(&self) -> usize {
&mut self, self.nodes.len()
context: Option<KeyContext>, }
focus_id: Option<FocusId>,
view_id: Option<EntityId>, pub fn push_node(&mut self) -> DispatchNodeId {
) {
let parent = self.node_stack.last().copied(); let parent = self.node_stack.last().copied();
let node_id = DispatchNodeId(self.nodes.len()); let node_id = DispatchNodeId(self.nodes.len());
self.nodes.push(DispatchNode { self.nodes.push(DispatchNode {
parent, parent,
focus_id,
view_id,
..Default::default() ..Default::default()
}); });
self.node_stack.push(node_id); self.node_stack.push(node_id);
node_id
}
if let Some(context) = context { pub fn move_to_next_node(&mut self) -> DispatchNodeId {
self.active_node().context = Some(context.clone()); let next_node_id = DispatchNodeId(self.active_node_id().0 + 1);
let next_node_parent = self.nodes[next_node_id.0].parent;
while self.node_stack.last().copied() != next_node_parent {
self.pop_node();
}
self.node_stack.push(next_node_id);
let active_node = &self.nodes[next_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); self.context_stack.push(context);
} }
if let Some(focus_id) = focus_id { next_node_id
self.focusable_node_ids.insert(focus_id, node_id); }
}
if let Some(view_id) = view_id { 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.parent_view_id() != 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_node_ids.insert(view_id, node_id);
self.view_stack.push(view_id);
} }
} }
pub fn parent_view_id(&self) -> Option<EntityId> {
self.view_stack.last().copied()
}
pub fn pop_node(&mut self) { pub fn pop_node(&mut self) {
let node = &self.nodes[self.active_node_id().0]; let node = &self.nodes[self.active_node_id().0];
if node.context.is_some() { if node.context.is_some() {
self.context_stack.pop(); self.context_stack.pop();
} }
if node.view_id.is_some() {
self.view_stack.pop();
}
self.node_stack.pop(); self.node_stack.pop();
} }
fn move_node(&mut self, source: &mut DispatchNode) { fn move_node(&mut self, source: &mut DispatchNode) {
self.push_node(source.context.take(), source.focus_id, source.view_id); self.push_node();
if let Some(context) = source.context.take() {
self.set_key_context(context);
}
if let Some(focus_id) = source.focus_id.take() {
self.set_focus_id(focus_id);
}
if let Some(view_id) = source.view_id.take() {
self.set_view_id(view_id);
}
let target = self.active_node(); let target = self.active_node();
target.key_listeners = mem::take(&mut source.key_listeners); target.key_listeners = mem::take(&mut source.key_listeners);
target.action_listeners = mem::take(&mut source.action_listeners); target.action_listeners = mem::take(&mut source.action_listeners);
} }
pub fn reuse_view(&mut self, view_id: EntityId, source: &mut Self) -> SmallVec<[EntityId; 8]> { pub fn reuse_subtree(
let view_source_node_id = source &mut self,
.view_node_ids range: Range<usize>,
.get(&view_id) source: &mut Self,
.expect("view should exist in previous dispatch tree"); ) -> SmallVec<[EntityId; 8]> {
let view_source_node = &mut source.nodes[view_source_node_id.0]; let mut grafted_view_ids = SmallVec::new();
self.move_node(view_source_node); let mut source_stack = vec![];
let mut grafted_view_ids = smallvec![view_id];
let mut source_stack = vec![*view_source_node_id];
for (source_node_id, source_node) in source for (source_node_id, source_node) in source
.nodes .nodes
.iter_mut() .iter_mut()
.enumerate() .enumerate()
.skip(view_source_node_id.0 + 1) .skip(range.start)
.take(range.len())
{ {
let source_node_id = DispatchNodeId(source_node_id); let source_node_id = DispatchNodeId(source_node_id);
while let Some(source_ancestor) = source_stack.last() { while let Some(source_ancestor) = source_stack.last() {
@ -188,14 +232,10 @@ impl DispatchTree {
} }
} }
if source_stack.is_empty() { source_stack.push(source_node_id);
break; self.move_node(source_node);
} else { if let Some(view_id) = source_node.view_id {
source_stack.push(source_node_id); grafted_view_ids.push(view_id);
self.move_node(source_node);
if let Some(view_id) = source_node.view_id {
grafted_view_ids.push(view_id);
}
} }
} }

View file

@ -1,6 +1,6 @@
// todo!(linux): remove // todo(linux): remove
#![cfg_attr(target_os = "linux", allow(dead_code))] #![cfg_attr(target_os = "linux", allow(dead_code))]
// todo!("windows"): remove // todo("windows"): remove
#![cfg_attr(windows, allow(dead_code))] #![cfg_attr(windows, allow(dead_code))]
mod app_menu; mod app_menu;
@ -63,7 +63,7 @@ pub(crate) fn current_platform() -> Rc<dyn Platform> {
pub(crate) fn current_platform() -> Rc<dyn Platform> { pub(crate) fn current_platform() -> Rc<dyn Platform> {
Rc::new(LinuxPlatform::new()) Rc::new(LinuxPlatform::new())
} }
// todo!("windows") // todo("windows")
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub(crate) fn current_platform() -> Rc<dyn Platform> { pub(crate) fn current_platform() -> Rc<dyn Platform> {
unimplemented!() unimplemented!()

View file

@ -564,7 +564,7 @@ impl BladeRenderer {
} }
PrimitiveBatch::Paths(paths) => { PrimitiveBatch::Paths(paths) => {
let mut encoder = pass.with(&self.pipelines.paths); let mut encoder = pass.with(&self.pipelines.paths);
//todo!(linux): group by texture ID // todo(linux): group by texture ID
for path in paths { for path in paths {
let tile = &self.path_tiles[&path.id]; let tile = &self.path_tiles[&path.id];
let tex_info = self.atlas.get_texture_info(tile.texture_id); let tex_info = self.atlas.get_texture_info(tile.texture_id);

View file

@ -1,7 +1,7 @@
#![allow(non_upper_case_globals)] #![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(non_snake_case)] #![allow(non_snake_case)]
//todo!(linux): remove // todo(linux): remove
#![allow(unused_variables)] #![allow(unused_variables)]
use crate::{PlatformDispatcher, TaskLabel}; use crate::{PlatformDispatcher, TaskLabel};

View file

@ -141,19 +141,19 @@ impl Platform for LinuxPlatform {
self.inner.loop_signal.stop(); self.inner.loop_signal.stop();
} }
//todo!(linux) // todo(linux)
fn restart(&self) {} fn restart(&self) {}
//todo!(linux) // todo(linux)
fn activate(&self, ignoring_other_apps: bool) {} fn activate(&self, ignoring_other_apps: bool) {}
//todo!(linux) // todo(linux)
fn hide(&self) {} fn hide(&self) {}
//todo!(linux) // todo(linux)
fn hide_other_apps(&self) {} fn hide_other_apps(&self) {}
//todo!(linux) // todo(linux)
fn unhide_other_apps(&self) {} fn unhide_other_apps(&self) {}
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> { fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
@ -164,7 +164,7 @@ impl Platform for LinuxPlatform {
self.client.display(id) self.client.display(id)
} }
//todo!(linux) // todo(linux)
fn active_window(&self) -> Option<AnyWindowHandle> { fn active_window(&self) -> Option<AnyWindowHandle> {
None None
} }
@ -328,7 +328,7 @@ impl Platform for LinuxPlatform {
unimplemented!() unimplemented!()
} }
//todo!(linux) // todo(linux)
fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap) {} fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap) {}
fn local_timezone(&self) -> UtcOffset { fn local_timezone(&self) -> UtcOffset {
@ -339,18 +339,18 @@ impl Platform for LinuxPlatform {
unimplemented!() unimplemented!()
} }
//todo!(linux) // todo(linux)
fn set_cursor_style(&self, style: CursorStyle) {} fn set_cursor_style(&self, style: CursorStyle) {}
//todo!(linux) // todo(linux)
fn should_auto_hide_scrollbars(&self) -> bool { fn should_auto_hide_scrollbars(&self) -> bool {
false false
} }
//todo!(linux) // todo(linux)
fn write_to_clipboard(&self, item: ClipboardItem) {} fn write_to_clipboard(&self, item: ClipboardItem) {}
//todo!(linux) // todo(linux)
fn read_from_clipboard(&self) -> Option<ClipboardItem> { fn read_from_clipboard(&self) -> Option<ClipboardItem> {
None None
} }

View file

@ -32,7 +32,7 @@ impl LinuxTextSystem {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
let mut font_system = FontSystem::new(); let mut font_system = FontSystem::new();
// todo!(linux) make font loading non-blocking // todo(linux) make font loading non-blocking
font_system.db_mut().load_system_fonts(); font_system.db_mut().load_system_fonts();
Self(RwLock::new(LinuxTextSystemState { Self(RwLock::new(LinuxTextSystemState {
@ -59,7 +59,7 @@ impl PlatformTextSystem for LinuxTextSystem {
self.0.write().add_fonts(fonts) self.0.write().add_fonts(fonts)
} }
// todo!(linux) ensure that this integrates with platform font loading // todo(linux) ensure that this integrates with platform font loading
// do we need to do more than call load_system_fonts()? // do we need to do more than call load_system_fonts()?
fn all_font_names(&self) -> Vec<String> { fn all_font_names(&self) -> Vec<String> {
self.0 self.0
@ -71,13 +71,13 @@ impl PlatformTextSystem for LinuxTextSystem {
.collect() .collect()
} }
// todo!(linux) // todo(linux)
fn all_font_families(&self) -> Vec<String> { fn all_font_families(&self) -> Vec<String> {
Vec::new() Vec::new()
} }
fn font_id(&self, font: &Font) -> Result<FontId> { fn font_id(&self, font: &Font) -> Result<FontId> {
// todo!(linux): Do we need to use CosmicText's Font APIs? Can we consolidate this to use font_kit? // todo(linux): Do we need to use CosmicText's Font APIs? Can we consolidate this to use font_kit?
let lock = self.0.upgradable_read(); let lock = self.0.upgradable_read();
if let Some(font_id) = lock.font_selections.get(font) { if let Some(font_id) = lock.font_selections.get(font) {
Ok(*font_id) Ok(*font_id)
@ -127,13 +127,13 @@ impl PlatformTextSystem for LinuxTextSystem {
FontMetrics { FontMetrics {
units_per_em: metrics.units_per_em as u32, units_per_em: metrics.units_per_em as u32,
ascent: metrics.ascent, ascent: metrics.ascent,
descent: -metrics.descent, // todo!(linux) confirm this is correct descent: -metrics.descent, // todo(linux) confirm this is correct
line_gap: metrics.leading, line_gap: metrics.leading,
underline_position: metrics.underline_offset, underline_position: metrics.underline_offset,
underline_thickness: metrics.stroke_size, underline_thickness: metrics.stroke_size,
cap_height: metrics.cap_height, cap_height: metrics.cap_height,
x_height: metrics.x_height, x_height: metrics.x_height,
// todo!(linux): Compute this correctly // todo(linux): Compute this correctly
bounding_box: Bounds { bounding_box: Bounds {
origin: point(0.0, 0.0), origin: point(0.0, 0.0),
size: size(metrics.max_width, metrics.ascent + metrics.descent), size: size(metrics.max_width, metrics.ascent + metrics.descent),
@ -146,7 +146,7 @@ impl PlatformTextSystem for LinuxTextSystem {
let metrics = lock.fonts[font_id.0].as_swash().metrics(&[]); let metrics = lock.fonts[font_id.0].as_swash().metrics(&[]);
let glyph_metrics = lock.fonts[font_id.0].as_swash().glyph_metrics(&[]); let glyph_metrics = lock.fonts[font_id.0].as_swash().glyph_metrics(&[]);
let glyph_id = glyph_id.0 as u16; let glyph_id = glyph_id.0 as u16;
// todo!(linux): Compute this correctly // todo(linux): Compute this correctly
// see https://github.com/servo/font-kit/blob/master/src/loaders/freetype.rs#L614-L620 // see https://github.com/servo/font-kit/blob/master/src/loaders/freetype.rs#L614-L620
Ok(Bounds { Ok(Bounds {
origin: point(0.0, 0.0), origin: point(0.0, 0.0),
@ -181,7 +181,7 @@ impl PlatformTextSystem for LinuxTextSystem {
self.0.write().layout_line(text, font_size, runs) self.0.write().layout_line(text, font_size, runs)
} }
// todo!(linux) Confirm that this has been superseded by the LineWrapper // todo(linux) Confirm that this has been superseded by the LineWrapper
fn wrap_line( fn wrap_line(
&self, &self,
text: &str, text: &str,
@ -256,7 +256,7 @@ impl LinuxTextSystemState {
} }
fn is_emoji(&self, font_id: FontId) -> bool { fn is_emoji(&self, font_id: FontId) -> bool {
// todo!(linux): implement this correctly // todo(linux): implement this correctly
self.postscript_names_by_font_id self.postscript_names_by_font_id
.get(&font_id) .get(&font_id)
.map_or(false, |postscript_name| { .map_or(false, |postscript_name| {
@ -264,7 +264,7 @@ impl LinuxTextSystemState {
}) })
} }
// todo!(linux) both raster functions have problems because I am not sure this is the correct mapping from cosmic text to gpui system // todo(linux) both raster functions have problems because I am not sure this is the correct mapping from cosmic text to gpui system
fn raster_bounds(&mut self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> { fn raster_bounds(&mut self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
let font = &self.fonts[params.font_id.0]; let font = &self.fonts[params.font_id.0];
let font_system = &mut self.font_system; let font_system = &mut self.font_system;
@ -297,7 +297,7 @@ impl LinuxTextSystemState {
if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 { if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 {
Err(anyhow!("glyph bounds are empty")) Err(anyhow!("glyph bounds are empty"))
} else { } else {
// todo!(linux) handle subpixel variants // todo(linux) handle subpixel variants
let bitmap_size = glyph_bounds.size; let bitmap_size = glyph_bounds.size;
let font = &self.fonts[params.font_id.0]; let font = &self.fonts[params.font_id.0];
let font_system = &mut self.font_system; let font_system = &mut self.font_system;
@ -320,13 +320,13 @@ impl LinuxTextSystemState {
} }
} }
// todo!(linux) This is all a quick first pass, maybe we should be using cosmic_text::Buffer // todo(linux) This is all a quick first pass, maybe we should be using cosmic_text::Buffer
#[profiling::function] #[profiling::function]
fn layout_line(&mut self, text: &str, font_size: Pixels, font_runs: &[FontRun]) -> LineLayout { fn layout_line(&mut self, text: &str, font_size: Pixels, font_runs: &[FontRun]) -> LineLayout {
let mut attrs_list = AttrsList::new(Attrs::new()); let mut attrs_list = AttrsList::new(Attrs::new());
let mut offs = 0; let mut offs = 0;
for run in font_runs { for run in font_runs {
// todo!(linux) We need to check we are doing utf properly // todo(linux) We need to check we are doing utf properly
let font = &self.fonts[run.font_id.0]; let font = &self.fonts[run.font_id.0];
let font = self.font_system.db().face(font.id()).unwrap(); let font = self.font_system.db().face(font.id()).unwrap();
attrs_list.add_span( attrs_list.add_span(
@ -343,11 +343,11 @@ impl LinuxTextSystemState {
let layout = line.layout( let layout = line.layout(
&mut self.font_system, &mut self.font_system,
font_size.0, font_size.0,
f32::MAX, // todo!(linux) we don't have a width cause this should technically not be wrapped I believe f32::MAX, // todo(linux) we don't have a width cause this should technically not be wrapped I believe
cosmic_text::Wrap::None, cosmic_text::Wrap::None,
); );
let mut runs = Vec::new(); let mut runs = Vec::new();
// todo!(linux) what I think can happen is layout returns possibly multiple lines which means we should be probably working with it higher up in the text rendering // todo(linux) what I think can happen is layout returns possibly multiple lines which means we should be probably working with it higher up in the text rendering
let layout = layout.first().unwrap(); let layout = layout.first().unwrap();
for glyph in &layout.glyphs { for glyph in &layout.glyphs {
let font_id = glyph.font_id; let font_id = glyph.font_id;
@ -358,7 +358,7 @@ impl LinuxTextSystemState {
.unwrap(), .unwrap(),
); );
let mut glyphs = SmallVec::new(); let mut glyphs = SmallVec::new();
// todo!(linux) this is definitely wrong, each glyph in glyphs from cosmic-text is a cluster with one glyph, ShapedRun takes a run of glyphs with the same font and direction // todo(linux) this is definitely wrong, each glyph in glyphs from cosmic-text is a cluster with one glyph, ShapedRun takes a run of glyphs with the same font and direction
glyphs.push(ShapedGlyph { glyphs.push(ShapedGlyph {
id: GlyphId(glyph.glyph_id as u32), id: GlyphId(glyph.glyph_id as u32),
position: point((glyph.x).into(), glyph.y.into()), position: point((glyph.x).into(), glyph.y.into()),

View file

@ -1,4 +1,4 @@
//todo!(linux): remove this once the relevant functionality has been implemented // todo(linux): remove this once the relevant functionality has been implemented
#![allow(unused_variables)] #![allow(unused_variables)]
pub(crate) use client::*; pub(crate) use client::*;

View file

@ -183,7 +183,7 @@ impl Client for WaylandClient {
let decoration = let decoration =
decoration_manager.get_toplevel_decoration(&toplevel, &self.qh, xdg_surface.id()); decoration_manager.get_toplevel_decoration(&toplevel, &self.qh, xdg_surface.id());
// todo!(linux) - options.titlebar is lacking information required for wayland. // todo(linux) - options.titlebar is lacking information required for wayland.
// Especially, whether a titlebar is wanted in itself. // Especially, whether a titlebar is wanted in itself.
// //
// Removing the titlebar also removes the entire window frame (ie. the ability to // Removing the titlebar also removes the entire window frame (ie. the ability to
@ -482,7 +482,7 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientState {
wl_keyboard::KeyState::Pressed => { wl_keyboard::KeyState::Pressed => {
let input = PlatformInput::KeyDown(KeyDownEvent { let input = PlatformInput::KeyDown(KeyDownEvent {
keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode), keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
is_held: false, // todo!(linux) is_held: false, // todo(linux)
}); });
focused_window.handle_input(input.clone()); focused_window.handle_input(input.clone());

View file

@ -8,17 +8,17 @@ use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size};
pub(crate) struct WaylandDisplay {} pub(crate) struct WaylandDisplay {}
impl PlatformDisplay for WaylandDisplay { impl PlatformDisplay for WaylandDisplay {
// todo!(linux) // todo(linux)
fn id(&self) -> DisplayId { fn id(&self) -> DisplayId {
DisplayId(123) // return some fake data so it doesn't panic DisplayId(123) // return some fake data so it doesn't panic
} }
// todo!(linux) // todo(linux)
fn uuid(&self) -> anyhow::Result<Uuid> { fn uuid(&self) -> anyhow::Result<Uuid> {
Ok(Uuid::from_bytes([0; 16])) // return some fake data so it doesn't panic Ok(Uuid::from_bytes([0; 16])) // return some fake data so it doesn't panic
} }
// todo!(linux) // todo(linux)
fn bounds(&self) -> Bounds<GlobalPixels> { fn bounds(&self) -> Bounds<GlobalPixels> {
Bounds { Bounds {
origin: Default::default(), origin: Default::default(),

View file

@ -132,7 +132,7 @@ impl WaylandWindowState {
size: Size { size: Size {
width: 500, width: 500,
height: 500, height: 500,
}, //todo!(implement) }, // todo(implement)
}, },
WindowBounds::Fixed(bounds) => bounds.map(|p| p.0 as i32), WindowBounds::Fixed(bounds) => bounds.map(|p| p.0 as i32),
}; };
@ -200,7 +200,7 @@ impl WaylandWindowState {
pub fn set_decoration_state(&self, state: WaylandDecorationState) { pub fn set_decoration_state(&self, state: WaylandDecorationState) {
self.inner.borrow_mut().decoration_state = state; self.inner.borrow_mut().decoration_state = state;
log::trace!("Window decorations are now handled by {:?}", state); log::trace!("Window decorations are now handled by {:?}", state);
// todo!(linux) - Handle this properly // todo(linux) - Handle this properly
} }
pub fn close(&self) { pub fn close(&self) {
@ -250,7 +250,7 @@ impl HasDisplayHandle for WaylandWindow {
} }
impl PlatformWindow for WaylandWindow { impl PlatformWindow for WaylandWindow {
//todo!(linux) // todo(linux)
fn bounds(&self) -> WindowBounds { fn bounds(&self) -> WindowBounds {
WindowBounds::Maximized WindowBounds::Maximized
} }
@ -267,32 +267,32 @@ impl PlatformWindow for WaylandWindow {
self.0.inner.borrow_mut().scale self.0.inner.borrow_mut().scale
} }
//todo!(linux) // todo(linux)
fn titlebar_height(&self) -> Pixels { fn titlebar_height(&self) -> Pixels {
unimplemented!() unimplemented!()
} }
// todo!(linux) // todo(linux)
fn appearance(&self) -> WindowAppearance { fn appearance(&self) -> WindowAppearance {
WindowAppearance::Light WindowAppearance::Light
} }
// todo!(linux) // todo(linux)
fn display(&self) -> Rc<dyn PlatformDisplay> { fn display(&self) -> Rc<dyn PlatformDisplay> {
Rc::new(WaylandDisplay {}) Rc::new(WaylandDisplay {})
} }
// todo!(linux) // todo(linux)
fn mouse_position(&self) -> Point<Pixels> { fn mouse_position(&self) -> Point<Pixels> {
Point::default() Point::default()
} }
//todo!(linux) // todo(linux)
fn modifiers(&self) -> Modifiers { fn modifiers(&self) -> Modifiers {
crate::Modifiers::default() crate::Modifiers::default()
} }
//todo!(linux) // todo(linux)
fn as_any_mut(&mut self) -> &mut dyn Any { fn as_any_mut(&mut self) -> &mut dyn Any {
unimplemented!() unimplemented!()
} }
@ -305,7 +305,7 @@ impl PlatformWindow for WaylandWindow {
self.0.inner.borrow_mut().input_handler.take() self.0.inner.borrow_mut().input_handler.take()
} }
//todo!(linux) // todo(linux)
fn prompt( fn prompt(
&self, &self,
level: PromptLevel, level: PromptLevel,
@ -317,7 +317,7 @@ impl PlatformWindow for WaylandWindow {
} }
fn activate(&self) { fn activate(&self) {
//todo!(linux) // todo(linux)
} }
fn set_title(&mut self, title: &str) { fn set_title(&mut self, title: &str) {
@ -325,23 +325,23 @@ impl PlatformWindow for WaylandWindow {
} }
fn set_edited(&mut self, edited: bool) { fn set_edited(&mut self, edited: bool) {
//todo!(linux) // todo(linux)
} }
fn show_character_palette(&self) { fn show_character_palette(&self) {
//todo!(linux) // todo(linux)
} }
fn minimize(&self) { fn minimize(&self) {
//todo!(linux) // todo(linux)
} }
fn zoom(&self) { fn zoom(&self) {
//todo!(linux) // todo(linux)
} }
fn toggle_full_screen(&self) { fn toggle_full_screen(&self) {
//todo!(linux) // todo(linux)
} }
fn on_request_frame(&self, callback: Box<dyn FnMut()>) { fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
@ -361,7 +361,7 @@ impl PlatformWindow for WaylandWindow {
} }
fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) { fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
//todo!(linux) // todo(linux)
} }
fn on_moved(&self, callback: Box<dyn FnMut()>) { fn on_moved(&self, callback: Box<dyn FnMut()>) {
@ -377,10 +377,10 @@ impl PlatformWindow for WaylandWindow {
} }
fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) { fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
//todo!(linux) // todo(linux)
} }
// todo!(linux) // todo(linux)
fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool { fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
false false
} }

View file

@ -1,4 +1,4 @@
//todo!(linux): remove // todo(linux): remove
#![allow(unused)] #![allow(unused)]
use crate::{ use crate::{
@ -99,7 +99,7 @@ pub(crate) struct X11WindowState {
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct X11Window(pub(crate) Rc<X11WindowState>); pub(crate) struct X11Window(pub(crate) Rc<X11WindowState>);
//todo!(linux): Remove other RawWindowHandle implementation // todo(linux): Remove other RawWindowHandle implementation
unsafe impl blade_rwh::HasRawWindowHandle for RawWindow { unsafe impl blade_rwh::HasRawWindowHandle for RawWindow {
fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle { fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle {
let mut wh = blade_rwh::XcbWindowHandle::empty(); let mut wh = blade_rwh::XcbWindowHandle::empty();
@ -301,7 +301,7 @@ impl X11WindowState {
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.borrow_mut();
let old_bounds = mem::replace(&mut inner.bounds, bounds); let old_bounds = mem::replace(&mut inner.bounds, bounds);
do_move = old_bounds.origin != bounds.origin; do_move = old_bounds.origin != bounds.origin;
//todo!(linux): use normal GPUI types here, refactor out the double // todo(linux): use normal GPUI types here, refactor out the double
// viewport check and extra casts ( ) // viewport check and extra casts ( )
let gpu_size = query_render_extent(&self.xcb_connection, self.x_window); let gpu_size = query_render_extent(&self.xcb_connection, self.x_window);
if inner.renderer.viewport_size() != gpu_size { if inner.renderer.viewport_size() != gpu_size {
@ -377,12 +377,12 @@ impl PlatformWindow for X11Window {
self.0.inner.borrow_mut().scale_factor self.0.inner.borrow_mut().scale_factor
} }
//todo!(linux) // todo(linux)
fn titlebar_height(&self) -> Pixels { fn titlebar_height(&self) -> Pixels {
unimplemented!() unimplemented!()
} }
//todo!(linux) // todo(linux)
fn appearance(&self) -> WindowAppearance { fn appearance(&self) -> WindowAppearance {
WindowAppearance::Light WindowAppearance::Light
} }
@ -402,7 +402,7 @@ impl PlatformWindow for X11Window {
) )
} }
//todo!(linux) // todo(linux)
fn modifiers(&self) -> Modifiers { fn modifiers(&self) -> Modifiers {
Modifiers::default() Modifiers::default()
} }
@ -419,7 +419,7 @@ impl PlatformWindow for X11Window {
self.0.inner.borrow_mut().input_handler.take() self.0.inner.borrow_mut().input_handler.take()
} }
//todo!(linux) // todo(linux)
fn prompt( fn prompt(
&self, &self,
_level: PromptLevel, _level: PromptLevel,
@ -447,10 +447,10 @@ impl PlatformWindow for X11Window {
}); });
} }
//todo!(linux) // todo(linux)
fn set_edited(&mut self, edited: bool) {} fn set_edited(&mut self, edited: bool) {}
//todo!(linux), this corresponds to `orderFrontCharacterPalette` on macOS, // todo(linux), this corresponds to `orderFrontCharacterPalette` on macOS,
// but it looks like the equivalent for Linux is GTK specific: // but it looks like the equivalent for Linux is GTK specific:
// //
// https://docs.gtk.org/gtk3/signal.Entry.insert-emoji.html // https://docs.gtk.org/gtk3/signal.Entry.insert-emoji.html
@ -460,17 +460,17 @@ impl PlatformWindow for X11Window {
unimplemented!() unimplemented!()
} }
//todo!(linux) // todo(linux)
fn minimize(&self) { fn minimize(&self) {
unimplemented!() unimplemented!()
} }
//todo!(linux) // todo(linux)
fn zoom(&self) { fn zoom(&self) {
unimplemented!() unimplemented!()
} }
//todo!(linux) // todo(linux)
fn toggle_full_screen(&self) { fn toggle_full_screen(&self) {
unimplemented!() unimplemented!()
} }
@ -511,7 +511,7 @@ impl PlatformWindow for X11Window {
self.0.callbacks.borrow_mut().appearance_changed = Some(callback); self.0.callbacks.borrow_mut().appearance_changed = Some(callback);
} }
//todo!(linux) // todo(linux)
fn is_topmost_for_position(&self, _position: Point<Pixels>) -> bool { fn is_topmost_for_position(&self, _position: Point<Pixels>) -> bool {
unimplemented!() unimplemented!()
} }

View file

@ -293,6 +293,7 @@ impl MetalRenderer {
znear: 0.0, znear: 0.0,
zfar: 1.0, zfar: 1.0,
}); });
for batch in scene.batches() { for batch in scene.batches() {
let ok = match batch { let ok = match batch {
PrimitiveBatch::Shadows(shadows) => self.draw_shadows( PrimitiveBatch::Shadows(shadows) => self.draw_shadows(

View file

@ -126,7 +126,7 @@ impl Platform for TestPlatform {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
return Arc::new(crate::platform::mac::MacTextSystem::new()); return Arc::new(crate::platform::mac::MacTextSystem::new());
// todo!("windows") // todo("windows")
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
unimplemented!() unimplemented!()
} }

View file

@ -7,7 +7,7 @@ use std::borrow::Cow;
pub(crate) struct TestTextSystem {} pub(crate) struct TestTextSystem {}
//todo!(linux) // todo(linux)
#[allow(unused)] #[allow(unused)]
impl PlatformTextSystem for TestTextSystem { impl PlatformTextSystem for TestTextSystem {
fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> { fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {

View file

@ -1,48 +1,21 @@
// todo!("windows"): remove // todo("windows"): remove
#![cfg_attr(windows, allow(dead_code))] #![cfg_attr(windows, allow(dead_code))]
use crate::{ use crate::{
point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, EntityId, Hsla, Pixels, bounds_tree::BoundsTree, point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges,
Point, ScaledPixels, StackingOrder, Hsla, Pixels, Point, ScaledPixels,
}; };
use collections::{BTreeMap, FxHashSet};
use std::{fmt::Debug, iter::Peekable, slice}; use std::{fmt::Debug, iter::Peekable, slice};
#[allow(non_camel_case_types, unused)] #[allow(non_camel_case_types, unused)]
pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>; pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
pub(crate) type LayerId = u32;
pub(crate) type DrawOrder = u32; pub(crate) type DrawOrder = u32;
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[repr(C)]
pub(crate) struct ViewId {
low_bits: u32,
high_bits: u32,
}
impl From<EntityId> for ViewId {
fn from(value: EntityId) -> Self {
let value = value.as_u64();
Self {
low_bits: value as u32,
high_bits: (value >> 32) as u32,
}
}
}
impl From<ViewId> for EntityId {
fn from(value: ViewId) -> Self {
let value = (value.low_bits as u64) | ((value.high_bits as u64) << 32);
value.into()
}
}
#[derive(Default)] #[derive(Default)]
pub(crate) struct Scene { pub(crate) struct Scene {
last_layer: Option<(StackingOrder, LayerId)>, pub(crate) primitives: Vec<Primitive>,
layers_by_order: BTreeMap<StackingOrder, LayerId>, primitive_bounds: BoundsTree<ScaledPixels, ()>,
orders_by_layer: BTreeMap<LayerId, StackingOrder>,
pub(crate) shadows: Vec<Shadow>, pub(crate) shadows: Vec<Shadow>,
pub(crate) quads: Vec<Quad>, pub(crate) quads: Vec<Quad>,
pub(crate) paths: Vec<Path<ScaledPixels>>, pub(crate) paths: Vec<Path<ScaledPixels>>,
@ -54,12 +27,11 @@ pub(crate) struct Scene {
impl Scene { impl Scene {
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.last_layer = None; self.primitives.clear();
self.layers_by_order.clear(); self.primitive_bounds.clear();
self.orders_by_layer.clear(); self.paths.clear();
self.shadows.clear(); self.shadows.clear();
self.quads.clear(); self.quads.clear();
self.paths.clear();
self.underlines.clear(); self.underlines.clear();
self.monochrome_sprites.clear(); self.monochrome_sprites.clear();
self.polychrome_sprites.clear(); self.polychrome_sprites.clear();
@ -70,6 +42,66 @@ impl Scene {
&self.paths &self.paths
} }
pub fn len(&self) -> usize {
self.primitives.len()
}
pub(crate) fn push(&mut self, primitive: impl Into<Primitive>) {
let mut primitive = primitive.into();
let clipped_bounds = primitive
.bounds()
.intersect(&primitive.content_mask().bounds);
if clipped_bounds.size.width <= ScaledPixels(0.)
|| clipped_bounds.size.height <= ScaledPixels(0.)
{
return;
}
let order = self.primitive_bounds.insert(clipped_bounds, ());
match &mut primitive {
Primitive::Shadow(shadow) => {
shadow.order = order;
self.shadows.push(shadow.clone());
}
Primitive::Quad(quad) => {
quad.order = order;
self.quads.push(quad.clone());
}
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.clone());
}
Primitive::MonochromeSprite(sprite) => {
sprite.order = order;
self.monochrome_sprites.push(sprite.clone());
}
Primitive::PolychromeSprite(sprite) => {
sprite.order = order;
self.polychrome_sprites.push(sprite.clone());
}
Primitive::Surface(surface) => {
surface.order = order;
self.surfaces.push(surface.clone());
}
}
self.primitives.push(primitive);
}
pub fn finish(&mut self) {
self.shadows.sort_unstable();
self.quads.sort_unstable();
self.paths.sort_unstable();
self.underlines.sort_unstable();
self.monochrome_sprites.sort_unstable();
self.polychrome_sprites.sort_unstable();
self.surfaces.sort_unstable();
}
pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> { pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> {
BatchIterator { BatchIterator {
shadows: &self.shadows, shadows: &self.shadows,
@ -95,162 +127,54 @@ impl Scene {
surfaces_iter: self.surfaces.iter().peekable(), surfaces_iter: self.surfaces.iter().peekable(),
} }
} }
}
pub(crate) fn insert(&mut self, order: &StackingOrder, primitive: impl Into<Primitive>) { #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
let primitive = primitive.into(); pub(crate) enum PrimitiveKind {
let clipped_bounds = primitive Shadow,
.bounds() #[default]
.intersect(&primitive.content_mask().bounds); Quad,
if clipped_bounds.size.width <= ScaledPixels(0.) Path,
|| clipped_bounds.size.height <= ScaledPixels(0.) Underline,
{ MonochromeSprite,
return; PolychromeSprite,
} Surface,
}
let layer_id = self.layer_id_for_order(order); #[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
match primitive { pub(crate) enum Primitive {
Primitive::Shadow(mut shadow) => { Shadow(Shadow),
shadow.layer_id = layer_id; Quad(Quad),
self.shadows.push(shadow); Path(Path<ScaledPixels>),
} Underline(Underline),
Primitive::Quad(mut quad) => { MonochromeSprite(MonochromeSprite),
quad.layer_id = layer_id; PolychromeSprite(PolychromeSprite),
self.quads.push(quad); Surface(Surface),
} }
Primitive::Path(mut path) => {
path.layer_id = layer_id; impl Primitive {
path.id = PathId(self.paths.len()); pub fn bounds(&self) -> &Bounds<ScaledPixels> {
self.paths.push(path); match self {
} Primitive::Shadow(shadow) => &shadow.bounds,
Primitive::Underline(mut underline) => { Primitive::Quad(quad) => &quad.bounds,
underline.layer_id = layer_id; Primitive::Path(path) => &path.bounds,
self.underlines.push(underline); Primitive::Underline(underline) => &underline.bounds,
} Primitive::MonochromeSprite(sprite) => &sprite.bounds,
Primitive::MonochromeSprite(mut sprite) => { Primitive::PolychromeSprite(sprite) => &sprite.bounds,
sprite.layer_id = layer_id; Primitive::Surface(surface) => &surface.bounds,
self.monochrome_sprites.push(sprite);
}
Primitive::PolychromeSprite(mut sprite) => {
sprite.layer_id = layer_id;
self.polychrome_sprites.push(sprite);
}
Primitive::Surface(mut surface) => {
surface.layer_id = layer_id;
self.surfaces.push(surface);
}
} }
} }
fn layer_id_for_order(&mut self, order: &StackingOrder) -> LayerId { pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
if let Some((last_order, last_layer_id)) = self.last_layer.as_ref() { match self {
if order == last_order { Primitive::Shadow(shadow) => &shadow.content_mask,
return *last_layer_id; Primitive::Quad(quad) => &quad.content_mask,
} Primitive::Path(path) => &path.content_mask,
Primitive::Underline(underline) => &underline.content_mask,
Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
Primitive::Surface(surface) => &surface.content_mask,
} }
let layer_id = if let Some(layer_id) = self.layers_by_order.get(order) {
*layer_id
} else {
let next_id = self.layers_by_order.len() as LayerId;
self.layers_by_order.insert(order.clone(), next_id);
self.orders_by_layer.insert(next_id, order.clone());
next_id
};
self.last_layer = Some((order.clone(), layer_id));
layer_id
}
pub fn reuse_views(&mut self, views: &FxHashSet<EntityId>, prev_scene: &mut Self) {
for shadow in prev_scene.shadows.drain(..) {
if views.contains(&shadow.view_id.into()) {
let order = &prev_scene.orders_by_layer[&shadow.layer_id];
self.insert(order, shadow);
}
}
for quad in prev_scene.quads.drain(..) {
if views.contains(&quad.view_id.into()) {
let order = &prev_scene.orders_by_layer[&quad.layer_id];
self.insert(order, quad);
}
}
for path in prev_scene.paths.drain(..) {
if views.contains(&path.view_id.into()) {
let order = &prev_scene.orders_by_layer[&path.layer_id];
self.insert(order, path);
}
}
for underline in prev_scene.underlines.drain(..) {
if views.contains(&underline.view_id.into()) {
let order = &prev_scene.orders_by_layer[&underline.layer_id];
self.insert(order, underline);
}
}
for sprite in prev_scene.monochrome_sprites.drain(..) {
if views.contains(&sprite.view_id.into()) {
let order = &prev_scene.orders_by_layer[&sprite.layer_id];
self.insert(order, sprite);
}
}
for sprite in prev_scene.polychrome_sprites.drain(..) {
if views.contains(&sprite.view_id.into()) {
let order = &prev_scene.orders_by_layer[&sprite.layer_id];
self.insert(order, sprite);
}
}
for surface in prev_scene.surfaces.drain(..) {
if views.contains(&surface.view_id.into()) {
let order = &prev_scene.orders_by_layer[&surface.layer_id];
self.insert(order, surface);
}
}
}
pub fn finish(&mut self) {
let mut orders = vec![0; self.layers_by_order.len()];
for (ix, layer_id) in self.layers_by_order.values().enumerate() {
orders[*layer_id as usize] = ix as u32;
}
for shadow in &mut self.shadows {
shadow.order = orders[shadow.layer_id as usize];
}
self.shadows.sort_by_key(|shadow| shadow.order);
for quad in &mut self.quads {
quad.order = orders[quad.layer_id as usize];
}
self.quads.sort_by_key(|quad| quad.order);
for path in &mut self.paths {
path.order = orders[path.layer_id as usize];
}
self.paths.sort_by_key(|path| path.order);
for underline in &mut self.underlines {
underline.order = orders[underline.layer_id as usize];
}
self.underlines.sort_by_key(|underline| underline.order);
for monochrome_sprite in &mut self.monochrome_sprites {
monochrome_sprite.order = orders[monochrome_sprite.layer_id as usize];
}
self.monochrome_sprites.sort_by_key(|sprite| sprite.order);
for polychrome_sprite in &mut self.polychrome_sprites {
polychrome_sprite.order = orders[polychrome_sprite.layer_id as usize];
}
self.polychrome_sprites.sort_by_key(|sprite| sprite.order);
for surface in &mut self.surfaces {
surface.order = orders[surface.layer_id as usize];
}
self.surfaces.sort_by_key(|surface| surface.order);
} }
} }
@ -439,54 +363,6 @@ impl<'a> Iterator for BatchIterator<'a> {
} }
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
pub(crate) enum PrimitiveKind {
Shadow,
#[default]
Quad,
Path,
Underline,
MonochromeSprite,
PolychromeSprite,
Surface,
}
pub(crate) enum Primitive {
Shadow(Shadow),
Quad(Quad),
Path(Path<ScaledPixels>),
Underline(Underline),
MonochromeSprite(MonochromeSprite),
PolychromeSprite(PolychromeSprite),
Surface(Surface),
}
impl Primitive {
pub fn bounds(&self) -> &Bounds<ScaledPixels> {
match self {
Primitive::Shadow(shadow) => &shadow.bounds,
Primitive::Quad(quad) => &quad.bounds,
Primitive::Path(path) => &path.bounds,
Primitive::Underline(underline) => &underline.bounds,
Primitive::MonochromeSprite(sprite) => &sprite.bounds,
Primitive::PolychromeSprite(sprite) => &sprite.bounds,
Primitive::Surface(surface) => &surface.bounds,
}
}
pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
match self {
Primitive::Shadow(shadow) => &shadow.content_mask,
Primitive::Quad(quad) => &quad.content_mask,
Primitive::Path(path) => &path.content_mask,
Primitive::Underline(underline) => &underline.content_mask,
Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
Primitive::Surface(surface) => &surface.content_mask,
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum PrimitiveBatch<'a> { pub(crate) enum PrimitiveBatch<'a> {
Shadows(&'a [Shadow]), Shadows(&'a [Shadow]),
@ -507,8 +383,6 @@ pub(crate) enum PrimitiveBatch<'a> {
#[derive(Default, Debug, Clone, Eq, PartialEq)] #[derive(Default, Debug, Clone, Eq, PartialEq)]
#[repr(C)] #[repr(C)]
pub(crate) struct Quad { pub(crate) struct Quad {
pub view_id: ViewId,
pub layer_id: LayerId,
pub order: DrawOrder, pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>, pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>, pub content_mask: ContentMask<ScaledPixels>,
@ -539,8 +413,6 @@ impl From<Quad> for Primitive {
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
#[repr(C)] #[repr(C)]
pub(crate) struct Underline { pub(crate) struct Underline {
pub view_id: ViewId,
pub layer_id: LayerId,
pub order: DrawOrder, pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>, pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>, pub content_mask: ContentMask<ScaledPixels>,
@ -570,8 +442,6 @@ impl From<Underline> for Primitive {
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
#[repr(C)] #[repr(C)]
pub(crate) struct Shadow { pub(crate) struct Shadow {
pub view_id: ViewId,
pub layer_id: LayerId,
pub order: DrawOrder, pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>, pub bounds: Bounds<ScaledPixels>,
pub corner_radii: Corners<ScaledPixels>, pub corner_radii: Corners<ScaledPixels>,
@ -602,8 +472,6 @@ impl From<Shadow> for Primitive {
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
#[repr(C)] #[repr(C)]
pub(crate) struct MonochromeSprite { pub(crate) struct MonochromeSprite {
pub view_id: ViewId,
pub layer_id: LayerId,
pub order: DrawOrder, pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>, pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>, pub content_mask: ContentMask<ScaledPixels>,
@ -635,8 +503,6 @@ impl From<MonochromeSprite> for Primitive {
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
#[repr(C)] #[repr(C)]
pub(crate) struct PolychromeSprite { pub(crate) struct PolychromeSprite {
pub view_id: ViewId,
pub layer_id: LayerId,
pub order: DrawOrder, pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>, pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>, pub content_mask: ContentMask<ScaledPixels>,
@ -669,8 +535,6 @@ impl From<PolychromeSprite> for Primitive {
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct Surface { pub(crate) struct Surface {
pub view_id: ViewId,
pub layer_id: LayerId,
pub order: DrawOrder, pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>, pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>, pub content_mask: ContentMask<ScaledPixels>,
@ -700,11 +564,9 @@ impl From<Surface> for Primitive {
pub(crate) struct PathId(pub(crate) usize); pub(crate) struct PathId(pub(crate) usize);
/// A line made up of a series of vertices and control points. /// A line made up of a series of vertices and control points.
#[derive(Debug)] #[derive(Clone, Debug)]
pub struct Path<P: Clone + Default + Debug> { pub struct Path<P: Clone + Default + Debug> {
pub(crate) id: PathId, pub(crate) id: PathId,
pub(crate) view_id: ViewId,
layer_id: LayerId,
order: DrawOrder, order: DrawOrder,
pub(crate) bounds: Bounds<P>, pub(crate) bounds: Bounds<P>,
pub(crate) content_mask: ContentMask<P>, pub(crate) content_mask: ContentMask<P>,
@ -720,8 +582,6 @@ impl Path<Pixels> {
pub fn new(start: Point<Pixels>) -> Self { pub fn new(start: Point<Pixels>) -> Self {
Self { Self {
id: PathId(0), id: PathId(0),
view_id: ViewId::default(),
layer_id: LayerId::default(),
order: DrawOrder::default(), order: DrawOrder::default(),
vertices: Vec::new(), vertices: Vec::new(),
start, start,
@ -740,8 +600,6 @@ impl Path<Pixels> {
pub fn scale(&self, factor: f32) -> Path<ScaledPixels> { pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
Path { Path {
id: self.id, id: self.id,
view_id: self.view_id,
layer_id: self.layer_id,
order: self.order, order: self.order,
bounds: self.bounds.scale(factor), bounds: self.bounds.scale(factor),
content_mask: self.content_mask.scale(factor), content_mask: self.content_mask.scale(factor),

View file

@ -115,9 +115,6 @@ pub struct Style {
/// The mouse cursor style shown when the mouse pointer is over an element. /// The mouse cursor style shown when the mouse pointer is over an element.
pub mouse_cursor: Option<CursorStyle>, pub mouse_cursor: Option<CursorStyle>,
/// The z-index to set for this element
pub z_index: Option<u16>,
/// Whether to draw a red debugging outline around this element /// Whether to draw a red debugging outline around this element
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub debug: bool, pub debug: bool,
@ -208,7 +205,7 @@ impl Default for TextStyle {
fn default() -> Self { fn default() -> Self {
TextStyle { TextStyle {
color: black(), color: black(),
// todo!(linux) make this configurable or choose better default // todo(linux) make this configurable or choose better default
font_family: if cfg!(target_os = "linux") { font_family: if cfg!(target_os = "linux") {
"FreeMono".into() "FreeMono".into()
} else { } else {
@ -323,6 +320,13 @@ pub struct HighlightStyle {
impl Eq for HighlightStyle {} impl Eq for HighlightStyle {}
impl Style { 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. /// Get the text style in this element style.
pub fn text_style(&self) -> Option<&TextStyleRefinement> { pub fn text_style(&self) -> Option<&TextStyleRefinement> {
if self.text.is_some() { if self.text.is_some() {
@ -402,97 +406,87 @@ impl Style {
let rem_size = cx.rem_size(); let rem_size = cx.rem_size();
cx.with_z_index(0, |cx| { cx.paint_shadows(
cx.paint_shadows( bounds,
bounds, self.corner_radii.to_pixels(bounds.size, rem_size),
self.corner_radii.to_pixels(bounds.size, rem_size), &self.box_shadow,
&self.box_shadow, );
);
});
let background_color = self.background.as_ref().and_then(Fill::color); let background_color = self.background.as_ref().and_then(Fill::color);
if background_color.map_or(false, |color| !color.is_transparent()) { if background_color.map_or(false, |color| !color.is_transparent()) {
cx.with_z_index(1, |cx| { let mut border_color = background_color.unwrap_or_default();
let mut border_color = background_color.unwrap_or_default(); border_color.a = 0.;
border_color.a = 0.; cx.paint_quad(quad(
cx.paint_quad(quad( bounds,
bounds, self.corner_radii.to_pixels(bounds.size, rem_size),
self.corner_radii.to_pixels(bounds.size, rem_size), background_color.unwrap_or_default(),
background_color.unwrap_or_default(), Edges::default(),
Edges::default(), border_color,
border_color, ));
));
});
} }
cx.with_z_index(2, |cx| { continuation(cx);
continuation(cx);
});
if self.is_border_visible() { if self.is_border_visible() {
cx.with_z_index(3, |cx| { let corner_radii = self.corner_radii.to_pixels(bounds.size, rem_size);
let corner_radii = self.corner_radii.to_pixels(bounds.size, rem_size); let border_widths = self.border_widths.to_pixels(rem_size);
let border_widths = self.border_widths.to_pixels(rem_size); let max_border_width = border_widths.max();
let max_border_width = border_widths.max(); let max_corner_radius = corner_radii.max();
let max_corner_radius = corner_radii.max();
let top_bounds = Bounds::from_corners( let top_bounds = Bounds::from_corners(
bounds.origin, bounds.origin,
bounds.upper_right() bounds.upper_right() + point(Pixels::ZERO, max_border_width.max(max_corner_radius)),
+ point(Pixels::ZERO, max_border_width.max(max_corner_radius)), );
); let bottom_bounds = Bounds::from_corners(
let bottom_bounds = Bounds::from_corners( bounds.lower_left() - point(Pixels::ZERO, max_border_width.max(max_corner_radius)),
bounds.lower_left() bounds.lower_right(),
- point(Pixels::ZERO, max_border_width.max(max_corner_radius)), );
bounds.lower_right(), let left_bounds = Bounds::from_corners(
); top_bounds.lower_left(),
let left_bounds = Bounds::from_corners( bottom_bounds.origin + point(max_border_width, Pixels::ZERO),
top_bounds.lower_left(), );
bottom_bounds.origin + point(max_border_width, Pixels::ZERO), let right_bounds = Bounds::from_corners(
); top_bounds.lower_right() - point(max_border_width, Pixels::ZERO),
let right_bounds = Bounds::from_corners( bottom_bounds.upper_right(),
top_bounds.lower_right() - point(max_border_width, Pixels::ZERO), );
bottom_bounds.upper_right(),
);
let mut background = self.border_color.unwrap_or_default(); let mut background = self.border_color.unwrap_or_default();
background.a = 0.; background.a = 0.;
let quad = quad( let quad = quad(
bounds, bounds,
corner_radii, corner_radii,
background, background,
border_widths, border_widths,
self.border_color.unwrap_or_default(), self.border_color.unwrap_or_default(),
); );
cx.with_content_mask(Some(ContentMask { bounds: top_bounds }), |cx| { cx.with_content_mask(Some(ContentMask { bounds: top_bounds }), |cx| {
cx.paint_quad(quad.clone()); cx.paint_quad(quad.clone());
});
cx.with_content_mask(
Some(ContentMask {
bounds: right_bounds,
}),
|cx| {
cx.paint_quad(quad.clone());
},
);
cx.with_content_mask(
Some(ContentMask {
bounds: bottom_bounds,
}),
|cx| {
cx.paint_quad(quad.clone());
},
);
cx.with_content_mask(
Some(ContentMask {
bounds: left_bounds,
}),
|cx| {
cx.paint_quad(quad);
},
);
}); });
cx.with_content_mask(
Some(ContentMask {
bounds: right_bounds,
}),
|cx| {
cx.paint_quad(quad.clone());
},
);
cx.with_content_mask(
Some(ContentMask {
bounds: bottom_bounds,
}),
|cx| {
cx.paint_quad(quad.clone());
},
);
cx.with_content_mask(
Some(ContentMask {
bounds: left_bounds,
}),
|cx| {
cx.paint_quad(quad);
},
);
} }
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -545,7 +539,6 @@ impl Default for Style {
box_shadow: Default::default(), box_shadow: Default::default(),
text: TextStyleRefinement::default(), text: TextStyleRefinement::default(),
mouse_cursor: None, mouse_cursor: None,
z_index: None,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
debug: false, debug: false,

View file

@ -15,12 +15,6 @@ pub trait Styled: Sized {
gpui_macros::style_helpers!(); gpui_macros::style_helpers!();
/// Set the z-index of this element.
fn z_index(mut self, z_index: u16) -> Self {
self.style().z_index = Some(z_index);
self
}
/// Sets the position of the element to `relative`. /// Sets the position of the element to `relative`.
/// [Docs](https://tailwindcss.com/docs/position) /// [Docs](https://tailwindcss.com/docs/position)
fn relative(mut self) -> Self { fn relative(mut self) -> Self {

View file

@ -51,7 +51,7 @@ impl TaffyLayoutEngine {
self.styles.get(&layout_id) self.styles.get(&layout_id)
} }
pub fn request_layout( pub fn before_layout(
&mut self, &mut self,
style: &Style, style: &Style,
rem_size: Pixels, rem_size: Pixels,
@ -447,6 +447,27 @@ pub enum AvailableSpace {
MaxContent, 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
///
/// ```
/// 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<Self> {
Size {
width: Self::MinContent,
height: Self::MinContent,
}
}
}
impl From<AvailableSpace> for TaffyAvailableSpace { impl From<AvailableSpace> for TaffyAvailableSpace {
fn from(space: AvailableSpace) -> TaffyAvailableSpace { fn from(space: AvailableSpace) -> TaffyAvailableSpace {
match space { match space {

View file

@ -291,8 +291,8 @@ impl WindowTextSystem {
} }
} }
pub(crate) fn with_view<R>(&self, view_id: EntityId, f: impl FnOnce() -> R) -> R { pub(crate) fn set_parent_view_id(&self, view_id: Option<EntityId>) {
self.line_layout_cache.with_view(view_id, f) self.line_layout_cache.set_parent_view_id(view_id);
} }
/// Shape the given line, at the given font_size, for painting to the screen. /// Shape the given line, at the given font_size, for painting to the screen.

View file

@ -118,11 +118,13 @@ fn paint_line(
let mut current_underline: Option<(Point<Pixels>, UnderlineStyle)> = None; let mut current_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
let mut current_strikethrough: Option<(Point<Pixels>, StrikethroughStyle)> = None; let mut current_strikethrough: Option<(Point<Pixels>, StrikethroughStyle)> = None;
let mut current_background: Option<(Point<Pixels>, Hsla)> = None; let mut current_background: Option<(Point<Pixels>, Hsla)> = None;
let text_system = cx.text_system().clone();
let mut glyph_origin = origin; let mut glyph_origin = origin;
let mut prev_glyph_position = Point::default(); let mut prev_glyph_position = Point::default();
for (run_ix, run) in layout.runs.iter().enumerate() { for (run_ix, run) in layout.runs.iter().enumerate() {
let max_glyph_size = text_system.bounding_box(run.font_id, layout.font_size).size; let max_glyph_size = cx
.text_system()
.bounding_box(run.font_id, layout.font_size)
.size;
for (glyph_ix, glyph) in run.glyphs.iter().enumerate() { for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
glyph_origin.x += glyph.position.x - prev_glyph_position.x; glyph_origin.x += glyph.position.x - prev_glyph_position.x;

View file

@ -4,6 +4,7 @@ use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{ use std::{
borrow::Borrow, borrow::Borrow,
cell::Cell,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
sync::Arc, sync::Arc,
}; };
@ -276,7 +277,7 @@ impl WrappedLineLayout {
} }
pub(crate) struct LineLayoutCache { pub(crate) struct LineLayoutCache {
view_stack: Mutex<Vec<EntityId>>, parent_view_id: Cell<Option<EntityId>>,
previous_frame: Mutex<FxHashMap<CacheKey, Arc<LineLayout>>>, previous_frame: Mutex<FxHashMap<CacheKey, Arc<LineLayout>>>,
current_frame: RwLock<FxHashMap<CacheKey, Arc<LineLayout>>>, current_frame: RwLock<FxHashMap<CacheKey, Arc<LineLayout>>>,
previous_frame_wrapped: Mutex<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>, previous_frame_wrapped: Mutex<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
@ -287,7 +288,7 @@ pub(crate) struct LineLayoutCache {
impl LineLayoutCache { impl LineLayoutCache {
pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self { pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
Self { Self {
view_stack: Mutex::default(), parent_view_id: Cell::default(),
previous_frame: Mutex::default(), previous_frame: Mutex::default(),
current_frame: RwLock::default(), current_frame: RwLock::default(),
previous_frame_wrapped: Mutex::default(), previous_frame_wrapped: Mutex::default(),
@ -297,8 +298,6 @@ impl LineLayoutCache {
} }
pub fn finish_frame(&self, reused_views: &FxHashSet<EntityId>) { pub fn finish_frame(&self, reused_views: &FxHashSet<EntityId>) {
debug_assert_eq!(self.view_stack.lock().len(), 0);
let mut prev_frame = self.previous_frame.lock(); let mut prev_frame = self.previous_frame.lock();
let mut curr_frame = self.current_frame.write(); let mut curr_frame = self.current_frame.write();
for (key, layout) in prev_frame.drain() { for (key, layout) in prev_frame.drain() {
@ -324,15 +323,8 @@ impl LineLayoutCache {
std::mem::swap(&mut *prev_frame_wrapped, &mut *curr_frame_wrapped); std::mem::swap(&mut *prev_frame_wrapped, &mut *curr_frame_wrapped);
} }
pub fn with_view<R>(&self, view_id: EntityId, f: impl FnOnce() -> R) -> R { pub fn set_parent_view_id(&self, view_id: Option<EntityId>) {
self.view_stack.lock().push(view_id); self.parent_view_id.replace(view_id);
let result = f();
self.view_stack.lock().pop();
result
}
fn parent_view_id(&self) -> Option<EntityId> {
self.view_stack.lock().last().copied()
} }
pub fn layout_wrapped_line( pub fn layout_wrapped_line(
@ -347,7 +339,7 @@ impl LineLayoutCache {
font_size, font_size,
runs, runs,
wrap_width, wrap_width,
parent_view_id: self.parent_view_id(), parent_view_id: self.parent_view_id.get(),
} as &dyn AsCacheKeyRef; } as &dyn AsCacheKeyRef;
let current_frame = self.current_frame_wrapped.upgradable_read(); let current_frame = self.current_frame_wrapped.upgradable_read();
@ -376,7 +368,7 @@ impl LineLayoutCache {
font_size, font_size,
runs: SmallVec::from(runs), runs: SmallVec::from(runs),
wrap_width, wrap_width,
parent_view_id: self.parent_view_id(), parent_view_id: self.parent_view_id.get(),
}; };
current_frame.insert(key, layout.clone()); current_frame.insert(key, layout.clone());
layout layout
@ -389,7 +381,7 @@ impl LineLayoutCache {
font_size, font_size,
runs, runs,
wrap_width: None, wrap_width: None,
parent_view_id: self.parent_view_id(), parent_view_id: self.parent_view_id.get(),
} as &dyn AsCacheKeyRef; } as &dyn AsCacheKeyRef;
let current_frame = self.current_frame.upgradable_read(); let current_frame = self.current_frame.upgradable_read();
@ -408,7 +400,7 @@ impl LineLayoutCache {
font_size, font_size,
runs: SmallVec::from(runs), runs: SmallVec::from(runs),
wrap_width: None, wrap_width: None,
parent_view_id: self.parent_view_id(), parent_view_id: self.parent_view_id.get(),
}; };
current_frame.insert(key, layout.clone()); current_frame.insert(key, layout.clone());
layout layout

View file

@ -1,14 +1,15 @@
use crate::{ use crate::{
seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, Bounds, seal::Sealed, AfterLayoutIndex, AnyElement, AnyModel, AnyWeakModel, AppContext, Bounds,
ContentMask, Element, ElementContext, ElementId, Entity, EntityId, Flatten, FocusHandle, ContentMask, Element, ElementContext, ElementId, Entity, EntityId, Flatten, FocusHandle,
FocusableView, IntoElement, LayoutId, Model, Pixels, Point, Render, Size, StackingOrder, Style, FocusableView, IntoElement, LayoutId, Model, PaintIndex, Pixels, Render, Style, TextStyle,
TextStyle, ViewContext, VisualContext, WeakModel, ViewContext, VisualContext, WeakModel,
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use std::{ use std::{
any::{type_name, TypeId}, any::{type_name, TypeId},
fmt, fmt,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
ops::Range,
}; };
/// A view is a piece of state that can be presented on screen by implementing the [Render] trait. /// A view is a piece of state that can be presented on screen by implementing the [Render] trait.
@ -20,17 +21,16 @@ pub struct View<V> {
impl<V> Sealed for View<V> {} impl<V> Sealed for View<V> {}
#[doc(hidden)] struct AnyViewState {
pub struct AnyViewState {
root_style: Style, root_style: Style,
next_stacking_order_id: u16, after_layout_range: Range<AfterLayoutIndex>,
cache_key: Option<ViewCacheKey>, paint_range: Range<PaintIndex>,
element: Option<AnyElement>, cache_key: ViewCacheKey,
} }
#[derive(Default)]
struct ViewCacheKey { struct ViewCacheKey {
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
stacking_order: StackingOrder,
content_mask: ContentMask<Pixels>, content_mask: ContentMask<Pixels>,
text_style: TextStyle, text_style: TextStyle,
} }
@ -90,22 +90,39 @@ impl<V: 'static> View<V> {
} }
impl<V: Render> Element for View<V> { impl<V: Render> Element for View<V> {
type State = Option<AnyElement>; type BeforeLayout = AnyElement;
type AfterLayout = ();
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
&mut self, cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
_state: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
cx.with_view_id(self.entity_id(), |cx| {
let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element()); let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
let layout_id = element.request_layout(cx); let layout_id = element.before_layout(cx);
(layout_id, Some(element)) (layout_id, element)
}) })
} }
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) { fn after_layout(
cx.paint_view(self.entity_id(), |cx| element.take().unwrap().paint(cx)); &mut self,
_: Bounds<Pixels>,
element: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) {
cx.set_view_id(self.entity_id());
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
element.after_layout(cx)
})
}
fn paint(
&mut self,
_: Bounds<Pixels>,
element: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
element.paint(cx)
})
} }
} }
@ -203,7 +220,7 @@ impl<V> Eq for WeakView<V> {}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct AnyView { pub struct AnyView {
model: AnyModel, model: AnyModel,
request_layout: fn(&AnyView, &mut ElementContext) -> (LayoutId, AnyElement), render: fn(&AnyView, &mut ElementContext) -> AnyElement,
cache: bool, cache: bool,
} }
@ -220,7 +237,7 @@ impl AnyView {
pub fn downgrade(&self) -> AnyWeakView { pub fn downgrade(&self) -> AnyWeakView {
AnyWeakView { AnyWeakView {
model: self.model.downgrade(), model: self.model.downgrade(),
layout: self.request_layout, render: self.render,
} }
} }
@ -231,7 +248,7 @@ impl AnyView {
Ok(model) => Ok(View { model }), Ok(model) => Ok(View { model }),
Err(model) => Err(Self { Err(model) => Err(Self {
model, model,
request_layout: self.request_layout, render: self.render,
cache: self.cache, cache: self.cache,
}), }),
} }
@ -246,113 +263,154 @@ impl AnyView {
pub fn entity_id(&self) -> EntityId { pub fn entity_id(&self) -> EntityId {
self.model.entity_id() self.model.entity_id()
} }
pub(crate) fn draw(
&self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) {
cx.paint_view(self.entity_id(), |cx| {
cx.with_absolute_element_offset(origin, |cx| {
let (layout_id, mut rendered_element) = (self.request_layout)(self, cx);
cx.compute_layout(layout_id, available_space);
rendered_element.paint(cx)
});
})
}
} }
impl<V: Render> From<View<V>> for AnyView { impl<V: Render> From<View<V>> for AnyView {
fn from(value: View<V>) -> Self { fn from(value: View<V>) -> Self {
AnyView { AnyView {
model: value.model.into_any(), model: value.model.into_any(),
request_layout: any_view::request_layout::<V>, render: any_view::render::<V>,
cache: false, cache: false,
} }
} }
} }
impl Element for AnyView { impl Element for AnyView {
type State = AnyViewState; type BeforeLayout = Option<AnyElement>;
type AfterLayout = Option<AnyElement>;
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
&mut self, if self.cache {
state: Option<Self::State>, cx.with_element_state::<AnyViewState, _>(
cx: &mut ElementContext, Some(ElementId::View(self.entity_id())),
) -> (LayoutId, Self::State) { |element_state, cx| {
cx.with_view_id(self.entity_id(), |cx| { let mut element_state = element_state.unwrap();
if self.cache
&& !cx.window.dirty_views.contains(&self.entity_id())
&& !cx.window.refreshing
{
if let Some(state) = state {
let layout_id = cx.request_layout(&state.root_style, None);
return (layout_id, state);
}
}
let (layout_id, element) = (self.request_layout)(self, cx); if !cx.window.dirty_views.contains(&self.entity_id()) && !cx.window.refreshing {
let root_style = cx.layout_style(layout_id).unwrap().clone(); if let Some(root_style) = element_state
let state = AnyViewState { .as_ref()
root_style, .map(|element_state| &element_state.root_style)
next_stacking_order_id: 0, {
cache_key: None, let layout_id = cx.request_layout(root_style, None);
element: Some(element), return ((layout_id, None), element_state);
}; }
(layout_id, state) }
})
let mut element = (self.render)(self, cx);
let layout_id = element.before_layout(cx);
let element_state = Some(AnyViewState {
root_style: cx.layout_style(layout_id).unwrap().clone(),
cache_key: ViewCacheKey::default(),
after_layout_range: AfterLayoutIndex::default()
..AfterLayoutIndex::default(),
paint_range: PaintIndex::default()..PaintIndex::default(),
});
((layout_id, Some(element)), element_state)
},
)
} else {
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
let mut element = (self.render)(self, cx);
let layout_id = element.before_layout(cx);
(layout_id, Some(element))
})
}
} }
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) { fn after_layout(
cx.paint_view(self.entity_id(), |cx| { &mut self,
if !self.cache { bounds: Bounds<Pixels>,
state.element.take().unwrap().paint(cx); element: &mut Self::BeforeLayout,
return; cx: &mut ElementContext,
} ) -> Option<AnyElement> {
cx.set_view_id(self.entity_id());
if self.cache {
cx.with_element_state::<AnyViewState, _>(
Some(ElementId::View(self.entity_id())),
|element_state, cx| {
let mut element_state = element_state.unwrap().unwrap();
if let Some(cache_key) = state.cache_key.as_mut() { let after_layout_start = cx.window.next_frame.after_layout_index();
if cache_key.bounds == bounds let content_mask = cx.content_mask();
&& cache_key.content_mask == cx.content_mask() let text_style = cx.text_style();
&& cache_key.stacking_order == *cx.stacking_order()
&& cache_key.text_style == cx.text_style()
{
cx.reuse_view(state.next_stacking_order_id);
return;
}
}
if let Some(mut element) = state.element.take() { let element = if let Some(mut element) = element.take() {
element.paint(cx); element.after_layout(cx);
} else { Some(element)
let mut element = (self.request_layout)(self, cx).1; } else if element_state.cache_key.bounds == bounds
element.draw(bounds.origin, bounds.size.into(), cx); && element_state.cache_key.content_mask == content_mask
} && element_state.cache_key.text_style == text_style
{
cx.reuse_after_layout(element_state.after_layout_range.clone());
None
} else {
let mut element = (self.render)(self, cx);
let layout_id = element.before_layout(cx);
cx.compute_layout(layout_id, bounds.size.into());
element_state.root_style = cx.layout_style(layout_id).unwrap().clone();
cx.with_absolute_element_offset(bounds.origin, |cx| {
element.after_layout(cx)
});
state.next_stacking_order_id = cx Some(element)
.window };
.next_frame
.next_stacking_order_ids let after_layout_end = cx.window.next_frame.after_layout_index();
.last() element_state.after_layout_range = after_layout_start..after_layout_end;
.copied() element_state.cache_key.bounds = bounds;
.unwrap(); element_state.cache_key.content_mask = content_mask;
state.cache_key = Some(ViewCacheKey { element_state.cache_key.text_style = text_style;
bounds,
stacking_order: cx.stacking_order().clone(), (element, Some(element_state))
content_mask: cx.content_mask(), },
text_style: cx.text_style(), )
}); } else {
}) cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
let mut element = element.take().unwrap();
element.after_layout(cx);
Some(element)
})
}
}
fn paint(
&mut self,
_bounds: Bounds<Pixels>,
_: &mut Self::BeforeLayout,
element: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
if self.cache {
cx.with_element_state::<AnyViewState, _>(
Some(ElementId::View(self.entity_id())),
|element_state, cx| {
let mut element_state = element_state.unwrap().unwrap();
let paint_start = cx.window.next_frame.paint_index();
if let Some(element) = element {
element.paint(cx);
} else {
cx.reuse_paint(element_state.paint_range.clone());
}
let paint_end = cx.window.next_frame.paint_index();
element_state.paint_range = paint_start..paint_end;
((), Some(element_state))
},
)
} else {
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
element.as_mut().unwrap().paint(cx);
})
}
} }
} }
impl<V: 'static + Render> IntoElement for View<V> { impl<V: 'static + Render> IntoElement for View<V> {
type Element = View<V>; type Element = View<V>;
fn element_id(&self) -> Option<ElementId> {
Some(ElementId::from_entity_id(self.model.entity_id))
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
@ -361,10 +419,6 @@ impl<V: 'static + Render> IntoElement for View<V> {
impl IntoElement for AnyView { impl IntoElement for AnyView {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ElementId> {
Some(ElementId::from_entity_id(self.model.entity_id))
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
@ -373,7 +427,7 @@ impl IntoElement for AnyView {
/// A weak, dynamically-typed view handle that does not prevent the view from being released. /// A weak, dynamically-typed view handle that does not prevent the view from being released.
pub struct AnyWeakView { pub struct AnyWeakView {
model: AnyWeakModel, model: AnyWeakModel,
layout: fn(&AnyView, &mut ElementContext) -> (LayoutId, AnyElement), render: fn(&AnyView, &mut ElementContext) -> AnyElement,
} }
impl AnyWeakView { impl AnyWeakView {
@ -382,7 +436,7 @@ impl AnyWeakView {
let model = self.model.upgrade()?; let model = self.model.upgrade()?;
Some(AnyView { Some(AnyView {
model, model,
request_layout: self.layout, render: self.render,
cache: false, cache: false,
}) })
} }
@ -392,7 +446,7 @@ impl<V: 'static + Render> From<WeakView<V>> for AnyWeakView {
fn from(view: WeakView<V>) -> Self { fn from(view: WeakView<V>) -> Self {
Self { Self {
model: view.model.into(), model: view.model.into(),
layout: any_view::request_layout::<V>, render: any_view::render::<V>,
} }
} }
} }
@ -412,15 +466,13 @@ impl std::fmt::Debug for AnyWeakView {
} }
mod any_view { mod any_view {
use crate::{AnyElement, AnyView, ElementContext, IntoElement, LayoutId, Render}; use crate::{AnyElement, AnyView, ElementContext, IntoElement, Render};
pub(crate) fn request_layout<V: 'static + Render>( pub(crate) fn render<V: 'static + Render>(
view: &AnyView, view: &AnyView,
cx: &mut ElementContext, cx: &mut ElementContext,
) -> (LayoutId, AnyElement) { ) -> AnyElement {
let view = view.clone().downcast::<V>().unwrap(); let view = view.clone().downcast::<V>().unwrap();
let mut element = view.update(cx, |view, cx| view.render(cx).into_any_element()); view.update(cx, |view, cx| view.render(cx).into_any_element())
let layout_id = element.request_layout(cx);
(layout_id, element)
} }
} }

View file

@ -1,13 +1,12 @@
use crate::{ use crate::{
px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext, px, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext, Bounds,
AvailableSpace, Bounds, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId,
DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten, Global, GlobalElementId,
Global, GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchResult, Hsla, KeyBinding, KeyDownEvent, KeyMatch, KeymatchResult, Keystroke, KeystrokeEvent, Model,
Keystroke, KeystrokeEvent, Model, ModelContext, Modifiers, MouseButton, MouseMoveEvent, ModelContext, Modifiers, MouseButton, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas,
MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Render, ScaledPixels,
PromptLevel, Render, ScaledPixels, SharedString, Size, SubscriberSet, Subscription, SharedString, Size, SubscriberSet, Subscription, TaffyLayoutEngine, Task, View, VisualContext,
TaffyLayoutEngine, Task, View, VisualContext, WeakView, WindowAppearance, WindowBounds, WeakView, WindowAppearance, WindowBounds, WindowOptions, WindowTextSystem,
WindowOptions, WindowTextSystem,
}; };
use anyhow::{anyhow, Context as _, Result}; use anyhow::{anyhow, Context as _, Result};
use collections::FxHashSet; use collections::FxHashSet;
@ -37,8 +36,6 @@ use util::{measure, ResultExt};
mod element_cx; mod element_cx;
pub use element_cx::*; pub use element_cx::*;
const ACTIVE_DRAG_Z_INDEX: u16 = 1;
/// A global stacking order, which is created by stacking successive z-index values. /// A global stacking order, which is created by stacking successive z-index values.
/// Each z-index will always be interpreted in the context of its parent z-index. /// Each z-index will always be interpreted in the context of its parent z-index.
#[derive(Debug, Deref, DerefMut, Clone, Ord, PartialOrd, PartialEq, Eq, Default)] #[derive(Debug, Deref, DerefMut, Clone, Ord, PartialOrd, PartialEq, Eq, Default)]
@ -257,6 +254,7 @@ pub struct Window {
pub(crate) element_id_stack: GlobalElementId, pub(crate) element_id_stack: GlobalElementId,
pub(crate) rendered_frame: Frame, pub(crate) rendered_frame: Frame,
pub(crate) next_frame: Frame, pub(crate) next_frame: Frame,
pub(crate) next_hitbox_id: HitboxId,
next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>>, next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>>,
pub(crate) dirty_views: FxHashSet<EntityId>, pub(crate) dirty_views: FxHashSet<EntityId>,
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>, pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
@ -264,6 +262,7 @@ pub struct Window {
focus_lost_listeners: SubscriberSet<(), AnyObserver>, focus_lost_listeners: SubscriberSet<(), AnyObserver>,
default_prevented: bool, default_prevented: bool,
mouse_position: Point<Pixels>, mouse_position: Point<Pixels>,
mouse_hit_test: HitTest,
modifiers: Modifiers, modifiers: Modifiers,
scale_factor: f32, scale_factor: f32,
bounds: WindowBounds, bounds: WindowBounds,
@ -451,12 +450,14 @@ impl Window {
rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), 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: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
next_frame_callbacks, next_frame_callbacks,
next_hitbox_id: HitboxId::default(),
dirty_views: FxHashSet::default(), dirty_views: FxHashSet::default(),
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
focus_listeners: SubscriberSet::new(), focus_listeners: SubscriberSet::new(),
focus_lost_listeners: SubscriberSet::new(), focus_lost_listeners: SubscriberSet::new(),
default_prevented: true, default_prevented: true,
mouse_position, mouse_position,
mouse_hit_test: HitTest::default(),
modifiers, modifiers,
scale_factor, scale_factor,
bounds, bounds,
@ -583,7 +584,7 @@ impl<'a> WindowContext<'a> {
} }
/// Accessor for the text system. /// Accessor for the text system.
pub fn text_system(&self) -> &Arc<WindowTextSystem> { pub fn text_system(&self) -> &WindowTextSystem {
&self.window.text_system &self.window.text_system
} }
@ -857,95 +858,6 @@ impl<'a> WindowContext<'a> {
self.window.modifiers self.window.modifiers
} }
/// Returns true if there is no opaque layer containing the given point
/// on top of the given level. Layers who are extensions of the queried layer
/// are not considered to be on top of queried layer.
pub fn was_top_layer(&self, point: &Point<Pixels>, layer: &StackingOrder) -> bool {
// Precondition: the depth map is ordered from topmost to bottomost.
for (opaque_layer, _, bounds) in self.window.rendered_frame.depth_map.iter() {
if layer >= opaque_layer {
// The queried layer is either above or is the same as the this opaque layer.
// Anything after this point is guaranteed to be below the queried layer.
return true;
}
if !bounds.contains(point) {
// This opaque layer is above the queried layer but it doesn't contain
// the given position, so we can ignore it even if it's above.
continue;
}
// At this point, we've established that this opaque layer is on top of the queried layer
// and contains the position:
// If neither the opaque layer or the queried layer is an extension of the other then
// we know they are on different stacking orders, and return false.
let is_on_same_layer = opaque_layer
.iter()
.zip(layer.iter())
.all(|(a, b)| a.z_index == b.z_index);
if !is_on_same_layer {
return false;
}
}
true
}
pub(crate) fn was_top_layer_under_active_drag(
&self,
point: &Point<Pixels>,
layer: &StackingOrder,
) -> bool {
// Precondition: the depth map is ordered from topmost to bottomost.
for (opaque_layer, _, bounds) in self.window.rendered_frame.depth_map.iter() {
if layer >= opaque_layer {
// The queried layer is either above or is the same as the this opaque layer.
// Anything after this point is guaranteed to be below the queried layer.
return true;
}
if !bounds.contains(point) {
// This opaque layer is above the queried layer but it doesn't contain
// the given position, so we can ignore it even if it's above.
continue;
}
// All normal content is rendered with a base z-index of 0, we know that if the root of this opaque layer
// equals `ACTIVE_DRAG_Z_INDEX` then it must be the drag layer and we can ignore it as we are
// looking to see if the queried layer was the topmost underneath the drag layer.
if opaque_layer
.first()
.map(|c| c.z_index == ACTIVE_DRAG_Z_INDEX)
.unwrap_or(false)
{
continue;
}
// At this point, we've established that this opaque layer is on top of the queried layer
// and contains the position:
// If neither the opaque layer or the queried layer is an extension of the other then
// we know they are on different stacking orders, and return false.
let is_on_same_layer = opaque_layer
.iter()
.zip(layer.iter())
.all(|(a, b)| a.z_index == b.z_index);
if !is_on_same_layer {
return false;
}
}
true
}
/// Called during painting to get the current stacking order.
pub fn stacking_order(&self) -> &StackingOrder {
&self.window.next_frame.z_index_stack
}
/// Produces a new frame and assigns it to `rendered_frame`. To actually show /// Produces a new frame and assigns it to `rendered_frame`. To actually show
/// the contents of the new [Scene], use [present]. /// the contents of the new [Scene], use [present].
#[profiling::function] #[profiling::function]
@ -959,54 +871,7 @@ impl<'a> WindowContext<'a> {
requested_handler.handler = input_handler; requested_handler.handler = input_handler;
} }
let root_view = self.window.root_view.take().unwrap(); self.with_element_context(|cx| cx.draw_roots());
self.with_element_context(|cx| {
cx.with_z_index(0, |cx| {
cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| {
// We need to use cx.cx here so we can utilize borrow splitting
for (action_type, action_listeners) in &cx.cx.app.global_action_listeners {
for action_listener in action_listeners.iter().cloned() {
cx.cx.window.next_frame.dispatch_tree.on_action(
*action_type,
Rc::new(
move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| {
action_listener(action, phase, cx)
},
),
)
}
}
let available_space = cx.window.viewport_size.map(Into::into);
root_view.draw(Point::default(), available_space, cx);
})
})
});
if let Some(active_drag) = self.app.active_drag.take() {
self.with_element_context(|cx| {
cx.with_z_index(ACTIVE_DRAG_Z_INDEX, |cx| {
let offset = cx.mouse_position() - active_drag.cursor_offset;
let available_space =
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
active_drag.view.draw(offset, available_space, cx);
})
});
self.active_drag = Some(active_drag);
} else if let Some(tooltip_request) = self.window.next_frame.tooltip_request.take() {
self.with_element_context(|cx| {
cx.with_z_index(1, |cx| {
let available_space =
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
tooltip_request.tooltip.view.draw(
tooltip_request.tooltip.cursor_offset,
available_space,
cx,
);
})
});
self.window.next_frame.tooltip_request = Some(tooltip_request);
}
self.window.dirty_views.clear(); self.window.dirty_views.clear();
self.window self.window
@ -1018,13 +883,10 @@ impl<'a> WindowContext<'a> {
); );
self.window.next_frame.focus = self.window.focus; self.window.next_frame.focus = self.window.focus;
self.window.next_frame.window_active = self.window.active.get(); self.window.next_frame.window_active = self.window.active.get();
self.window.root_view = Some(root_view);
// Set the cursor only if we're the active window. // Set the cursor only if we're the active window.
let cursor_style_request = self.window.next_frame.requested_cursor_style.take();
if self.is_window_active() { if self.is_window_active() {
let cursor_style = let cursor_style = self.compute_cursor_style().unwrap_or(CursorStyle::Arrow);
cursor_style_request.map_or(CursorStyle::Arrow, |request| request.style);
self.platform.set_cursor_style(cursor_style); self.platform.set_cursor_style(cursor_style);
} }
@ -1097,6 +959,18 @@ impl<'a> WindowContext<'a> {
profiling::finish_frame!(); profiling::finish_frame!();
} }
fn compute_cursor_style(&mut self) -> Option<CursorStyle> {
// TODO: maybe we should have a HashMap keyed by HitboxId.
let request = self
.window
.next_frame
.cursor_styles
.iter()
.rev()
.find(|request| request.hitbox_id.is_hovered(self))?;
Some(request.style)
}
/// Dispatch a given keystroke as though the user had typed it. /// Dispatch a given keystroke as though the user had typed it.
/// You can create a keystroke with Keystroke::parse(""). /// You can create a keystroke with Keystroke::parse("").
pub fn dispatch_keystroke(&mut self, keystroke: Keystroke) -> bool { pub fn dispatch_keystroke(&mut self, keystroke: Keystroke) -> bool {
@ -1231,43 +1105,32 @@ impl<'a> WindowContext<'a> {
} }
fn dispatch_mouse_event(&mut self, event: &dyn Any) { fn dispatch_mouse_event(&mut self, event: &dyn Any) {
if let Some(mut handlers) = self self.window.mouse_hit_test = self.window.rendered_frame.hit_test(self.mouse_position());
.window
.rendered_frame
.mouse_listeners
.remove(&event.type_id())
{
// Because handlers may add other handlers, we sort every time.
handlers.sort_by(|(a, _, _), (b, _, _)| a.cmp(b));
let mut mouse_listeners = mem::take(&mut self.window.rendered_frame.mouse_listeners);
self.with_element_context(|cx| {
// Capture phase, events bubble from back to front. Handlers for this phase are used for // 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. // special purposes, such as detecting events outside of a given Bounds.
for (_, _, handler) in &mut handlers { for listener in &mut mouse_listeners {
self.with_element_context(|cx| { let listener = listener.as_mut().unwrap();
handler(event, DispatchPhase::Capture, cx); listener(event, DispatchPhase::Capture, cx);
}); if !cx.app.propagate_event {
if !self.app.propagate_event {
break; break;
} }
} }
// Bubble phase, where most normal handlers do their work. // Bubble phase, where most normal handlers do their work.
if self.app.propagate_event { if cx.app.propagate_event {
for (_, _, handler) in handlers.iter_mut().rev() { for listener in mouse_listeners.iter_mut().rev() {
self.with_element_context(|cx| { let listener = listener.as_mut().unwrap();
handler(event, DispatchPhase::Bubble, cx); listener(event, DispatchPhase::Bubble, cx);
}); if !cx.app.propagate_event {
if !self.app.propagate_event {
break; break;
} }
} }
} }
});
self.window self.window.rendered_frame.mouse_listeners = mouse_listeners;
.rendered_frame
.mouse_listeners
.insert(event.type_id(), handlers);
}
if self.app.propagate_event && self.has_active_drag() { if self.app.propagate_event && self.has_active_drag() {
if event.is::<MouseMoveEvent>() { if event.is::<MouseMoveEvent>() {
@ -1337,6 +1200,7 @@ impl<'a> WindowContext<'a> {
}) })
.log_err(); .log_err();
})); }));
self.window.pending_input = Some(currently_pending); self.window.pending_input = Some(currently_pending);
self.propagate_event = false; self.propagate_event = false;
@ -1645,12 +1509,11 @@ impl<'a> WindowContext<'a> {
} }
pub(crate) fn parent_view_id(&self) -> EntityId { pub(crate) fn parent_view_id(&self) -> EntityId {
*self self.window
.window
.next_frame .next_frame
.view_stack .dispatch_tree
.last() .parent_view_id()
.expect("a view should always be on the stack while drawing") .unwrap()
} }
/// Register an action listener on the window for the next frame. The type of action /// Register an action listener on the window for the next frame. The type of action
@ -2681,12 +2544,6 @@ impl Display for ElementId {
} }
} }
impl ElementId {
pub(crate) fn from_entity_id(entity_id: EntityId) -> Self {
ElementId::View(entity_id)
}
}
impl TryInto<SharedString> for ElementId { impl TryInto<SharedString> for ElementId {
type Error = anyhow::Error; type Error = anyhow::Error;

File diff suppressed because it is too large Load diff

View file

@ -13,10 +13,6 @@ pub fn derive_into_element(input: TokenStream) -> TokenStream {
{ {
type Element = gpui::Component<Self>; type Element = gpui::Component<Self>;
fn element_id(&self) -> Option<gpui::ElementId> {
None
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
gpui::Component::new(self) gpui::Component::new(self)
} }

View file

@ -18,7 +18,7 @@ pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> {
// If the symlink is not there or is outdated, first try replacing it // If the symlink is not there or is outdated, first try replacing it
// without escalating. // without escalating.
smol::fs::remove_file(link_path).await.log_err(); smol::fs::remove_file(link_path).await.log_err();
// todo!("windows") // todo("windows")
#[cfg(not(windows))] #[cfg(not(windows))]
{ {
if smol::fs::unix::symlink(&cli_path, link_path) if smol::fs::unix::symlink(&cli_path, link_path)

View file

@ -807,7 +807,7 @@ impl Render for LspLogToolbarItemView {
.justify_between() .justify_between()
.child(Label::new(RPC_MESSAGES)) .child(Label::new(RPC_MESSAGES))
.child( .child(
div().z_index(120).child( div().child(
Checkbox::new( Checkbox::new(
ix, ix,
if row.rpc_trace_enabled { if row.rpc_trace_enabled {

View file

@ -1,9 +1,9 @@
use editor::{scroll::Autoscroll, Anchor, Editor, ExcerptId}; use editor::{scroll::Autoscroll, Anchor, Editor, ExcerptId};
use gpui::{ use gpui::{
actions, canvas, div, rems, uniform_list, AnyElement, AppContext, AvailableSpace, Div, actions, canvas, div, rems, uniform_list, AnyElement, AppContext, Div, EventEmitter,
EventEmitter, FocusHandle, FocusableView, Hsla, InteractiveElement, IntoElement, Model, FocusHandle, FocusableView, Hsla, InteractiveElement, IntoElement, Model, MouseButton,
MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, Render, Styled, MouseDownEvent, MouseMoveEvent, ParentElement, Render, Styled, UniformListScrollHandle, View,
UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext, ViewContext, VisualContext, WeakView, WindowContext,
}; };
use language::{Buffer, OwnedSyntaxLayer}; use language::{Buffer, OwnedSyntaxLayer};
use std::{mem, ops::Range}; use std::{mem, ops::Range};
@ -277,7 +277,7 @@ impl Render for SyntaxTreeView {
.and_then(|buffer| buffer.active_layer.as_ref()) .and_then(|buffer| buffer.active_layer.as_ref())
{ {
let layer = layer.clone(); let layer = layer.clone();
let list = uniform_list( let mut list = uniform_list(
cx.view().clone(), cx.view().clone(),
"SyntaxTreeView", "SyntaxTreeView",
layer.node().descendant_count(), layer.node().descendant_count(),
@ -356,16 +356,16 @@ impl Render for SyntaxTreeView {
) )
.size_full() .size_full()
.track_scroll(self.list_scroll_handle.clone()) .track_scroll(self.list_scroll_handle.clone())
.text_bg(cx.theme().colors().background); .text_bg(cx.theme().colors().background).into_any_element();
rendered = rendered.child( rendered = rendered.child(
canvas(move |bounds, cx| { canvas(
list.into_any_element().draw( move |bounds, cx| {
bounds.origin, list.layout(bounds.origin, bounds.size.into(), cx);
bounds.size.map(AvailableSpace::Definite), list
cx, },
) |_, mut list, cx| list.paint(cx),
}) )
.size_full(), .size_full(),
); );
} }

View file

@ -81,7 +81,7 @@ impl super::LspAdapter for OmniSharpAdapter {
archive.unpack(container_dir).await?; archive.unpack(container_dir).await?;
} }
// todo!("windows") // todo("windows")
#[cfg(not(windows))] #[cfg(not(windows))]
{ {
fs::set_permissions( fs::set_permissions(

View file

@ -358,7 +358,7 @@ impl LspAdapter for NextLspAdapter {
} }
futures::io::copy(response.body_mut(), &mut file).await?; futures::io::copy(response.body_mut(), &mut file).await?;
// todo!("windows") // todo("windows")
#[cfg(not(windows))] #[cfg(not(windows))]
{ {
fs::set_permissions( fs::set_permissions(

View file

@ -83,7 +83,7 @@ impl super::LspAdapter for LuaLspAdapter {
archive.unpack(container_dir).await?; archive.unpack(container_dir).await?;
} }
// todo!("windows") // todo("windows")
#[cfg(not(windows))] #[cfg(not(windows))]
{ {
fs::set_permissions( fs::set_permissions(

View file

@ -26,7 +26,7 @@ pub struct PurescriptLspAdapter {
} }
impl PurescriptLspAdapter { impl PurescriptLspAdapter {
// todo!(linux): remove // todo(linux): remove
#[cfg_attr(target_os = "linux", allow(dead_code))] #[cfg_attr(target_os = "linux", allow(dead_code))]
pub fn new(node: Arc<dyn NodeRuntime>) -> Self { pub fn new(node: Arc<dyn NodeRuntime>) -> Self {
Self { node } Self { node }

View file

@ -74,7 +74,7 @@ impl LspAdapter for RustLspAdapter {
let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut())); let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
let mut file = File::create(&destination_path).await?; let mut file = File::create(&destination_path).await?;
futures::io::copy(decompressed_bytes, &mut file).await?; futures::io::copy(decompressed_bytes, &mut file).await?;
// todo!("windows") // todo("windows")
#[cfg(not(windows))] #[cfg(not(windows))]
{ {
fs::set_permissions( fs::set_permissions(

View file

@ -72,7 +72,7 @@ impl LspAdapter for TaploLspAdapter {
futures::io::copy(decompressed_bytes, &mut file).await?; futures::io::copy(decompressed_bytes, &mut file).await?;
// todo!("windows") // todo("windows")
#[cfg(not(windows))] #[cfg(not(windows))]
{ {
fs::set_permissions( fs::set_permissions(

View file

@ -86,7 +86,7 @@ impl LspAdapter for ZlsAdapter {
archive.unpack(container_dir).await?; archive.unpack(container_dir).await?;
} }
// todo!("windows") // todo("windows")
#[cfg(not(windows))] #[cfg(not(windows))]
{ {
fs::set_permissions( fs::set_permissions(

View file

@ -73,7 +73,7 @@ impl TestServer {
} }
pub async fn create_room(&self, room: String) -> Result<()> { pub async fn create_room(&self, room: String) -> Result<()> {
//todo!(linux): Remove this once the cross-platform LiveKit implementation is merged // todo(linux): Remove this once the cross-platform LiveKit implementation is merged
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
self.executor.simulate_random_delay().await; self.executor.simulate_random_delay().await;
let mut server_rooms = self.rooms.lock(); let mut server_rooms = self.rooms.lock();
@ -87,7 +87,7 @@ impl TestServer {
async fn delete_room(&self, room: String) -> Result<()> { async fn delete_room(&self, room: String) -> Result<()> {
// TODO: clear state associated with all `Room`s. // TODO: clear state associated with all `Room`s.
//todo!(linux): Remove this once the cross-platform LiveKit implementation is merged // todo(linux): Remove this once the cross-platform LiveKit implementation is merged
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
self.executor.simulate_random_delay().await; self.executor.simulate_random_delay().await;
let mut server_rooms = self.rooms.lock(); let mut server_rooms = self.rooms.lock();
@ -98,7 +98,7 @@ impl TestServer {
} }
async fn join_room(&self, token: String, client_room: Arc<Room>) -> Result<()> { async fn join_room(&self, token: String, client_room: Arc<Room>) -> Result<()> {
//todo!(linux): Remove this once the cross-platform LiveKit implementation is merged // todo(linux): Remove this once the cross-platform LiveKit implementation is merged
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
self.executor.simulate_random_delay().await; self.executor.simulate_random_delay().await;
@ -147,7 +147,7 @@ impl TestServer {
} }
async fn leave_room(&self, token: String) -> Result<()> { async fn leave_room(&self, token: String) -> Result<()> {
//todo!(linux): Remove this once the cross-platform LiveKit implementation is merged // todo(linux): Remove this once the cross-platform LiveKit implementation is merged
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
self.executor.simulate_random_delay().await; self.executor.simulate_random_delay().await;
let claims = live_kit_server::token::validate(&token, &self.secret_key)?; let claims = live_kit_server::token::validate(&token, &self.secret_key)?;
@ -169,7 +169,7 @@ impl TestServer {
async fn remove_participant(&self, room_name: String, identity: String) -> Result<()> { async fn remove_participant(&self, room_name: String, identity: String) -> Result<()> {
// TODO: clear state associated with the `Room`. // TODO: clear state associated with the `Room`.
//todo!(linux): Remove this once the cross-platform LiveKit implementation is merged // todo(linux): Remove this once the cross-platform LiveKit implementation is merged
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
self.executor.simulate_random_delay().await; self.executor.simulate_random_delay().await;
@ -193,7 +193,7 @@ impl TestServer {
identity: String, identity: String,
permission: proto::ParticipantPermission, permission: proto::ParticipantPermission,
) -> Result<()> { ) -> Result<()> {
//todo!(linux): Remove this once the cross-platform LiveKit implementation is merged // todo(linux): Remove this once the cross-platform LiveKit implementation is merged
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
self.executor.simulate_random_delay().await; self.executor.simulate_random_delay().await;
let mut server_rooms = self.rooms.lock(); let mut server_rooms = self.rooms.lock();
@ -205,7 +205,7 @@ impl TestServer {
} }
pub async fn disconnect_client(&self, client_identity: String) { pub async fn disconnect_client(&self, client_identity: String) {
//todo!(linux): Remove this once the cross-platform LiveKit implementation is merged // todo(linux): Remove this once the cross-platform LiveKit implementation is merged
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
self.executor.simulate_random_delay().await; self.executor.simulate_random_delay().await;
let mut server_rooms = self.rooms.lock(); let mut server_rooms = self.rooms.lock();
@ -221,7 +221,7 @@ impl TestServer {
token: String, token: String,
local_track: LocalVideoTrack, local_track: LocalVideoTrack,
) -> Result<Sid> { ) -> Result<Sid> {
//todo!(linux): Remove this once the cross-platform LiveKit implementation is merged // todo(linux): Remove this once the cross-platform LiveKit implementation is merged
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
self.executor.simulate_random_delay().await; self.executor.simulate_random_delay().await;
let claims = live_kit_server::token::validate(&token, &self.secret_key)?; let claims = live_kit_server::token::validate(&token, &self.secret_key)?;
@ -276,7 +276,7 @@ impl TestServer {
token: String, token: String,
_local_track: &LocalAudioTrack, _local_track: &LocalAudioTrack,
) -> Result<Sid> { ) -> Result<Sid> {
//todo!(linux): Remove this once the cross-platform LiveKit implementation is merged // todo(linux): Remove this once the cross-platform LiveKit implementation is merged
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
self.executor.simulate_random_delay().await; self.executor.simulate_random_delay().await;
@ -559,7 +559,7 @@ impl Room {
pub fn display_sources(self: &Arc<Self>) -> impl Future<Output = Result<Vec<MacOSDisplay>>> { pub fn display_sources(self: &Arc<Self>) -> impl Future<Output = Result<Vec<MacOSDisplay>>> {
let this = self.clone(); let this = self.clone();
async move { async move {
//todo!(linux): Remove this once the cross-platform LiveKit implementation is merged // todo(linux): Remove this once the cross-platform LiveKit implementation is merged
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
{ {
let server = this.test_server(); let server = this.test_server();

View file

@ -7,7 +7,6 @@ mod picker;
mod scroll; mod scroll;
mod text; mod text;
mod viewport_units; mod viewport_units;
mod z_index;
pub use auto_height_editor::*; pub use auto_height_editor::*;
pub use cursor::*; pub use cursor::*;
@ -18,4 +17,3 @@ pub use picker::*;
pub use scroll::*; pub use scroll::*;
pub use text::*; pub use text::*;
pub use viewport_units::*; pub use viewport_units::*;
pub use z_index::*;

View file

@ -1,172 +0,0 @@
use gpui::{px, rgb, Div, IntoElement, Render, RenderOnce};
use story::Story;
use ui::prelude::*;
/// A reimplementation of the MDN `z-index` example, found here:
/// [https://developer.mozilla.org/en-US/docs/Web/CSS/z-index](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index).
pub struct ZIndexStory;
impl Render for ZIndexStory {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
Story::container().child(Story::title("z-index")).child(
div()
.flex()
.child(
div()
.w(px(250.))
.child(Story::label("z-index: auto"))
.child(ZIndexExample::new(0)),
)
.child(
div()
.w(px(250.))
.child(Story::label("z-index: 1"))
.child(ZIndexExample::new(1)),
)
.child(
div()
.w(px(250.))
.child(Story::label("z-index: 3"))
.child(ZIndexExample::new(3)),
)
.child(
div()
.w(px(250.))
.child(Story::label("z-index: 5"))
.child(ZIndexExample::new(5)),
)
.child(
div()
.w(px(250.))
.child(Story::label("z-index: 7"))
.child(ZIndexExample::new(7)),
),
)
}
}
trait Styles: Styled + Sized {
// Trailing `_` is so we don't collide with `block` style `StyleHelpers`.
fn block_(self) -> Self {
self.absolute()
.w(px(150.))
.h(px(50.))
.text_color(rgb(0x000000))
}
fn blue(self) -> Self {
self.bg(rgb(0xe5e8fc))
.border_5()
.border_color(rgb(0x112382))
.line_height(px(55.))
// HACK: Simulate `text-align: center`.
.pl(px(24.))
}
fn red(self) -> Self {
self.bg(rgb(0xfce5e7))
.border_5()
.border_color(rgb(0xe3a1a7))
// HACK: Simulate `text-align: center`.
.pl(px(8.))
}
}
impl Styles for Div {}
#[derive(IntoElement)]
struct ZIndexExample {
z_index: u16,
}
impl RenderOnce for ZIndexExample {
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
div()
.relative()
.size_full()
// Example element.
.child(
div()
.absolute()
.top(px(15.))
.left(px(15.))
.w(px(180.))
.h(px(230.))
.bg(rgb(0xfcfbe5))
.text_color(rgb(0x000000))
.border_5()
.border_color(rgb(0xe3e0a1))
.line_height(px(215.))
// HACK: Simulate `text-align: center`.
.pl(px(24.))
.z_index(self.z_index)
.child(SharedString::from(format!(
"z-index: {}",
if self.z_index == 0 {
"auto".to_string()
} else {
self.z_index.to_string()
}
))),
)
// Blue blocks.
.child(
div()
.blue()
.block_()
.top(px(0.))
.left(px(0.))
.z_index(6)
.child("z-index: 6"),
)
.child(
div()
.blue()
.block_()
.top(px(30.))
.left(px(30.))
.z_index(4)
.child("z-index: 4"),
)
.child(
div()
.blue()
.block_()
.top(px(60.))
.left(px(60.))
.z_index(2)
.child("z-index: 2"),
)
// Red blocks.
.child(
div()
.red()
.block_()
.top(px(150.))
.left(px(0.))
.child("z-index: auto"),
)
.child(
div()
.red()
.block_()
.top(px(180.))
.left(px(30.))
.child("z-index: auto"),
)
.child(
div()
.red()
.block_()
.top(px(210.))
.left(px(60.))
.child("z-index: auto"),
)
}
}
impl ZIndexExample {
pub fn new(z_index: u16) -> Self {
Self { z_index }
}
}

View file

@ -35,7 +35,6 @@ pub enum ComponentStory {
ToggleButton, ToggleButton,
Text, Text,
ViewportUnits, ViewportUnits,
ZIndex,
Picker, Picker,
} }
@ -67,7 +66,6 @@ impl ComponentStory {
Self::TabBar => cx.new_view(|_| ui::TabBarStory).into(), Self::TabBar => cx.new_view(|_| ui::TabBarStory).into(),
Self::ToggleButton => cx.new_view(|_| ui::ToggleButtonStory).into(), Self::ToggleButton => cx.new_view(|_| ui::ToggleButtonStory).into(),
Self::ViewportUnits => cx.new_view(|_| crate::stories::ViewportUnitsStory).into(), Self::ViewportUnits => cx.new_view(|_| crate::stories::ViewportUnitsStory).into(),
Self::ZIndex => cx.new_view(|_| ZIndexStory).into(),
Self::Picker => PickerStory::new(cx).into(), Self::Picker => PickerStory::new(cx).into(),
} }
} }

View file

@ -399,7 +399,7 @@ impl TerminalBuilder {
#[cfg(unix)] #[cfg(unix)]
let (fd, shell_pid) = (pty.file().as_raw_fd(), pty.child().id()); let (fd, shell_pid) = (pty.file().as_raw_fd(), pty.child().id());
// todo!("windows") // todo("windows")
#[cfg(windows)] #[cfg(windows)]
let (fd, shell_pid) = (-1, 0); let (fd, shell_pid) = (-1, 0);
@ -667,7 +667,7 @@ impl Terminal {
fn update_process_info(&mut self) -> bool { fn update_process_info(&mut self) -> bool {
#[cfg(unix)] #[cfg(unix)]
let mut pid = unsafe { libc::tcgetpgrp(self.shell_fd as i32) }; let mut pid = unsafe { libc::tcgetpgrp(self.shell_fd as i32) };
// todo!("windows") // todo("windows")
#[cfg(windows)] #[cfg(windows)]
let mut pid = -1; let mut pid = -1;
if pid < 0 { if pid < 0 {

File diff suppressed because it is too large Load diff

View file

@ -100,7 +100,7 @@ pub(crate) fn one_dark() -> Theme {
editor_document_highlight_write_background: gpui::red(), editor_document_highlight_write_background: gpui::red(),
terminal_background: bg, terminal_background: bg,
// todo!("Use one colors for terminal") // todo("Use one colors for terminal")
terminal_foreground: crate::white().dark().step_12(), terminal_foreground: crate::white().dark().step_12(),
terminal_bright_foreground: crate::white().dark().step_11(), terminal_bright_foreground: crate::white().dark().step_11(),
terminal_dim_foreground: crate::white().dark().step_10(), terminal_dim_foreground: crate::white().dark().step_10(),

View file

@ -130,7 +130,7 @@ pub struct ThemeColors {
/// The border color of the scrollbar track. /// The border color of the scrollbar track.
pub scrollbar_track_border: Hsla, pub scrollbar_track_border: Hsla,
// /// The opacity of the scrollbar status marks, like diagnostic states and git status. // /// The opacity of the scrollbar status marks, like diagnostic states and git status.
// todo!() // todo()
// pub scrollbar_status_opacity: Hsla, // pub scrollbar_status_opacity: Hsla,
// === // ===

View file

@ -25,8 +25,8 @@ pub fn format_localized_timestamp(
} }
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
{ {
//todo!(linux) respect user's date/time preferences // todo(linux) respect user's date/time preferences
//todo!(windows) respect user's date/time preferences // todo(windows) respect user's date/time preferences
format_timestamp_fallback(reference, timestamp, timezone) format_timestamp_fallback(reference, timestamp, timezone)
} }
} }

View file

@ -122,9 +122,6 @@ impl RenderOnce for Avatar {
.size(image_size) .size(image_size)
.bg(cx.theme().colors().ghost_element_background), .bg(cx.theme().colors().ghost_element_background),
) )
.children( .children(self.indicator.map(|indicator| div().child(indicator)))
self.indicator
.map(|indicator| div().z_index(1).child(indicator)),
)
} }
} }

View file

@ -2,9 +2,9 @@ use std::{cell::RefCell, rc::Rc};
use gpui::{ use gpui::{
overlay, point, prelude::FluentBuilder, px, rems, AnchorCorner, AnyElement, Bounds, overlay, point, prelude::FluentBuilder, px, rems, AnchorCorner, AnyElement, Bounds,
DismissEvent, DispatchPhase, Element, ElementContext, ElementId, InteractiveBounds, DismissEvent, DispatchPhase, Element, ElementContext, ElementId, HitboxId, IntoElement,
IntoElement, LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View, LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext,
VisualContext, WindowContext, WindowContext,
}; };
use crate::{Clickable, Selectable}; use crate::{Clickable, Selectable};
@ -107,6 +107,21 @@ impl<M: ManagedView> PopoverMenu<M> {
} }
}) })
} }
fn with_element_state<R>(
&mut self,
cx: &mut ElementContext,
f: impl FnOnce(&mut Self, &mut PopoverMenuElementState<M>, &mut ElementContext) -> R,
) -> R {
cx.with_element_state::<PopoverMenuElementState<M>, _>(
Some(self.id.clone()),
|element_state, cx| {
let mut element_state = element_state.unwrap().unwrap_or_default();
let result = f(self, &mut element_state, cx);
(result, Some(element_state))
},
)
}
} }
/// Creates a [`PopoverMenu`] /// Creates a [`PopoverMenu`]
@ -121,114 +136,136 @@ pub fn popover_menu<M: ManagedView>(id: impl Into<ElementId>) -> PopoverMenu<M>
} }
} }
pub struct PopoverMenuState<M> { pub struct PopoverMenuElementState<M> {
menu: Rc<RefCell<Option<View<M>>>>,
child_bounds: Option<Bounds<Pixels>>,
}
impl<M> Clone for PopoverMenuElementState<M> {
fn clone(&self) -> Self {
Self {
menu: Rc::clone(&self.menu),
child_bounds: self.child_bounds.clone(),
}
}
}
impl<M> Default for PopoverMenuElementState<M> {
fn default() -> Self {
Self {
menu: Rc::default(),
child_bounds: None,
}
}
}
pub struct PopoverMenuFrameState {
child_layout_id: Option<LayoutId>, child_layout_id: Option<LayoutId>,
child_element: Option<AnyElement>, child_element: Option<AnyElement>,
child_bounds: Option<Bounds<Pixels>>,
menu_element: Option<AnyElement>, menu_element: Option<AnyElement>,
menu: Rc<RefCell<Option<View<M>>>>,
} }
impl<M: ManagedView> Element for PopoverMenu<M> { impl<M: ManagedView> Element for PopoverMenu<M> {
type State = PopoverMenuState<M>; type BeforeLayout = PopoverMenuFrameState;
type AfterLayout = Option<HitboxId>;
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::BeforeLayout) {
self.with_element_state(cx, |this, element_state, cx| {
let mut menu_layout_id = None;
let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| {
let mut overlay = overlay().snap_to_window().anchor(this.anchor);
if let Some(child_bounds) = element_state.child_bounds {
overlay = overlay.position(
this.resolved_attach().corner(child_bounds) + this.resolved_offset(cx),
);
}
let mut element = overlay.child(menu.clone()).into_any();
menu_layout_id = Some(element.before_layout(cx));
element
});
let mut child_element = this.child_builder.take().map(|child_builder| {
(child_builder)(element_state.menu.clone(), this.menu_builder.clone())
});
let child_layout_id = child_element
.as_mut()
.map(|child_element| child_element.before_layout(cx));
let layout_id = cx.request_layout(
&gpui::Style::default(),
menu_layout_id.into_iter().chain(child_layout_id),
);
(
layout_id,
PopoverMenuFrameState {
child_element,
child_layout_id,
menu_element,
},
)
})
}
fn after_layout(
&mut self, &mut self,
element_state: Option<Self::State>, _bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) -> (gpui::LayoutId, Self::State) { ) -> Option<HitboxId> {
let mut menu_layout_id = None; self.with_element_state(cx, |_this, element_state, cx| {
if let Some(child) = before_layout.child_element.as_mut() {
let (menu, child_bounds) = if let Some(element_state) = element_state { child.after_layout(cx);
(element_state.menu, element_state.child_bounds)
} else {
(Rc::default(), None)
};
let menu_element = menu.borrow_mut().as_mut().map(|menu| {
let mut overlay = overlay().snap_to_window().anchor(self.anchor);
if let Some(child_bounds) = child_bounds {
overlay = overlay.position(
self.resolved_attach().corner(child_bounds) + self.resolved_offset(cx),
);
} }
let mut element = overlay.child(menu.clone()).into_any(); if let Some(menu) = before_layout.menu_element.as_mut() {
menu_layout_id = Some(element.request_layout(cx)); menu.after_layout(cx);
element }
});
let mut child_element = self before_layout.child_layout_id.map(|layout_id| {
.child_builder let bounds = cx.layout_bounds(layout_id);
.take() element_state.child_bounds = Some(bounds);
.map(|child_builder| (child_builder)(menu.clone(), self.menu_builder.clone())); cx.insert_hitbox(bounds, false).id
})
let child_layout_id = child_element })
.as_mut()
.map(|child_element| child_element.request_layout(cx));
let layout_id = cx.request_layout(
&gpui::Style::default(),
menu_layout_id.into_iter().chain(child_layout_id),
);
(
layout_id,
PopoverMenuState {
menu,
child_element,
child_layout_id,
menu_element,
child_bounds,
},
)
} }
fn paint( fn paint(
&mut self, &mut self,
_: Bounds<gpui::Pixels>, _: Bounds<gpui::Pixels>,
element_state: &mut Self::State, before_layout: &mut Self::BeforeLayout,
child_hitbox: &mut Option<HitboxId>,
cx: &mut ElementContext, cx: &mut ElementContext,
) { ) {
if let Some(mut child) = element_state.child_element.take() { self.with_element_state(cx, |_this, _element_state, cx| {
child.paint(cx); if let Some(mut child) = before_layout.child_element.take() {
} child.paint(cx);
if let Some(child_layout_id) = element_state.child_layout_id.take() {
element_state.child_bounds = Some(cx.layout_bounds(child_layout_id));
}
if let Some(mut menu) = element_state.menu_element.take() {
menu.paint(cx);
if let Some(child_bounds) = element_state.child_bounds {
let interactive_bounds = InteractiveBounds {
bounds: child_bounds,
stacking_order: cx.stacking_order().clone(),
};
// Mouse-downing outside the menu dismisses it, so we don't
// want a click on the toggle to re-open it.
cx.on_mouse_event(move |e: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble
&& interactive_bounds.visibly_contains(&e.position, cx)
{
cx.stop_propagation()
}
})
} }
}
if let Some(mut menu) = before_layout.menu_element.take() {
menu.paint(cx);
if let Some(child_hitbox) = *child_hitbox {
// Mouse-downing outside the menu dismisses it, so we don't
// want a click on the toggle to re-open it.
cx.on_mouse_event(move |_: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble && child_hitbox.is_hovered(cx) {
cx.stop_propagation()
}
})
}
}
})
} }
} }
impl<M: ManagedView> IntoElement for PopoverMenu<M> { impl<M: ManagedView> IntoElement for PopoverMenu<M> {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<gpui::ElementId> {
Some(self.id.clone())
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }

View file

@ -2,7 +2,7 @@ use std::{cell::RefCell, rc::Rc};
use gpui::{ use gpui::{
overlay, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase, Element, overlay, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase, Element,
ElementContext, ElementId, InteractiveBounds, IntoElement, LayoutId, ManagedView, MouseButton, ElementContext, ElementId, Hitbox, IntoElement, LayoutId, ManagedView, MouseButton,
MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext, WindowContext, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext, WindowContext,
}; };
@ -37,6 +37,21 @@ impl<M: ManagedView> RightClickMenu<M> {
self.attach = Some(attach); self.attach = Some(attach);
self self
} }
fn with_element_state<R>(
&mut self,
cx: &mut ElementContext,
f: impl FnOnce(&mut Self, &mut MenuHandleElementState<M>, &mut ElementContext) -> R,
) -> R {
cx.with_element_state::<MenuHandleElementState<M>, _>(
Some(self.id.clone()),
|element_state, cx| {
let mut element_state = element_state.unwrap().unwrap_or_default();
let result = f(self, &mut element_state, cx);
(result, Some(element_state))
},
)
}
} }
/// Creates a [`RightClickMenu`] /// Creates a [`RightClickMenu`]
@ -50,139 +65,169 @@ pub fn right_click_menu<M: ManagedView>(id: impl Into<ElementId>) -> RightClickM
} }
} }
pub struct MenuHandleState<M> { pub struct MenuHandleElementState<M> {
menu: Rc<RefCell<Option<View<M>>>>, menu: Rc<RefCell<Option<View<M>>>>,
position: Rc<RefCell<Point<Pixels>>>, position: Rc<RefCell<Point<Pixels>>>,
}
impl<M> Clone for MenuHandleElementState<M> {
fn clone(&self) -> Self {
Self {
menu: Rc::clone(&self.menu),
position: Rc::clone(&self.position),
}
}
}
impl<M> Default for MenuHandleElementState<M> {
fn default() -> Self {
Self {
menu: Rc::default(),
position: Rc::default(),
}
}
}
pub struct MenuHandleFrameState {
child_layout_id: Option<LayoutId>, child_layout_id: Option<LayoutId>,
child_element: Option<AnyElement>, child_element: Option<AnyElement>,
menu_element: Option<AnyElement>, menu_element: Option<AnyElement>,
} }
impl<M: ManagedView> Element for RightClickMenu<M> { impl<M: ManagedView> Element for RightClickMenu<M> {
type State = MenuHandleState<M>; type BeforeLayout = MenuHandleFrameState;
type AfterLayout = Hitbox;
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::BeforeLayout) {
self.with_element_state(cx, |this, element_state, cx| {
let mut menu_layout_id = None;
let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| {
let mut overlay = overlay().snap_to_window();
if let Some(anchor) = this.anchor {
overlay = overlay.anchor(anchor);
}
overlay = overlay.position(*element_state.position.borrow());
let mut element = overlay.child(menu.clone()).into_any();
menu_layout_id = Some(element.before_layout(cx));
element
});
let mut child_element = this
.child_builder
.take()
.map(|child_builder| (child_builder)(element_state.menu.borrow().is_some()));
let child_layout_id = child_element
.as_mut()
.map(|child_element| child_element.before_layout(cx));
let layout_id = cx.request_layout(
&gpui::Style::default(),
menu_layout_id.into_iter().chain(child_layout_id),
);
(
layout_id,
MenuHandleFrameState {
child_element,
child_layout_id,
menu_element,
},
)
})
}
fn after_layout(
&mut self, &mut self,
element_state: Option<Self::State>, bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) -> (gpui::LayoutId, Self::State) { ) -> Hitbox {
let (menu, position) = if let Some(element_state) = element_state { cx.with_element_id(Some(self.id.clone()), |cx| {
(element_state.menu, element_state.position) // todo!("occlude child bounds instead?")
} else { let hitbox = cx.insert_hitbox(bounds, false);
(Rc::default(), Rc::default())
};
let mut menu_layout_id = None; if let Some(child) = before_layout.child_element.as_mut() {
child.after_layout(cx);
let menu_element = menu.borrow_mut().as_mut().map(|menu| {
let mut overlay = overlay().snap_to_window();
if let Some(anchor) = self.anchor {
overlay = overlay.anchor(anchor);
} }
overlay = overlay.position(*position.borrow());
let mut element = overlay.child(menu.clone()).into_any(); if let Some(menu) = before_layout.menu_element.as_mut() {
menu_layout_id = Some(element.request_layout(cx)); menu.after_layout(cx);
element }
});
let mut child_element = self hitbox
.child_builder })
.take()
.map(|child_builder| (child_builder)(menu.borrow().is_some()));
let child_layout_id = child_element
.as_mut()
.map(|child_element| child_element.request_layout(cx));
let layout_id = cx.request_layout(
&gpui::Style::default(),
menu_layout_id.into_iter().chain(child_layout_id),
);
(
layout_id,
MenuHandleState {
menu,
position,
child_element,
child_layout_id,
menu_element,
},
)
} }
fn paint( fn paint(
&mut self, &mut self,
bounds: Bounds<gpui::Pixels>, _bounds: Bounds<gpui::Pixels>,
element_state: &mut Self::State, before_layout: &mut Self::BeforeLayout,
hitbox: &mut Self::AfterLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) { ) {
if let Some(mut child) = element_state.child_element.take() { self.with_element_state(cx, |this, element_state, cx| {
child.paint(cx); if let Some(mut child) = before_layout.child_element.take() {
} child.paint(cx);
if let Some(mut menu) = element_state.menu_element.take() {
menu.paint(cx);
return;
}
let Some(builder) = self.menu_builder.take() else {
return;
};
let menu = element_state.menu.clone();
let position = element_state.position.clone();
let attach = self.attach.clone();
let child_layout_id = element_state.child_layout_id.clone();
let child_bounds = cx.layout_bounds(child_layout_id.unwrap());
let interactive_bounds = InteractiveBounds {
bounds: bounds.intersect(&cx.content_mask().bounds),
stacking_order: cx.stacking_order().clone(),
};
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble
&& event.button == MouseButton::Right
&& interactive_bounds.visibly_contains(&event.position, cx)
{
cx.stop_propagation();
cx.prevent_default();
let new_menu = (builder)(cx);
let menu2 = menu.clone();
let previous_focus_handle = cx.focused();
cx.subscribe(&new_menu, move |modal, _: &DismissEvent, cx| {
if modal.focus_handle(cx).contains_focused(cx) {
if previous_focus_handle.is_some() {
cx.focus(previous_focus_handle.as_ref().unwrap())
}
}
*menu2.borrow_mut() = None;
cx.refresh();
})
.detach();
cx.focus_view(&new_menu);
*menu.borrow_mut() = Some(new_menu);
*position.borrow_mut() = if attach.is_some() && child_layout_id.is_some() {
attach.unwrap().corner(child_bounds)
} else {
cx.mouse_position()
};
cx.refresh();
} }
});
if let Some(mut menu) = before_layout.menu_element.take() {
menu.paint(cx);
return;
}
let Some(builder) = this.menu_builder.take() else {
return;
};
let attach = this.attach.clone();
let menu = element_state.menu.clone();
let position = element_state.position.clone();
let child_layout_id = before_layout.child_layout_id.clone();
let child_bounds = cx.layout_bounds(child_layout_id.unwrap());
let hitbox_id = hitbox.id;
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble
&& event.button == MouseButton::Right
&& hitbox_id.is_hovered(cx)
{
cx.stop_propagation();
cx.prevent_default();
let new_menu = (builder)(cx);
let menu2 = menu.clone();
let previous_focus_handle = cx.focused();
cx.subscribe(&new_menu, move |modal, _: &DismissEvent, cx| {
if modal.focus_handle(cx).contains_focused(cx) {
if previous_focus_handle.is_some() {
cx.focus(previous_focus_handle.as_ref().unwrap())
}
}
*menu2.borrow_mut() = None;
cx.refresh();
})
.detach();
cx.focus_view(&new_menu);
*menu.borrow_mut() = Some(new_menu);
*position.borrow_mut() = if attach.is_some() && child_layout_id.is_some() {
attach.unwrap().corner(child_bounds)
} else {
cx.mouse_position()
};
cx.refresh();
}
});
})
} }
} }
impl<M: ManagedView> IntoElement for RightClickMenu<M> { impl<M: ManagedView> IntoElement for RightClickMenu<M> {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<gpui::ElementId> {
Some(self.id.clone())
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }

View file

@ -123,7 +123,6 @@ impl RenderOnce for TabBar {
.absolute() .absolute()
.top_0() .top_0()
.left_0() .left_0()
.z_index(1)
.size_full() .size_full()
.border_b() .border_b()
.border_color(cx.theme().colors().border), .border_color(cx.theme().colors().border),
@ -131,7 +130,6 @@ impl RenderOnce for TabBar {
.child( .child(
h_flex() h_flex()
.id("tabs") .id("tabs")
.z_index(2)
.flex_grow() .flex_grow()
.overflow_x_scroll() .overflow_x_scroll()
.when_some(self.scroll_handle, |cx, scroll_handle| { .when_some(self.scroll_handle, |cx, scroll_handle| {

View file

@ -7,7 +7,7 @@ use crate::{ElevationIndex, UiTextSize};
fn elevated<E: Styled>(this: E, cx: &mut WindowContext, index: ElevationIndex) -> E { fn elevated<E: Styled>(this: E, cx: &mut WindowContext, index: ElevationIndex) -> E {
this.bg(cx.theme().colors().elevated_surface_background) this.bg(cx.theme().colors().elevated_surface_background)
.z_index(index.z_index()) // .z_index(index.z_index()) // TODO! we need a new solution here.
.rounded(px(8.)) .rounded(px(8.))
.border() .border()
.border_color(cx.theme().colors().border_variant) .border_color(cx.theme().colors().border_variant)

View file

@ -563,7 +563,6 @@ impl Render for Dock {
cx.stop_propagation(); cx.stop_propagation();
} }
})) }))
.z_index(1)
.block_mouse(); .block_mouse();
match self.position() { match self.position() {

View file

@ -139,21 +139,15 @@ impl Render for ModalLayer {
return div(); return div();
}; };
div() div().absolute().size_full().top_0().left_0().child(
.absolute() v_flex()
.size_full() .h(px(0.0))
.top_0() .top_20()
.left_0() .flex()
.z_index(169) .flex_col()
.child( .items_center()
v_flex() .track_focus(&active_modal.focus_handle)
.h(px(0.0)) .child(h_flex().child(active_modal.modal.view())),
.top_20() )
.flex()
.flex_col()
.items_center()
.track_focus(&active_modal.focus_handle)
.child(h_flex().child(active_modal.modal.view())),
)
} }
} }

View file

@ -1549,7 +1549,6 @@ impl Pane {
fn render_menu_overlay(menu: &View<ContextMenu>) -> Div { fn render_menu_overlay(menu: &View<ContextMenu>) -> Div {
div() div()
.absolute() .absolute()
.z_index(1)
.bottom_0() .bottom_0()
.right_0() .right_0()
.size_0() .size_0()
@ -1867,7 +1866,6 @@ impl Render for Pane {
.child( .child(
// drag target // drag target
div() div()
.z_index(1)
.invisible() .invisible()
.absolute() .absolute()
.bg(theme::color_alpha( .bg(theme::color_alpha(

View file

@ -258,7 +258,6 @@ impl Member {
.right_3() .right_3()
.elevation_2(cx) .elevation_2(cx)
.p_1() .p_1()
.z_index(1)
.child(status_box) .child(status_box)
.when_some( .when_some(
leader_join_data, leader_join_data,
@ -588,9 +587,9 @@ mod element {
use std::{cell::RefCell, iter, rc::Rc, sync::Arc}; use std::{cell::RefCell, iter, rc::Rc, sync::Arc};
use gpui::{ use gpui::{
px, relative, Along, AnyElement, Axis, Bounds, CursorStyle, Element, IntoElement, px, relative, Along, AnyElement, Axis, Bounds, Element, IntoElement, MouseDownEvent,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Size, Style, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Size, Style, WeakView,
WeakView, WindowContext, WindowContext,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use settings::Settings; use settings::Settings;
@ -753,58 +752,47 @@ mod element {
size: pane_bounds.size.apply_along(axis, |_| px(DIVIDER_SIZE)), size: pane_bounds.size.apply_along(axis, |_| px(DIVIDER_SIZE)),
}; };
cx.with_z_index(3, |cx| { let handle_hitbox = cx.insert_hitbox(handle_bounds, false);
if handle_bounds.contains(&cx.mouse_position()) { cx.paint_quad(gpui::fill(divider_bounds, cx.theme().colors().border));
let stacking_order = cx.stacking_order().clone();
let cursor_style = match axis { cx.on_mouse_event({
Axis::Vertical => CursorStyle::ResizeUpDown, let dragged_handle = dragged_handle.clone();
Axis::Horizontal => CursorStyle::ResizeLeftRight, let flexes = flexes.clone();
}; let workspace = workspace.clone();
cx.set_cursor_style(cursor_style, stacking_order); move |e: &MouseDownEvent, phase, cx| {
if phase.bubble() && handle_hitbox.id.is_hovered(cx) {
dragged_handle.replace(Some(ix));
if e.click_count >= 2 {
let mut borrow = flexes.lock();
*borrow = vec![1.; borrow.len()];
workspace
.update(cx, |this, cx| this.schedule_serialize(cx))
.log_err();
cx.refresh();
}
cx.stop_propagation();
}
} }
});
cx.add_opaque_layer(handle_bounds); cx.on_mouse_event({
cx.paint_quad(gpui::fill(divider_bounds, cx.theme().colors().border)); let workspace = workspace.clone();
move |e: &MouseMoveEvent, phase, cx| {
cx.on_mouse_event({ let dragged_handle = dragged_handle.borrow();
let dragged_handle = dragged_handle.clone(); if phase.bubble() && *dragged_handle == Some(ix) && handle_hitbox.is_hovered(cx)
let flexes = flexes.clone(); {
let workspace = workspace.clone(); Self::compute_resize(
move |e: &MouseDownEvent, phase, cx| { &flexes,
if phase.bubble() && handle_bounds.contains(&e.position) { e,
dragged_handle.replace(Some(ix)); ix,
if e.click_count >= 2 { axis,
let mut borrow = flexes.lock(); pane_bounds.origin,
*borrow = vec![1.; borrow.len()]; axis_bounds.size,
workspace workspace.clone(),
.update(cx, |this, cx| this.schedule_serialize(cx)) cx,
.log_err(); )
cx.refresh();
}
cx.stop_propagation();
}
} }
}); }
cx.on_mouse_event({
let workspace = workspace.clone();
move |e: &MouseMoveEvent, phase, cx| {
let dragged_handle = dragged_handle.borrow();
if phase.bubble() && *dragged_handle == Some(ix) {
Self::compute_resize(
&flexes,
e,
ix,
axis,
pane_bounds.origin,
axis_bounds.size,
workspace.clone(),
cx,
)
}
}
});
}); });
} }
} }
@ -812,40 +800,54 @@ mod element {
impl IntoElement for PaneAxisElement { impl IntoElement for PaneAxisElement {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ui::prelude::ElementId> {
Some(self.basis.into())
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }
} }
impl Element for PaneAxisElement { impl Element for PaneAxisElement {
type State = Rc<RefCell<Option<usize>>>; type BeforeLayout = ();
type AfterLayout = ();
fn request_layout( fn before_layout(
&mut self, &mut self,
state: Option<Self::State>,
cx: &mut ui::prelude::ElementContext, cx: &mut ui::prelude::ElementContext,
) -> (gpui::LayoutId, Self::State) { ) -> (gpui::LayoutId, Self::BeforeLayout) {
let mut style = Style::default(); let mut style = Style::default();
style.flex_grow = 1.; style.flex_grow = 1.;
style.flex_shrink = 1.; style.flex_shrink = 1.;
style.flex_basis = relative(0.).into(); style.flex_basis = relative(0.).into();
style.size.width = relative(1.).into(); style.size.width = relative(1.).into();
style.size.height = relative(1.).into(); style.size.height = relative(1.).into();
let layout_id = cx.request_layout(&style, None); (cx.request_layout(&style, None), ())
let dragged_pane = state.unwrap_or_else(|| Rc::new(RefCell::new(None))); }
(layout_id, dragged_pane)
fn after_layout(
&mut self,
bounds: Bounds<Pixels>,
state: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) {
// we paint children below that need to be commited
todo!("implement commit bounds on pane axis element")
} }
fn paint( fn paint(
&mut self, &mut self,
bounds: gpui::Bounds<ui::prelude::Pixels>, bounds: gpui::Bounds<ui::prelude::Pixels>,
state: &mut Self::State, _: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
cx: &mut ui::prelude::ElementContext, cx: &mut ui::prelude::ElementContext,
) { ) {
let state = cx.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
Some(self.basis.into()),
|state, _cx| {
let state = state
.unwrap()
.unwrap_or_else(|| Rc::new(RefCell::new(None)));
(state.clone(), Some(state))
},
);
let flexes = self.flexes.lock().clone(); let flexes = self.flexes.lock().clone();
let len = self.children.len(); let len = self.children.len();
debug_assert!(flexes.len() == len); debug_assert!(flexes.len() == len);
@ -891,40 +893,34 @@ mod element {
size: child_size, size: child_size,
}; };
bounding_boxes.push(Some(child_bounds)); bounding_boxes.push(Some(child_bounds));
cx.with_z_index(0, |cx| { child.paint(cx);
child.draw(origin, child_size.into(), cx);
});
if active_pane_magnification.is_none() { if active_pane_magnification.is_none() {
cx.with_z_index(1, |cx| { if ix < len - 1 {
if ix < len - 1 { Self::push_handle(
Self::push_handle( self.flexes.clone(),
self.flexes.clone(), state.clone(),
state.clone(), self.axis,
self.axis, ix,
ix, child_bounds,
child_bounds, bounds,
bounds, self.workspace.clone(),
self.workspace.clone(), cx,
cx, );
); }
}
});
} }
origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis)); origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis));
} }
cx.with_z_index(1, |cx| { cx.on_mouse_event({
cx.on_mouse_event({ let state = state.clone();
let state = state.clone(); move |_: &MouseUpEvent, phase, _cx| {
move |_: &MouseUpEvent, phase, _cx| { if phase.bubble() {
if phase.bubble() { state.replace(None);
state.replace(None);
}
} }
}); }
}) });
} }
} }

View file

@ -2744,13 +2744,13 @@ impl Workspace {
} }
fn render_notifications(&self, _cx: &ViewContext<Self>) -> Option<Div> { fn render_notifications(&self, _cx: &ViewContext<Self>) -> Option<Div> {
// TODO! Put these in the right spo
if self.notifications.is_empty() { if self.notifications.is_empty() {
None None
} else { } else {
Some( Some(
div() div()
.absolute() .absolute()
.z_index(100)
.right_3() .right_3()
.bottom_3() .bottom_3()
.w_112() .w_112()
@ -3825,18 +3825,15 @@ impl Render for Workspace {
.border_t() .border_t()
.border_b() .border_b()
.border_color(colors.border) .border_color(colors.border)
.child( .child({
canvas({ let this = cx.view().clone();
let this = cx.view().clone(); canvas(
move |bounds, cx| { move |bounds, cx| this.update(cx, |this, _cx| this.bounds = bounds),
this.update(cx, |this, _cx| { |_, _, _| {},
this.bounds = *bounds; )
})
}
})
.absolute() .absolute()
.size_full(), .size_full()
) })
.on_drag_move( .on_drag_move(
cx.listener(|workspace, e: &DragMoveEvent<DraggedDock>, cx| { cx.listener(|workspace, e: &DragMoveEvent<DraggedDock>, cx| {
match e.drag(cx).0 { match e.drag(cx).0 {
@ -3914,7 +3911,6 @@ impl Render for Workspace {
.children(self.zoomed.as_ref().and_then(|view| { .children(self.zoomed.as_ref().and_then(|view| {
let zoomed_view = view.upgrade()?; let zoomed_view = view.upgrade()?;
let div = div() let div = div()
.z_index(1)
.absolute() .absolute()
.overflow_hidden() .overflow_hidden()
.border_color(colors.border) .border_color(colors.border)
@ -4606,14 +4602,12 @@ pub fn titlebar_height(cx: &mut WindowContext) -> Pixels {
struct DisconnectedOverlay; struct DisconnectedOverlay;
// TODO! Actually render this on top
impl Element for DisconnectedOverlay { impl Element for DisconnectedOverlay {
type State = AnyElement; type BeforeLayout = AnyElement;
type AfterLayout = ();
fn request_layout( fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
&mut self,
_: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
let mut background = cx.theme().colors().elevated_surface_background; let mut background = cx.theme().colors().elevated_surface_background;
background.fade_out(0.2); background.fade_out(0.2);
let mut overlay = div() let mut overlay = div()
@ -4631,29 +4625,33 @@ impl Element for DisconnectedOverlay {
"Your connection to the remote project has been lost.", "Your connection to the remote project has been lost.",
)) ))
.into_any(); .into_any();
(overlay.request_layout(cx), overlay) (overlay.before_layout(cx), overlay)
}
fn after_layout(
&mut self,
bounds: Bounds<Pixels>,
overlay: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) {
cx.insert_hitbox(bounds, true);
overlay.after_layout(cx);
} }
fn paint( fn paint(
&mut self, &mut self,
bounds: Bounds<Pixels>, _: Bounds<Pixels>,
overlay: &mut Self::State, overlay: &mut Self::BeforeLayout,
_: &mut Self::AfterLayout,
cx: &mut ElementContext, cx: &mut ElementContext,
) { ) {
cx.with_z_index(u16::MAX, |cx| { overlay.paint(cx)
cx.add_opaque_layer(bounds);
overlay.paint(cx);
})
} }
} }
impl IntoElement for DisconnectedOverlay { impl IntoElement for DisconnectedOverlay {
type Element = Self; type Element = Self;
fn element_id(&self) -> Option<ui::prelude::ElementId> {
None
}
fn into_element(self) -> Self::Element { fn into_element(self) -> Self::Element {
self self
} }

View file

@ -268,7 +268,7 @@ fn main() {
initialize_workspace(app_state.clone(), cx); initialize_workspace(app_state.clone(), cx);
if stdout_is_a_pty() { if stdout_is_a_pty() {
//todo!(linux): unblock this // todo(linux): unblock this
#[cfg(not(target_os = "linux"))] #[cfg(not(target_os = "linux"))]
upload_panics_and_crashes(http.clone(), cx); upload_panics_and_crashes(http.clone(), cx);
cx.activate(true); cx.activate(true);
@ -997,7 +997,7 @@ fn load_user_themes_in_background(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) {
.detach_and_log_err(cx); .detach_and_log_err(cx);
} }
//todo!(linux): Port fsevents to linux // todo(linux): Port fsevents to linux
/// Spawns a background task to watch the themes directory for changes. /// Spawns a background task to watch the themes directory for changes.
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
fn watch_themes(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) { fn watch_themes(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) {