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
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:
name: (Linux) Run Clippy and tests
runs-on: ubuntu-latest
@ -126,7 +126,7 @@ jobs:
- name: Build Zed
run: cargo build -p zed
# todo!(windows): Actually run the tests
# todo(windows): Actually run the tests
windows_tests:
name: (Windows) Run Clippy and tests
runs-on: windows-latest

114
Cargo.lock generated
View file

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

View file

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

View file

@ -73,7 +73,7 @@
"ctrl-n": "editor::MoveDown",
"ctrl-b": "editor::MoveLeft",
"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-b": "editor::MoveToPreviousWordStart",
"alt-right": "editor::MoveToNextWordEnd",
@ -388,7 +388,7 @@
}
},
// 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",
"bindings": {
@ -412,7 +412,7 @@
}
},
// 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",
"bindings": {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -27,10 +27,7 @@ impl RenderOnce for FacePile {
let player_list = self.faces.into_iter().enumerate().map(|(ix, player)| {
let isnt_last = ix < player_count - 1;
div()
.z_index((player_count - ix) as u16)
.when(isnt_last, |div| div.neg_mr_1())
.child(player)
div().when(isnt_last, |div| div.neg_mr_1()).child(player)
});
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.
///
/// 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 {
let adjustment_factor = if is_light { 0.1 } else { -0.1 };
let hover_adjustment = 1.0 - adjustment_factor;

View file

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

View file

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

View file

@ -51,7 +51,7 @@ pub use display_map::DisplayPoint;
use display_map::*;
pub use editor_settings::EditorSettings;
use element::LineWithInvisibles;
pub use element::{Cursor, EditorElement, HighlightedRange, HighlightedRangeLine};
pub use element::{CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine};
use futures::FutureExt;
use fuzzy::{StringMatch, StringMatchCandidate};
use git::diff_hunk_to_display;
@ -3173,7 +3173,6 @@ impl Editor {
pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
TextLayoutDetails {
text_system: cx.text_system().clone(),
editor_style: self.style.clone().unwrap(),
rem_size: cx.rem_size(),
scroll_anchor: self.scroll_manager.anchor(),
@ -4099,7 +4098,7 @@ impl Editor {
_line_height: Pixels,
_gutter_margin: Pixels,
editor_view: View<Editor>,
) -> Vec<Option<IconButton>> {
) -> Vec<Option<AnyElement>> {
fold_data
.iter()
.enumerate()
@ -4126,6 +4125,7 @@ impl Editor {
.selected(fold_status == FoldStatus::Folded)
.selected_icon(ui::IconName::ChevronRight)
.size(ui::ButtonSize::None)
.into_any_element()
})
})
.flatten()
@ -5258,7 +5258,7 @@ impl Editor {
head = display_map.clip_point(head, Bias::Right);
let goal = SelectionGoal::HorizontalPosition(
display_map
.x_for_display_point(head, &text_layout_details)
.x_for_display_point(head, &text_layout_details, cx)
.into(),
);
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 range = oldest_selection.display_range(&display_map).sorted();
let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
let end_x = display_map.x_for_display_point(range.end, &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, cx);
let positions = start_x.min(end_x)..start_x.max(end_x);
selections.clear();
@ -6337,16 +6337,17 @@ impl Editor {
let range = selection.display_range(&display_map).sorted();
debug_assert_eq!(range.start.row(), range.end.row());
let mut row = range.start.row();
let positions =
if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
px(start)..px(end)
} else {
let start_x =
display_map.x_for_display_point(range.start, &text_layout_details);
let end_x =
display_map.x_for_display_point(range.end, &text_layout_details);
start_x.min(end_x)..start_x.max(end_x)
};
let positions = if let SelectionGoal::HorizontalRange { start, end } =
selection.goal
{
px(start)..px(end)
} else {
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, cx);
start_x.min(end_x)..start_x.max(end_x)
};
while row != end_row {
if above {
@ -7033,7 +7034,7 @@ impl Editor {
let display_point = point.to_display_point(display_snapshot);
let goal = SelectionGoal::HorizontalPosition(
display_snapshot
.x_for_display_point(display_point, &text_layout_details)
.x_for_display_point(display_point, &text_layout_details, cx)
.into(),
);
(display_point, goal)
@ -10137,7 +10138,7 @@ impl ViewInputHandler for Editor {
let scroll_left = scroll_position.x * em_width;
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;
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
this.highlight_inlay_background::<HoverState>(
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,
);
this.hover_state.info_popover = Some(hover_popover);
@ -332,7 +332,7 @@ fn show_hover(
// Highlight the selected symbol using a background highlight
this.highlight_background::<HoverState>(
vec![symbol_range],
|theme| theme.element_hover, // todo! update theme
|theme| theme.element_hover, // todo update theme
cx,
);
} else {

View file

@ -3,11 +3,11 @@
use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
use crate::{char_kind, scroll::ScrollAnchor, CharKind, EditorStyle, ToOffset, ToPoint};
use gpui::{px, Pixels, WindowTextSystem};
use gpui::{px, Pixels};
use language::Point;
use multi_buffer::MultiBufferSnapshot;
use std::{ops::Range, sync::Arc};
use std::ops::Range;
/// Defines search strategy for items in `movement` module.
/// `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
/// taking into account variable width characters.
pub struct TextLayoutDetails {
pub(crate) text_system: Arc<WindowTextSystem>,
pub(crate) editor_style: EditorStyle,
pub(crate) rem_size: Pixels,
pub scroll_anchor: ScrollAnchor,

View file

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

View file

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

View file

@ -96,7 +96,7 @@ objc = "0.2"
flume = "0.11"
open = "5.0.1"
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"] }
wayland-client= { version = "0.31.2" }
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,
) {
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_view_id(entity_id, |cx| {
f(cx).draw(origin, space, cx);
})
let mut element = f(cx);
element.layout(origin, space, cx);
element.paint(cx);
});
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.
//! 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
//!
@ -35,33 +32,48 @@
//! your own custom layout algorithm or rendering a code editor.
use crate::{
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, ElementContext, ElementId, LayoutId,
Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementContext,
ElementId, LayoutId, Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
};
use derive_more::{Deref, DerefMut};
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.
/// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy.
/// You can create custom elements by implementing this trait, see the module-level documentation
/// for more details.
pub trait Element: 'static + IntoElement {
/// The type of state to store for this element between frames. See the module-level documentation
/// for details.
type State: 'static;
/// The type of state returned from [`Element::before_layout`]. A mutable reference to this state is subsequently
/// provided to [`Element::after_layout`] and [`Element::paint`].
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.
/// 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,
state: Option<Self::State>,
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
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.
/// The state argument is the same state that was returned from [`Element::request_layout()`].
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext);
/// The state argument is the same state that was returned from [`Element::before_layout()`].
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`].
fn into_any(self) -> AnyElement {
@ -75,10 +87,6 @@ pub trait IntoElement: Sized {
/// Useful for converting other types into elements automatically, like Strings
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`].
fn into_element(self) -> Self::Element;
@ -86,41 +94,6 @@ pub trait IntoElement: Sized {
fn into_any_element(self) -> AnyElement {
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 {}
@ -188,24 +161,36 @@ impl<C: RenderOnce> Component<C> {
}
impl<C: RenderOnce> Element for Component<C> {
type State = AnyElement;
type BeforeLayout = AnyElement;
type AfterLayout = ();
fn request_layout(
&mut self,
_: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let mut element = self
.0
.take()
.unwrap()
.render(cx.deref_mut())
.into_any_element();
let layout_id = element.request_layout(cx);
let layout_id = element.before_layout(cx);
(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)
}
}
@ -213,10 +198,6 @@ impl<C: RenderOnce> Element for Component<C> {
impl<C: RenderOnce> IntoElement for Component<C> {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
fn into_element(self) -> Self::Element {
self
}
@ -227,9 +208,11 @@ impl<C: RenderOnce> IntoElement for Component<C> {
pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
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);
@ -238,110 +221,103 @@ trait ElementObject {
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> 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.
pub(crate) struct DrawableElement<E: Element> {
element: Option<E>,
phase: ElementDrawPhase<E::State>,
pub struct Drawable<E: Element> {
/// The drawn element.
pub element: E,
phase: ElementDrawPhase<E::BeforeLayout, E::AfterLayout>,
}
#[derive(Default)]
enum ElementDrawPhase<S> {
enum ElementDrawPhase<BeforeLayout, AfterLayout> {
#[default]
Start,
LayoutRequested {
BeforeLayout {
layout_id: LayoutId,
frame_state: Option<S>,
before_layout: BeforeLayout,
},
LayoutComputed {
layout_id: LayoutId,
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.
impl<E: Element> DrawableElement<E> {
impl<E: Element> Drawable<E> {
fn new(element: E) -> Self {
DrawableElement {
element: Some(element),
Drawable {
element,
phase: ElementDrawPhase::Start,
}
}
fn element_id(&self) -> Option<ElementId> {
self.element.as_ref()?.element_id()
fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
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 {
let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
{
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 {
fn after_layout(&mut self, cx: &mut ElementContext) {
match mem::take(&mut self.phase) {
ElementDrawPhase::BeforeLayout {
layout_id,
frame_state,
mut before_layout,
}
| ElementDrawPhase::LayoutComputed {
layout_id,
frame_state,
mut before_layout,
..
} => {
let bounds = cx.layout_bounds(layout_id);
if let Some(mut frame_state) = frame_state {
self.element
.take()
.unwrap()
.paint(bounds, &mut frame_state, cx);
Some(frame_state)
} 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
}
cx.layout_element(|node_id, cx| {
let after_layout = self.element.after_layout(bounds, &mut before_layout, cx);
self.phase = ElementDrawPhase::AfterLayout {
node_id,
bounds,
before_layout,
after_layout,
};
});
}
_ => 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,
) -> Size<Pixels> {
if matches!(&self.phase, ElementDrawPhase::Start) {
self.request_layout(cx);
self.before_layout(cx);
}
let layout_id = match &mut self.phase {
ElementDrawPhase::LayoutRequested {
let layout_id = match mem::take(&mut self.phase) {
ElementDrawPhase::BeforeLayout {
layout_id,
frame_state,
before_layout,
} => {
cx.compute_layout(*layout_id, available_space);
let layout_id = *layout_id;
cx.compute_layout(layout_id, available_space);
self.phase = ElementDrawPhase::LayoutComputed {
layout_id,
available_space,
frame_state: frame_state.take(),
before_layout,
};
layout_id
}
ElementDrawPhase::LayoutComputed {
layout_id,
available_space: prev_available_space,
..
before_layout,
} => {
if available_space != *prev_available_space {
cx.compute_layout(*layout_id, available_space);
*prev_available_space = available_space;
if available_space != prev_available_space {
cx.compute_layout(layout_id, available_space);
}
*layout_id
self.phase = ElementDrawPhase::LayoutComputed {
layout_id,
available_space,
before_layout,
};
layout_id
}
_ => panic!("cannot measure after painting"),
};
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
E: Element,
E::State: 'static,
E::BeforeLayout: 'static,
{
fn element_id(&self) -> Option<ElementId> {
self.as_ref().unwrap().element_id()
fn inner_element(&mut self) -> &mut dyn Any {
&mut self.element
}
fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
DrawableElement::request_layout(self.as_mut().unwrap(), cx)
fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
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) {
DrawableElement::paint(self.take().unwrap(), cx);
Drawable::paint(self, cx);
}
fn measure(
@ -418,16 +391,7 @@ where
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Size<Pixels> {
DrawableElement::measure(self.as_mut().unwrap(), 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);
Drawable::measure(self, available_space, cx)
}
}
@ -438,18 +402,28 @@ impl AnyElement {
pub(crate) fn new<E>(element: E) -> Self
where
E: 'static + Element,
E::State: Any,
E::BeforeLayout: Any,
{
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);
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`.
/// Used for laying out child elements in a parent element.
pub fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
self.0.request_layout(cx)
pub fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
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`.
@ -466,35 +440,44 @@ impl AnyElement {
self.0.measure(available_space, cx)
}
/// Initializes this element and performs layout in the available space, then paints it at the given origin.
pub fn draw(
/// Initializes this element, performs layout if needed and commits its bounds.
pub fn layout(
&mut self,
origin: Point<Pixels>,
absolute_offset: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) {
self.0.draw(origin, available_space, cx)
}
/// Returns the element ID of the element stored in this `AnyElement`, if any.
pub fn inner_id(&self) -> Option<ElementId> {
self.0.element_id()
) -> Size<Pixels> {
let size = self.measure(available_space, cx);
cx.with_absolute_element_offset(absolute_offset, |cx| self.after_layout(cx));
size
}
}
impl Element for AnyElement {
type State = ();
type BeforeLayout = ();
type AfterLayout = ();
fn request_layout(
&mut self,
_: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
let layout_id = self.request_layout(cx);
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
let layout_id = self.before_layout(cx);
(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)
}
}
@ -502,10 +485,6 @@ impl Element for AnyElement {
impl IntoElement for AnyElement {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
fn into_element(self) -> Self::Element {
self
}
@ -521,30 +500,32 @@ pub struct Empty;
impl IntoElement for Empty {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
fn into_element(self) -> Self::Element {
self
}
}
impl Element for Empty {
type State = ();
type BeforeLayout = ();
type AfterLayout = ();
fn request_layout(
&mut self,
_state: Option<Self::State>,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
(cx.request_layout(&crate::Style::default(), None), ())
}
fn after_layout(
&mut self,
_bounds: Bounds<Pixels>,
_state: &mut Self::BeforeLayout,
_cx: &mut ElementContext,
) {
}
fn paint(
&mut self,
_bounds: Bounds<Pixels>,
_state: &mut Self::State,
_before_layout: &mut Self::BeforeLayout,
_after_layout: &mut Self::AfterLayout,
_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.
/// 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 {
paint_callback: Some(Box::new(callback)),
after_layout: Some(Box::new(after_layout)),
paint: Some(Box::new(paint)),
style: StyleRefinement::default(),
}
}
/// A canvas element, meant for accessing the low level paint API without defining a whole
/// custom element
pub struct Canvas {
paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut ElementContext)>>,
pub struct Canvas<T> {
after_layout: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut ElementContext) -> T>>,
paint: Option<Box<dyn FnOnce(Bounds<Pixels>, T, &mut ElementContext)>>,
style: StyleRefinement,
}
impl IntoElement for Canvas {
impl<T: 'static> IntoElement for Canvas<T> {
type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
None
}
fn into_element(self) -> Self::Element {
self
}
}
impl Element for Canvas {
type State = Style;
impl<T: 'static> Element for Canvas<T> {
type BeforeLayout = Style;
type AfterLayout = Option<T>;
fn request_layout(
&mut self,
_: Option<Self::State>,
cx: &mut ElementContext,
) -> (crate::LayoutId, Self::State) {
fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) {
let mut style = Style::default();
style.refine(&self.style);
let layout_id = cx.request_layout(&style, []);
(layout_id, style)
}
fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut 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| {
(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 {
&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 crate::{
point, size, Bounds, DevicePixels, Element, ElementContext, ImageData, InteractiveElement,
InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, SharedUri, Size,
point, size, Bounds, DevicePixels, Element, ElementContext, Hitbox, ImageData,
InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, SharedUri, Size,
StyleRefinement, Styled, UriOrPath,
};
use futures::FutureExt;
@ -88,86 +88,85 @@ impl 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,
element_state: Option<Self::State>,
bounds: Bounds<Pixels>,
_before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> (LayoutId, Self::State) {
) -> Option<Hitbox> {
self.interactivity
.layout(element_state, cx, |style, cx| cx.request_layout(&style, []))
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
element_state: &mut Self::State,
_: &mut Self::BeforeLayout,
hitbox: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
let source = self.source.clone();
self.interactivity.paint(
bounds,
bounds.size,
element_state,
cx,
|style, _scroll_offset, cx| {
self.interactivity
.paint(bounds, hitbox.as_ref(), cx, |style, cx| {
let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.with_z_index(1, |cx| {
match source {
ImageSource::Uri(_) | ImageSource::File(_) => {
let uri_or_path: UriOrPath = match source {
ImageSource::Uri(uri) => uri.into(),
ImageSource::File(path) => path.into(),
_ => unreachable!(),
};
match source {
ImageSource::Uri(_) | ImageSource::File(_) => {
let uri_or_path: UriOrPath = match source {
ImageSource::Uri(uri) => uri.into(),
ImageSource::File(path) => path.into(),
_ => unreachable!(),
};
let image_future = cx.image_cache.get(uri_or_path.clone(), cx);
if let Some(data) = image_future
.clone()
.now_or_never()
.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 image_future = cx.image_cache.get(uri_or_path.clone(), cx);
if let Some(data) = image_future
.clone()
.now_or_never()
.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();
}
}
#[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);
}
};
});
},
)
ImageSource::Data(data) => {
let new_bounds = preserve_aspect_ratio(bounds, data.size());
cx.paint_image(new_bounds, corner_radii, data, self.grayscale)
.log_err();
}
#[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 {
type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
self.interactivity.element_id.clone()
}
fn into_element(self) -> Self::Element {
self
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -828,6 +828,28 @@ where
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> {
@ -2617,6 +2639,12 @@ pub trait Half {
fn half(&self) -> Self;
}
impl Half for i32 {
fn half(&self) -> Self {
self / 2
}
}
impl Half for f32 {
fn half(&self) -> Self {
self / 2.

View file

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

View file

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

View file

@ -564,7 +564,7 @@ impl BladeRenderer {
}
PrimitiveBatch::Paths(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 {
let tile = &self.path_tiles[&path.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_camel_case_types)]
#![allow(non_snake_case)]
//todo!(linux): remove
// todo(linux): remove
#![allow(unused_variables)]
use crate::{PlatformDispatcher, TaskLabel};

View file

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

View file

@ -32,7 +32,7 @@ impl LinuxTextSystem {
pub(crate) fn new() -> Self {
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();
Self(RwLock::new(LinuxTextSystemState {
@ -59,7 +59,7 @@ impl PlatformTextSystem for LinuxTextSystem {
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()?
fn all_font_names(&self) -> Vec<String> {
self.0
@ -71,13 +71,13 @@ impl PlatformTextSystem for LinuxTextSystem {
.collect()
}
// todo!(linux)
// todo(linux)
fn all_font_families(&self) -> Vec<String> {
Vec::new()
}
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();
if let Some(font_id) = lock.font_selections.get(font) {
Ok(*font_id)
@ -127,13 +127,13 @@ impl PlatformTextSystem for LinuxTextSystem {
FontMetrics {
units_per_em: metrics.units_per_em as u32,
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,
underline_position: metrics.underline_offset,
underline_thickness: metrics.stroke_size,
cap_height: metrics.cap_height,
x_height: metrics.x_height,
// todo!(linux): Compute this correctly
// todo(linux): Compute this correctly
bounding_box: Bounds {
origin: point(0.0, 0.0),
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 glyph_metrics = lock.fonts[font_id.0].as_swash().glyph_metrics(&[]);
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
Ok(Bounds {
origin: point(0.0, 0.0),
@ -181,7 +181,7 @@ impl PlatformTextSystem for LinuxTextSystem {
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(
&self,
text: &str,
@ -256,7 +256,7 @@ impl LinuxTextSystemState {
}
fn is_emoji(&self, font_id: FontId) -> bool {
// todo!(linux): implement this correctly
// todo(linux): implement this correctly
self.postscript_names_by_font_id
.get(&font_id)
.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>> {
let font = &self.fonts[params.font_id.0];
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 {
Err(anyhow!("glyph bounds are empty"))
} else {
// todo!(linux) handle subpixel variants
// todo(linux) handle subpixel variants
let bitmap_size = glyph_bounds.size;
let font = &self.fonts[params.font_id.0];
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]
fn layout_line(&mut self, text: &str, font_size: Pixels, font_runs: &[FontRun]) -> LineLayout {
let mut attrs_list = AttrsList::new(Attrs::new());
let mut offs = 0;
for run in font_runs {
// 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.font_system.db().face(font.id()).unwrap();
attrs_list.add_span(
@ -343,11 +343,11 @@ impl LinuxTextSystemState {
let layout = line.layout(
&mut self.font_system,
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,
);
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();
for glyph in &layout.glyphs {
let font_id = glyph.font_id;
@ -358,7 +358,7 @@ impl LinuxTextSystemState {
.unwrap(),
);
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 {
id: GlyphId(glyph.glyph_id as u32),
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)]
pub(crate) use client::*;

View file

@ -183,7 +183,7 @@ impl Client for WaylandClient {
let decoration =
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.
//
// 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 => {
let input = PlatformInput::KeyDown(KeyDownEvent {
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());

View file

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

View file

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

View file

@ -1,4 +1,4 @@
//todo!(linux): remove
// todo(linux): remove
#![allow(unused)]
use crate::{
@ -99,7 +99,7 @@ pub(crate) struct X11WindowState {
#[derive(Clone)]
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 {
fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle {
let mut wh = blade_rwh::XcbWindowHandle::empty();
@ -301,7 +301,7 @@ impl X11WindowState {
let mut inner = self.inner.borrow_mut();
let old_bounds = mem::replace(&mut inner.bounds, bounds);
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 ( )
let gpu_size = query_render_extent(&self.xcb_connection, self.x_window);
if inner.renderer.viewport_size() != gpu_size {
@ -377,12 +377,12 @@ impl PlatformWindow for X11Window {
self.0.inner.borrow_mut().scale_factor
}
//todo!(linux)
// todo(linux)
fn titlebar_height(&self) -> Pixels {
unimplemented!()
}
//todo!(linux)
// todo(linux)
fn appearance(&self) -> WindowAppearance {
WindowAppearance::Light
}
@ -402,7 +402,7 @@ impl PlatformWindow for X11Window {
)
}
//todo!(linux)
// todo(linux)
fn modifiers(&self) -> Modifiers {
Modifiers::default()
}
@ -419,7 +419,7 @@ impl PlatformWindow for X11Window {
self.0.inner.borrow_mut().input_handler.take()
}
//todo!(linux)
// todo(linux)
fn prompt(
&self,
_level: PromptLevel,
@ -447,10 +447,10 @@ impl PlatformWindow for X11Window {
});
}
//todo!(linux)
// todo(linux)
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:
//
// https://docs.gtk.org/gtk3/signal.Entry.insert-emoji.html
@ -460,17 +460,17 @@ impl PlatformWindow for X11Window {
unimplemented!()
}
//todo!(linux)
// todo(linux)
fn minimize(&self) {
unimplemented!()
}
//todo!(linux)
// todo(linux)
fn zoom(&self) {
unimplemented!()
}
//todo!(linux)
// todo(linux)
fn toggle_full_screen(&self) {
unimplemented!()
}
@ -511,7 +511,7 @@ impl PlatformWindow for X11Window {
self.0.callbacks.borrow_mut().appearance_changed = Some(callback);
}
//todo!(linux)
// todo(linux)
fn is_topmost_for_position(&self, _position: Point<Pixels>) -> bool {
unimplemented!()
}

View file

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

View file

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

View file

@ -7,7 +7,7 @@ use std::borrow::Cow;
pub(crate) struct TestTextSystem {}
//todo!(linux)
// todo(linux)
#[allow(unused)]
impl PlatformTextSystem for TestTextSystem {
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))]
use crate::{
point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, EntityId, Hsla, Pixels,
Point, ScaledPixels, StackingOrder,
bounds_tree::BoundsTree, point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges,
Hsla, Pixels, Point, ScaledPixels,
};
use collections::{BTreeMap, FxHashSet};
use std::{fmt::Debug, iter::Peekable, slice};
#[allow(non_camel_case_types, unused)]
pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
pub(crate) type LayerId = 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)]
pub(crate) struct Scene {
last_layer: Option<(StackingOrder, LayerId)>,
layers_by_order: BTreeMap<StackingOrder, LayerId>,
orders_by_layer: BTreeMap<LayerId, StackingOrder>,
pub(crate) primitives: Vec<Primitive>,
primitive_bounds: BoundsTree<ScaledPixels, ()>,
pub(crate) shadows: Vec<Shadow>,
pub(crate) quads: Vec<Quad>,
pub(crate) paths: Vec<Path<ScaledPixels>>,
@ -54,12 +27,11 @@ pub(crate) struct Scene {
impl Scene {
pub fn clear(&mut self) {
self.last_layer = None;
self.layers_by_order.clear();
self.orders_by_layer.clear();
self.primitives.clear();
self.primitive_bounds.clear();
self.paths.clear();
self.shadows.clear();
self.quads.clear();
self.paths.clear();
self.underlines.clear();
self.monochrome_sprites.clear();
self.polychrome_sprites.clear();
@ -70,6 +42,66 @@ impl Scene {
&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> {
BatchIterator {
shadows: &self.shadows,
@ -95,162 +127,54 @@ impl Scene {
surfaces_iter: self.surfaces.iter().peekable(),
}
}
}
pub(crate) fn insert(&mut self, order: &StackingOrder, primitive: impl Into<Primitive>) {
let primitive = primitive.into();
let clipped_bounds = primitive
.bounds()
.intersect(&primitive.content_mask().bounds);
if clipped_bounds.size.width <= ScaledPixels(0.)
|| clipped_bounds.size.height <= ScaledPixels(0.)
{
return;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
pub(crate) enum PrimitiveKind {
Shadow,
#[default]
Quad,
Path,
Underline,
MonochromeSprite,
PolychromeSprite,
Surface,
}
let layer_id = self.layer_id_for_order(order);
match primitive {
Primitive::Shadow(mut shadow) => {
shadow.layer_id = layer_id;
self.shadows.push(shadow);
}
Primitive::Quad(mut quad) => {
quad.layer_id = layer_id;
self.quads.push(quad);
}
Primitive::Path(mut path) => {
path.layer_id = layer_id;
path.id = PathId(self.paths.len());
self.paths.push(path);
}
Primitive::Underline(mut underline) => {
underline.layer_id = layer_id;
self.underlines.push(underline);
}
Primitive::MonochromeSprite(mut sprite) => {
sprite.layer_id = layer_id;
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);
}
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
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,
}
}
fn layer_id_for_order(&mut self, order: &StackingOrder) -> LayerId {
if let Some((last_order, last_layer_id)) = self.last_layer.as_ref() {
if order == last_order {
return *last_layer_id;
}
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,
}
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)]
pub(crate) enum PrimitiveBatch<'a> {
Shadows(&'a [Shadow]),
@ -507,8 +383,6 @@ pub(crate) enum PrimitiveBatch<'a> {
#[derive(Default, Debug, Clone, Eq, PartialEq)]
#[repr(C)]
pub(crate) struct Quad {
pub view_id: ViewId,
pub layer_id: LayerId,
pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
@ -539,8 +413,6 @@ impl From<Quad> for Primitive {
#[derive(Debug, Clone, Eq, PartialEq)]
#[repr(C)]
pub(crate) struct Underline {
pub view_id: ViewId,
pub layer_id: LayerId,
pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
@ -570,8 +442,6 @@ impl From<Underline> for Primitive {
#[derive(Debug, Clone, Eq, PartialEq)]
#[repr(C)]
pub(crate) struct Shadow {
pub view_id: ViewId,
pub layer_id: LayerId,
pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub corner_radii: Corners<ScaledPixels>,
@ -602,8 +472,6 @@ impl From<Shadow> for Primitive {
#[derive(Clone, Debug, Eq, PartialEq)]
#[repr(C)]
pub(crate) struct MonochromeSprite {
pub view_id: ViewId,
pub layer_id: LayerId,
pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
@ -635,8 +503,6 @@ impl From<MonochromeSprite> for Primitive {
#[derive(Clone, Debug, Eq, PartialEq)]
#[repr(C)]
pub(crate) struct PolychromeSprite {
pub view_id: ViewId,
pub layer_id: LayerId,
pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
@ -669,8 +535,6 @@ impl From<PolychromeSprite> for Primitive {
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct Surface {
pub view_id: ViewId,
pub layer_id: LayerId,
pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
@ -700,11 +564,9 @@ impl From<Surface> for Primitive {
pub(crate) struct PathId(pub(crate) usize);
/// 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(crate) id: PathId,
pub(crate) view_id: ViewId,
layer_id: LayerId,
order: DrawOrder,
pub(crate) bounds: Bounds<P>,
pub(crate) content_mask: ContentMask<P>,
@ -720,8 +582,6 @@ impl Path<Pixels> {
pub fn new(start: Point<Pixels>) -> Self {
Self {
id: PathId(0),
view_id: ViewId::default(),
layer_id: LayerId::default(),
order: DrawOrder::default(),
vertices: Vec::new(),
start,
@ -740,8 +600,6 @@ impl Path<Pixels> {
pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
Path {
id: self.id,
view_id: self.view_id,
layer_id: self.layer_id,
order: self.order,
bounds: self.bounds.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.
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
#[cfg(debug_assertions)]
pub debug: bool,
@ -208,7 +205,7 @@ impl Default for TextStyle {
fn default() -> Self {
TextStyle {
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") {
"FreeMono".into()
} else {
@ -323,6 +320,13 @@ pub struct HighlightStyle {
impl Eq for HighlightStyle {}
impl Style {
/// Returns true if the style is visible and the background is opaque.
pub fn has_opaque_background(&self) -> bool {
self.background
.as_ref()
.is_some_and(|fill| fill.color().is_some_and(|color| !color.is_transparent()))
}
/// Get the text style in this element style.
pub fn text_style(&self) -> Option<&TextStyleRefinement> {
if self.text.is_some() {
@ -402,97 +406,87 @@ impl Style {
let rem_size = cx.rem_size();
cx.with_z_index(0, |cx| {
cx.paint_shadows(
bounds,
self.corner_radii.to_pixels(bounds.size, rem_size),
&self.box_shadow,
);
});
cx.paint_shadows(
bounds,
self.corner_radii.to_pixels(bounds.size, rem_size),
&self.box_shadow,
);
let background_color = self.background.as_ref().and_then(Fill::color);
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();
border_color.a = 0.;
cx.paint_quad(quad(
bounds,
self.corner_radii.to_pixels(bounds.size, rem_size),
background_color.unwrap_or_default(),
Edges::default(),
border_color,
));
});
let mut border_color = background_color.unwrap_or_default();
border_color.a = 0.;
cx.paint_quad(quad(
bounds,
self.corner_radii.to_pixels(bounds.size, rem_size),
background_color.unwrap_or_default(),
Edges::default(),
border_color,
));
}
cx.with_z_index(2, |cx| {
continuation(cx);
});
continuation(cx);
if self.is_border_visible() {
cx.with_z_index(3, |cx| {
let corner_radii = self.corner_radii.to_pixels(bounds.size, rem_size);
let border_widths = self.border_widths.to_pixels(rem_size);
let max_border_width = border_widths.max();
let max_corner_radius = corner_radii.max();
let corner_radii = self.corner_radii.to_pixels(bounds.size, rem_size);
let border_widths = self.border_widths.to_pixels(rem_size);
let max_border_width = border_widths.max();
let max_corner_radius = corner_radii.max();
let top_bounds = Bounds::from_corners(
bounds.origin,
bounds.upper_right()
+ point(Pixels::ZERO, max_border_width.max(max_corner_radius)),
);
let bottom_bounds = Bounds::from_corners(
bounds.lower_left()
- point(Pixels::ZERO, max_border_width.max(max_corner_radius)),
bounds.lower_right(),
);
let left_bounds = Bounds::from_corners(
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),
bottom_bounds.upper_right(),
);
let top_bounds = Bounds::from_corners(
bounds.origin,
bounds.upper_right() + point(Pixels::ZERO, max_border_width.max(max_corner_radius)),
);
let bottom_bounds = Bounds::from_corners(
bounds.lower_left() - point(Pixels::ZERO, max_border_width.max(max_corner_radius)),
bounds.lower_right(),
);
let left_bounds = Bounds::from_corners(
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),
bottom_bounds.upper_right(),
);
let mut background = self.border_color.unwrap_or_default();
background.a = 0.;
let quad = quad(
bounds,
corner_radii,
background,
border_widths,
self.border_color.unwrap_or_default(),
);
let mut background = self.border_color.unwrap_or_default();
background.a = 0.;
let quad = quad(
bounds,
corner_radii,
background,
border_widths,
self.border_color.unwrap_or_default(),
);
cx.with_content_mask(Some(ContentMask { bounds: top_bounds }), |cx| {
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: top_bounds }), |cx| {
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);
},
);
}
#[cfg(debug_assertions)]
@ -545,7 +539,6 @@ impl Default for Style {
box_shadow: Default::default(),
text: TextStyleRefinement::default(),
mouse_cursor: None,
z_index: None,
#[cfg(debug_assertions)]
debug: false,

View file

@ -15,12 +15,6 @@ pub trait Styled: Sized {
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`.
/// [Docs](https://tailwindcss.com/docs/position)
fn relative(mut self) -> Self {

View file

@ -51,7 +51,7 @@ impl TaffyLayoutEngine {
self.styles.get(&layout_id)
}
pub fn request_layout(
pub fn before_layout(
&mut self,
style: &Style,
rem_size: Pixels,
@ -447,6 +447,27 @@ pub enum AvailableSpace {
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 {
fn from(space: AvailableSpace) -> TaffyAvailableSpace {
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 {
self.line_layout_cache.with_view(view_id, f)
pub(crate) fn set_parent_view_id(&self, view_id: Option<EntityId>) {
self.line_layout_cache.set_parent_view_id(view_id);
}
/// 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_strikethrough: Option<(Point<Pixels>, StrikethroughStyle)> = None;
let mut current_background: Option<(Point<Pixels>, Hsla)> = None;
let text_system = cx.text_system().clone();
let mut glyph_origin = origin;
let mut prev_glyph_position = Point::default();
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() {
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 std::{
borrow::Borrow,
cell::Cell,
hash::{Hash, Hasher},
sync::Arc,
};
@ -276,7 +277,7 @@ impl WrappedLineLayout {
}
pub(crate) struct LineLayoutCache {
view_stack: Mutex<Vec<EntityId>>,
parent_view_id: Cell<Option<EntityId>>,
previous_frame: Mutex<FxHashMap<CacheKey, Arc<LineLayout>>>,
current_frame: RwLock<FxHashMap<CacheKey, Arc<LineLayout>>>,
previous_frame_wrapped: Mutex<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
@ -287,7 +288,7 @@ pub(crate) struct LineLayoutCache {
impl LineLayoutCache {
pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
Self {
view_stack: Mutex::default(),
parent_view_id: Cell::default(),
previous_frame: Mutex::default(),
current_frame: RwLock::default(),
previous_frame_wrapped: Mutex::default(),
@ -297,8 +298,6 @@ impl LineLayoutCache {
}
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 curr_frame = self.current_frame.write();
for (key, layout) in prev_frame.drain() {
@ -324,15 +323,8 @@ impl LineLayoutCache {
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 {
self.view_stack.lock().push(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 set_parent_view_id(&self, view_id: Option<EntityId>) {
self.parent_view_id.replace(view_id);
}
pub fn layout_wrapped_line(
@ -347,7 +339,7 @@ impl LineLayoutCache {
font_size,
runs,
wrap_width,
parent_view_id: self.parent_view_id(),
parent_view_id: self.parent_view_id.get(),
} as &dyn AsCacheKeyRef;
let current_frame = self.current_frame_wrapped.upgradable_read();
@ -376,7 +368,7 @@ impl LineLayoutCache {
font_size,
runs: SmallVec::from(runs),
wrap_width,
parent_view_id: self.parent_view_id(),
parent_view_id: self.parent_view_id.get(),
};
current_frame.insert(key, layout.clone());
layout
@ -389,7 +381,7 @@ impl LineLayoutCache {
font_size,
runs,
wrap_width: None,
parent_view_id: self.parent_view_id(),
parent_view_id: self.parent_view_id.get(),
} as &dyn AsCacheKeyRef;
let current_frame = self.current_frame.upgradable_read();
@ -408,7 +400,7 @@ impl LineLayoutCache {
font_size,
runs: SmallVec::from(runs),
wrap_width: None,
parent_view_id: self.parent_view_id(),
parent_view_id: self.parent_view_id.get(),
};
current_frame.insert(key, layout.clone());
layout

View file

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

View file

@ -1,13 +1,12 @@
use crate::{
px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext,
AvailableSpace, Bounds, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId,
DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten,
Global, GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchResult,
Keystroke, KeystrokeEvent, Model, ModelContext, Modifiers, MouseButton, MouseMoveEvent,
MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point,
PromptLevel, Render, ScaledPixels, SharedString, Size, SubscriberSet, Subscription,
TaffyLayoutEngine, Task, View, VisualContext, WeakView, WindowAppearance, WindowBounds,
WindowOptions, WindowTextSystem,
px, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext, Bounds,
Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId,
Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten, Global, GlobalElementId,
Hsla, KeyBinding, KeyDownEvent, KeyMatch, KeymatchResult, Keystroke, KeystrokeEvent, Model,
ModelContext, Modifiers, MouseButton, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas,
PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Render, ScaledPixels,
SharedString, Size, SubscriberSet, Subscription, TaffyLayoutEngine, Task, View, VisualContext,
WeakView, WindowAppearance, WindowBounds, WindowOptions, WindowTextSystem,
};
use anyhow::{anyhow, Context as _, Result};
use collections::FxHashSet;
@ -37,8 +36,6 @@ use util::{measure, ResultExt};
mod 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.
/// 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)]
@ -257,6 +254,7 @@ pub struct Window {
pub(crate) element_id_stack: GlobalElementId,
pub(crate) rendered_frame: Frame,
pub(crate) next_frame: Frame,
pub(crate) next_hitbox_id: HitboxId,
next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>>,
pub(crate) dirty_views: FxHashSet<EntityId>,
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
@ -264,6 +262,7 @@ pub struct Window {
focus_lost_listeners: SubscriberSet<(), AnyObserver>,
default_prevented: bool,
mouse_position: Point<Pixels>,
mouse_hit_test: HitTest,
modifiers: Modifiers,
scale_factor: f32,
bounds: WindowBounds,
@ -451,12 +450,14 @@ impl Window {
rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
next_frame_callbacks,
next_hitbox_id: HitboxId::default(),
dirty_views: FxHashSet::default(),
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
focus_listeners: SubscriberSet::new(),
focus_lost_listeners: SubscriberSet::new(),
default_prevented: true,
mouse_position,
mouse_hit_test: HitTest::default(),
modifiers,
scale_factor,
bounds,
@ -583,7 +584,7 @@ impl<'a> WindowContext<'a> {
}
/// Accessor for the text system.
pub fn text_system(&self) -> &Arc<WindowTextSystem> {
pub fn text_system(&self) -> &WindowTextSystem {
&self.window.text_system
}
@ -857,95 +858,6 @@ impl<'a> WindowContext<'a> {
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
/// the contents of the new [Scene], use [present].
#[profiling::function]
@ -959,54 +871,7 @@ impl<'a> WindowContext<'a> {
requested_handler.handler = input_handler;
}
let root_view = self.window.root_view.take().unwrap();
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.with_element_context(|cx| cx.draw_roots());
self.window.dirty_views.clear();
self.window
@ -1018,13 +883,10 @@ impl<'a> WindowContext<'a> {
);
self.window.next_frame.focus = self.window.focus;
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.
let cursor_style_request = self.window.next_frame.requested_cursor_style.take();
if self.is_window_active() {
let cursor_style =
cursor_style_request.map_or(CursorStyle::Arrow, |request| request.style);
let cursor_style = self.compute_cursor_style().unwrap_or(CursorStyle::Arrow);
self.platform.set_cursor_style(cursor_style);
}
@ -1097,6 +959,18 @@ impl<'a> WindowContext<'a> {
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.
/// You can create a keystroke with Keystroke::parse("").
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) {
if let Some(mut handlers) = self
.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));
self.window.mouse_hit_test = self.window.rendered_frame.hit_test(self.mouse_position());
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
// special purposes, such as detecting events outside of a given Bounds.
for (_, _, handler) in &mut handlers {
self.with_element_context(|cx| {
handler(event, DispatchPhase::Capture, cx);
});
if !self.app.propagate_event {
for listener in &mut mouse_listeners {
let listener = listener.as_mut().unwrap();
listener(event, DispatchPhase::Capture, cx);
if !cx.app.propagate_event {
break;
}
}
// Bubble phase, where most normal handlers do their work.
if self.app.propagate_event {
for (_, _, handler) in handlers.iter_mut().rev() {
self.with_element_context(|cx| {
handler(event, DispatchPhase::Bubble, cx);
});
if !self.app.propagate_event {
if cx.app.propagate_event {
for listener in mouse_listeners.iter_mut().rev() {
let listener = listener.as_mut().unwrap();
listener(event, DispatchPhase::Bubble, cx);
if !cx.app.propagate_event {
break;
}
}
}
self.window
.rendered_frame
.mouse_listeners
.insert(event.type_id(), handlers);
}
});
self.window.rendered_frame.mouse_listeners = mouse_listeners;
if self.app.propagate_event && self.has_active_drag() {
if event.is::<MouseMoveEvent>() {
@ -1337,6 +1200,7 @@ impl<'a> WindowContext<'a> {
})
.log_err();
}));
self.window.pending_input = Some(currently_pending);
self.propagate_event = false;
@ -1645,12 +1509,11 @@ impl<'a> WindowContext<'a> {
}
pub(crate) fn parent_view_id(&self) -> EntityId {
*self
.window
self.window
.next_frame
.view_stack
.last()
.expect("a view should always be on the stack while drawing")
.dispatch_tree
.parent_view_id()
.unwrap()
}
/// 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 {
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>;
fn element_id(&self) -> Option<gpui::ElementId> {
None
}
fn into_element(self) -> Self::Element {
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
// without escalating.
smol::fs::remove_file(link_path).await.log_err();
// todo!("windows")
// todo("windows")
#[cfg(not(windows))]
{
if smol::fs::unix::symlink(&cli_path, link_path)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -73,7 +73,7 @@ impl TestServer {
}
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"))]
self.executor.simulate_random_delay().await;
let mut server_rooms = self.rooms.lock();
@ -87,7 +87,7 @@ impl TestServer {
async fn delete_room(&self, room: String) -> Result<()> {
// 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"))]
self.executor.simulate_random_delay().await;
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<()> {
//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"))]
self.executor.simulate_random_delay().await;
@ -147,7 +147,7 @@ impl TestServer {
}
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"))]
self.executor.simulate_random_delay().await;
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<()> {
// 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"))]
self.executor.simulate_random_delay().await;
@ -193,7 +193,7 @@ impl TestServer {
identity: String,
permission: proto::ParticipantPermission,
) -> 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"))]
self.executor.simulate_random_delay().await;
let mut server_rooms = self.rooms.lock();
@ -205,7 +205,7 @@ impl TestServer {
}
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"))]
self.executor.simulate_random_delay().await;
let mut server_rooms = self.rooms.lock();
@ -221,7 +221,7 @@ impl TestServer {
token: String,
local_track: LocalVideoTrack,
) -> 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"))]
self.executor.simulate_random_delay().await;
let claims = live_kit_server::token::validate(&token, &self.secret_key)?;
@ -276,7 +276,7 @@ impl TestServer {
token: String,
_local_track: &LocalAudioTrack,
) -> 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"))]
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>>> {
let this = self.clone();
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"))]
{
let server = this.test_server();

View file

@ -7,7 +7,6 @@ mod picker;
mod scroll;
mod text;
mod viewport_units;
mod z_index;
pub use auto_height_editor::*;
pub use cursor::*;
@ -18,4 +17,3 @@ pub use picker::*;
pub use scroll::*;
pub use text::*;
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,
Text,
ViewportUnits,
ZIndex,
Picker,
}
@ -67,7 +66,6 @@ impl ComponentStory {
Self::TabBar => cx.new_view(|_| ui::TabBarStory).into(),
Self::ToggleButton => cx.new_view(|_| ui::ToggleButtonStory).into(),
Self::ViewportUnits => cx.new_view(|_| crate::stories::ViewportUnitsStory).into(),
Self::ZIndex => cx.new_view(|_| ZIndexStory).into(),
Self::Picker => PickerStory::new(cx).into(),
}
}

View file

@ -399,7 +399,7 @@ impl TerminalBuilder {
#[cfg(unix)]
let (fd, shell_pid) = (pty.file().as_raw_fd(), pty.child().id());
// todo!("windows")
// todo("windows")
#[cfg(windows)]
let (fd, shell_pid) = (-1, 0);
@ -667,7 +667,7 @@ impl Terminal {
fn update_process_info(&mut self) -> bool {
#[cfg(unix)]
let mut pid = unsafe { libc::tcgetpgrp(self.shell_fd as i32) };
// todo!("windows")
// todo("windows")
#[cfg(windows)]
let mut pid = -1;
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(),
terminal_background: bg,
// todo!("Use one colors for terminal")
// todo("Use one colors for terminal")
terminal_foreground: crate::white().dark().step_12(),
terminal_bright_foreground: crate::white().dark().step_11(),
terminal_dim_foreground: crate::white().dark().step_10(),

View file

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

View file

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

View file

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

View file

@ -2,9 +2,9 @@ use std::{cell::RefCell, rc::Rc};
use gpui::{
overlay, point, prelude::FluentBuilder, px, rems, AnchorCorner, AnyElement, Bounds,
DismissEvent, DispatchPhase, Element, ElementContext, ElementId, InteractiveBounds,
IntoElement, LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View,
VisualContext, WindowContext,
DismissEvent, DispatchPhase, Element, ElementContext, ElementId, HitboxId, IntoElement,
LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext,
WindowContext,
};
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`]
@ -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_element: Option<AnyElement>,
child_bounds: Option<Bounds<Pixels>>,
menu_element: Option<AnyElement>,
menu: Rc<RefCell<Option<View<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,
element_state: Option<Self::State>,
_bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> (gpui::LayoutId, Self::State) {
let mut menu_layout_id = None;
let (menu, child_bounds) = if let Some(element_state) = element_state {
(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),
);
) -> Option<HitboxId> {
self.with_element_state(cx, |_this, element_state, cx| {
if let Some(child) = before_layout.child_element.as_mut() {
child.after_layout(cx);
}
let mut element = overlay.child(menu.clone()).into_any();
menu_layout_id = Some(element.request_layout(cx));
element
});
if let Some(menu) = before_layout.menu_element.as_mut() {
menu.after_layout(cx);
}
let mut child_element = self
.child_builder
.take()
.map(|child_builder| (child_builder)(menu.clone(), self.menu_builder.clone()));
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,
},
)
before_layout.child_layout_id.map(|layout_id| {
let bounds = cx.layout_bounds(layout_id);
element_state.child_bounds = Some(bounds);
cx.insert_hitbox(bounds, false).id
})
})
}
fn paint(
&mut self,
_: Bounds<gpui::Pixels>,
element_state: &mut Self::State,
before_layout: &mut Self::BeforeLayout,
child_hitbox: &mut Option<HitboxId>,
cx: &mut ElementContext,
) {
if let Some(mut child) = element_state.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()
}
})
self.with_element_state(cx, |_this, _element_state, cx| {
if let Some(mut child) = before_layout.child_element.take() {
child.paint(cx);
}
}
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> {
type Element = Self;
fn element_id(&self) -> Option<gpui::ElementId> {
Some(self.id.clone())
}
fn into_element(self) -> Self::Element {
self
}

View file

@ -2,7 +2,7 @@ use std::{cell::RefCell, rc::Rc};
use gpui::{
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,
};
@ -37,6 +37,21 @@ impl<M: ManagedView> RightClickMenu<M> {
self.attach = Some(attach);
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`]
@ -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>>>>,
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_element: Option<AnyElement>,
menu_element: Option<AnyElement>,
}
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,
element_state: Option<Self::State>,
bounds: Bounds<Pixels>,
before_layout: &mut Self::BeforeLayout,
cx: &mut ElementContext,
) -> (gpui::LayoutId, Self::State) {
let (menu, position) = if let Some(element_state) = element_state {
(element_state.menu, element_state.position)
} else {
(Rc::default(), Rc::default())
};
) -> Hitbox {
cx.with_element_id(Some(self.id.clone()), |cx| {
// todo!("occlude child bounds instead?")
let hitbox = cx.insert_hitbox(bounds, false);
let mut menu_layout_id = None;
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);
if let Some(child) = before_layout.child_element.as_mut() {
child.after_layout(cx);
}
overlay = overlay.position(*position.borrow());
let mut element = overlay.child(menu.clone()).into_any();
menu_layout_id = Some(element.request_layout(cx));
element
});
if let Some(menu) = before_layout.menu_element.as_mut() {
menu.after_layout(cx);
}
let mut child_element = self
.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,
},
)
hitbox
})
}
fn paint(
&mut self,
bounds: Bounds<gpui::Pixels>,
element_state: &mut Self::State,
_bounds: Bounds<gpui::Pixels>,
before_layout: &mut Self::BeforeLayout,
hitbox: &mut Self::AfterLayout,
cx: &mut ElementContext,
) {
if let Some(mut child) = element_state.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();
self.with_element_state(cx, |this, element_state, cx| {
if let Some(mut child) = before_layout.child_element.take() {
child.paint(cx);
}
});
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> {
type Element = Self;
fn element_id(&self) -> Option<gpui::ElementId> {
Some(self.id.clone())
}
fn into_element(self) -> Self::Element {
self
}

View file

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

View file

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

View file

@ -139,21 +139,15 @@ impl Render for ModalLayer {
return div();
};
div()
.absolute()
.size_full()
.top_0()
.left_0()
.z_index(169)
.child(
v_flex()
.h(px(0.0))
.top_20()
.flex()
.flex_col()
.items_center()
.track_focus(&active_modal.focus_handle)
.child(h_flex().child(active_modal.modal.view())),
)
div().absolute().size_full().top_0().left_0().child(
v_flex()
.h(px(0.0))
.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 {
div()
.absolute()
.z_index(1)
.bottom_0()
.right_0()
.size_0()
@ -1867,7 +1866,6 @@ impl Render for Pane {
.child(
// drag target
div()
.z_index(1)
.invisible()
.absolute()
.bg(theme::color_alpha(

View file

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

View file

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

View file

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