mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 00:02:23 +00:00
perf(diff-overlay): draw the patch as a virtualised row list (#799)
The overlay built its whole patch as a nested element tree on every frame: a card per file, a header per hunk, six elements per line. gpui notifies the view on each scroll wheel event, so a few hundred lines of diff rebuilt tens of thousands of elements tens of times a second, and the window stalled. Flatten the tree into one row per line in a new `diff_list` module and draw it with `gpui::list`, which builds only the rows on screen. The rows are rebuilt only when what they are built from changes, so scrolling no longer re-splits hunks or re-clones every line, and a change to one file splices just the rows it touched rather than resetting the list and losing the scroll position. The key that decides a rebuild takes the snapshot each frame was asked about even when it matched only by contents. A probe that finds nothing new still lands a fresh `Arc` over an equal snapshot; a key left pointing at the old one would go on walking the whole patch to prove the two equal, once per wheel event, which is the cost the key exists to avoid. A list counts a row it has not laid out yet as zero tall, which left the scrollbar reading an 800-line patch as one viewport: its thumb filled the track, and a drag from top to bottom travelled 248px and stopped. The rows below the fold are counted at the 19px both views already give a line of a patch, through `ListState::with_size_hint` — added to the gpui fork for this, `Cargo.lock` following its `tty7` branch to `ece710e3`. A card cannot survive that flattening — its rows are separate items now — so the frame it drew is gone, and with it the grey header bars and hunk bands that made the overlay the one view in the app still speaking gpui-component's default container language. The rows take the source control panel's own measurements instead: 26px, 10px inset, 5px radius, colour only under the pointer. The title bar's view switch loses its border for the same reason.
This commit is contained in:
Generated
+45
-45
@@ -298,7 +298,7 @@ version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -309,7 +309,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -891,7 +891,7 @@ dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.13.0",
|
||||
"itertools 0.11.0",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
@@ -1483,7 +1483,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collections"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"gpui_util",
|
||||
"indexmap",
|
||||
@@ -2047,7 +2047,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "derive_refineable"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2104,7 +2104,7 @@ dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2142,7 +2142,7 @@ version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a"
|
||||
dependencies = [
|
||||
"libloading 0.8.9",
|
||||
"libloading 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2394,7 +2394,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3199,7 +3199,7 @@ dependencies = [
|
||||
"log",
|
||||
"presser",
|
||||
"thiserror 2.0.20",
|
||||
"windows 0.62.2",
|
||||
"windows 0.58.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3225,7 +3225,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui"
|
||||
version = "0.2.2"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"anyhow",
|
||||
@@ -3416,7 +3416,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_linux"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_unix",
|
||||
@@ -3467,7 +3467,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_macos"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_macos",
|
||||
@@ -3514,7 +3514,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@@ -3525,7 +3525,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_platform"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"gpui",
|
||||
@@ -3538,7 +3538,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_shared_string"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -3548,7 +3548,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_util"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"log",
|
||||
@@ -3557,7 +3557,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_web"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"console_error_panic_hook",
|
||||
@@ -3581,7 +3581,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_wgpu"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytemuck",
|
||||
@@ -3610,7 +3610,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_windows"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_windows",
|
||||
@@ -3939,7 +3939,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "http_client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compression",
|
||||
@@ -3964,7 +3964,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "http_client_tls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"rustls-platform-verifier",
|
||||
@@ -4057,7 +4057,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core 0.62.2",
|
||||
"windows-core 0.58.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5092,7 +5092,7 @@ checksum = "7ebb8d8732c6a6df3d8f032a82911cfc747e00efb95cc46e8d0acd5b5b88570c"
|
||||
[[package]]
|
||||
name = "media"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bindgen",
|
||||
@@ -5202,7 +5202,7 @@ version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5258,7 +5258,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"png",
|
||||
"thiserror 2.0.20",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5481,7 +5481,7 @@ version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6204,7 +6204,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
[[package]]
|
||||
name = "perf"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"collections",
|
||||
"serde",
|
||||
@@ -6900,7 +6900,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7207,7 +7207,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "refineable"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"derive_refineable",
|
||||
]
|
||||
@@ -7250,7 +7250,7 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
|
||||
[[package]]
|
||||
name = "reqwest_client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -7611,7 +7611,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.12.1",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7784,7 +7784,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "scheduler"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"backtrace",
|
||||
@@ -8389,7 +8389,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8515,7 +8515,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"psm",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8627,7 +8627,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
[[package]]
|
||||
name = "sum_tree"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"heapless",
|
||||
"log",
|
||||
@@ -8917,7 +8917,7 @@ dependencies = [
|
||||
"getrandom 0.4.3",
|
||||
"once_cell",
|
||||
"rustix 1.1.4",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9384,7 +9384,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"png",
|
||||
"thiserror 2.0.20",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9896,7 +9896,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
|
||||
dependencies = [
|
||||
"memoffset 0.9.1",
|
||||
"tempfile",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10108,7 +10108,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
[[package]]
|
||||
name = "util"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-fs",
|
||||
@@ -10147,7 +10147,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "util_macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"perf",
|
||||
"quote",
|
||||
@@ -10740,7 +10740,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11491,7 +11491,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -12048,7 +12048,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "zlog"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -12093,7 +12093,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ztracing"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
@@ -12104,7 +12104,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ztracing_macro"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#d99a40a5f79a173465feec00363d480f75881a81"
|
||||
source = "git+https://github.com/l0ng-ai/zed?branch=tty7#ece710e365575bc08656c4a8d20488769b2de9a6"
|
||||
|
||||
[[package]]
|
||||
name = "zune-core"
|
||||
|
||||
+9
-1
@@ -367,7 +367,7 @@ lto = "thin"
|
||||
codegen-units = 1
|
||||
|
||||
# ---- gpui fork ------------------------------------------------------------
|
||||
# Our `tty7` branch (cut from the pinned upstream rev, three commits on top) carries:
|
||||
# Our `tty7` branch (cut from the pinned upstream rev) carries:
|
||||
#
|
||||
# 1. `prefers_ime_for_printable_keys` takes the keystroke, so an input handler can
|
||||
# answer per key instead of per view. tty7 needs it for Option-as-Meta — macOS
|
||||
@@ -386,6 +386,14 @@ codegen-units = 1
|
||||
# 3. resvg/usvg bumped 0.45 → 0.47 so gpui's SVG stack unifies with the resvg
|
||||
# tty7 pins directly above (follow-up to the #227 dependabot bump).
|
||||
#
|
||||
# 4. `ListState::with_size_hint`. A list counts an item it has not laid out yet
|
||||
# as zero tall, so a long document reports itself as about one viewport and
|
||||
# a scrollbar reading that height drags one viewport and stops — the diff
|
||||
# overlay scrolls a whole working tree through one of these. The hint counts
|
||||
# the unmeasured items at an estimate instead, which `measure_all` would
|
||||
# settle exactly at the price of laying out the whole document on the first
|
||||
# frame (see `ui::diff_overlay::DIFF_LINE_H`).
|
||||
#
|
||||
# Patching by source rather than editing the `gpui`/`gpui_platform` pins above is
|
||||
# deliberate: `gpui-component` declares its own `gpui` from the upstream URL, and
|
||||
# a plain pin swap would put two incompatible copies of gpui in the tree. `[patch]`
|
||||
|
||||
@@ -0,0 +1,484 @@
|
||||
//! Flattening a diff snapshot into the one-dimensional list of rows the
|
||||
//! overlay scrolls.
|
||||
//!
|
||||
//! The overlay used to build its whole patch as a nested element tree —
|
||||
//! a card per file, a hunk header and one element per line inside it, every
|
||||
//! one of them constructed on every frame. A `20_000` line budget is around
|
||||
//! `120_000` elements to lay out, and gpui notifies the view on each scroll
|
||||
//! wheel event, so a diff of any size rebuilt the entire tree tens of times a
|
||||
//! second.
|
||||
//!
|
||||
//! [`gpui::list`] only builds the rows it can see, but it is one-dimensional:
|
||||
//! it takes an index, not a tree. So the tree is flattened here, once per
|
||||
//! change to what is on screen.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::core::config::DiffViewMode;
|
||||
use crate::terminal::git_diff::{
|
||||
AUTO_COLLAPSE_LINES, DiffSnapshot, FileDiff, FileStatus, MAX_RENDERED_FILES, Truncation,
|
||||
};
|
||||
use crate::ui::diff_rows::{SplitRow, UnifiedRow, split_hunk, unified_rows};
|
||||
|
||||
/// Everything the file header row draws, lifted out of its [`FileDiff`].
|
||||
///
|
||||
/// A copy rather than a borrow: the row list outlives the frame that built it,
|
||||
/// and these are a handful of small fields against a file's whole patch.
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub(crate) struct FileHead {
|
||||
/// Position in `snap.files`, for the element id and nothing else.
|
||||
pub(crate) index: usize,
|
||||
pub(crate) path: String,
|
||||
/// `old → new` for a rename, the path itself otherwise.
|
||||
pub(crate) shown_path: String,
|
||||
pub(crate) status: FileStatus,
|
||||
pub(crate) added: u32,
|
||||
pub(crate) removed: u32,
|
||||
pub(crate) binary: bool,
|
||||
pub(crate) expandable: bool,
|
||||
pub(crate) expanded: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub(crate) enum DiffRow {
|
||||
/// The space that used to be the `gap_3` of a flex column.
|
||||
Gap,
|
||||
/// The banner above an auto-collapsed tree. Its text is derived from the
|
||||
/// snapshot at render time, so nothing is carried here.
|
||||
Oversized,
|
||||
FileHeader(FileHead),
|
||||
HunkHeader {
|
||||
text: String,
|
||||
/// The first hunk of a file follows its header and needs no rule
|
||||
/// above it; every later one is separating itself from the lines of
|
||||
/// the hunk before.
|
||||
leads: bool,
|
||||
},
|
||||
Split(SplitRow),
|
||||
Unified(UnifiedRow),
|
||||
Truncated(Truncation),
|
||||
MoreFiles {
|
||||
rest: usize,
|
||||
},
|
||||
UntrackedHeader {
|
||||
total: usize,
|
||||
},
|
||||
Untracked {
|
||||
index: usize,
|
||||
path: String,
|
||||
},
|
||||
MoreUntracked {
|
||||
rest: usize,
|
||||
},
|
||||
}
|
||||
|
||||
/// The rows of the whole overlay, in the order they scroll past.
|
||||
pub(crate) fn build_rows(
|
||||
snap: &DiffSnapshot,
|
||||
expanded: &HashMap<String, bool>,
|
||||
focused: Option<usize>,
|
||||
mode: DiffViewMode,
|
||||
oversized: bool,
|
||||
) -> Vec<DiffRow> {
|
||||
let mut out: Vec<DiffRow> = Vec::new();
|
||||
// Stands in for the gap between the groups this list used to be a flex
|
||||
// column of: gpui's list stacks its items with nothing between them.
|
||||
let gap = |out: &mut Vec<DiffRow>| {
|
||||
if !out.is_empty() {
|
||||
out.push(DiffRow::Gap);
|
||||
}
|
||||
};
|
||||
|
||||
if oversized {
|
||||
out.push(DiffRow::Oversized);
|
||||
}
|
||||
|
||||
let shown = snap.files.len().min(MAX_RENDERED_FILES);
|
||||
for (idx, file) in snap.files.iter().enumerate() {
|
||||
if focused.is_some_and(|f| f != idx) {
|
||||
continue;
|
||||
}
|
||||
if focused.is_none() && idx >= shown {
|
||||
break;
|
||||
}
|
||||
let is_expanded = if focused == Some(idx) {
|
||||
expanded.get(&file.path).copied().unwrap_or(true)
|
||||
} else {
|
||||
file_expanded(file, expanded, oversized)
|
||||
};
|
||||
gap(&mut out);
|
||||
out.extend(file_rows(idx, file, is_expanded, mode));
|
||||
}
|
||||
|
||||
if focused.is_none() && snap.files.len() > shown {
|
||||
gap(&mut out);
|
||||
out.push(DiffRow::MoreFiles {
|
||||
rest: snap.files.len() - shown,
|
||||
});
|
||||
}
|
||||
|
||||
if focused.is_none() && !snap.untracked.is_empty() {
|
||||
gap(&mut out);
|
||||
out.extend(untracked_rows(snap));
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
/// The single synthesized file an untracked file's preview shows.
|
||||
///
|
||||
/// `usize::MAX` keeps its element ids clear of the real list's.
|
||||
pub(crate) fn preview_rows(file: &FileDiff, mode: DiffViewMode) -> Vec<DiffRow> {
|
||||
file_rows(usize::MAX, file, true, mode)
|
||||
}
|
||||
|
||||
fn file_rows(index: usize, file: &FileDiff, expanded: bool, mode: DiffViewMode) -> Vec<DiffRow> {
|
||||
let expandable =
|
||||
!file.binary && (!file.hunks.is_empty() || file.truncated == Some(Truncation::Budget));
|
||||
let shown_path = match &file.old_path {
|
||||
Some(old) => format!("{old} → {}", file.path),
|
||||
None => file.path.clone(),
|
||||
};
|
||||
let mut rows = vec![DiffRow::FileHeader(FileHead {
|
||||
index,
|
||||
path: file.path.clone(),
|
||||
shown_path,
|
||||
status: file.status,
|
||||
added: file.added,
|
||||
removed: file.removed,
|
||||
binary: file.binary,
|
||||
expandable,
|
||||
expanded,
|
||||
})];
|
||||
|
||||
if expanded && (!file.hunks.is_empty() || file.truncated.is_some()) {
|
||||
for (h, hunk) in file.hunks.iter().enumerate() {
|
||||
rows.push(DiffRow::HunkHeader {
|
||||
text: hunk.header.clone(),
|
||||
leads: h == 0,
|
||||
});
|
||||
match mode {
|
||||
DiffViewMode::Split => {
|
||||
rows.extend(split_hunk(&hunk.lines).into_iter().map(DiffRow::Split))
|
||||
}
|
||||
DiffViewMode::Unified => {
|
||||
rows.extend(unified_rows(&hunk.lines).into_iter().map(DiffRow::Unified))
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(reason) = file.truncated {
|
||||
rows.push(DiffRow::Truncated(reason));
|
||||
}
|
||||
}
|
||||
|
||||
rows
|
||||
}
|
||||
|
||||
fn untracked_rows(snap: &DiffSnapshot) -> Vec<DiffRow> {
|
||||
let total = snap.untracked_count();
|
||||
let shown = &snap.untracked[..snap.untracked.len().min(MAX_RENDERED_FILES)];
|
||||
let mut rows = vec![DiffRow::UntrackedHeader { total }];
|
||||
for (index, path) in shown.iter().enumerate() {
|
||||
rows.push(DiffRow::Untracked {
|
||||
index,
|
||||
path: path.clone(),
|
||||
});
|
||||
}
|
||||
if total > shown.len() {
|
||||
rows.push(DiffRow::MoreUntracked {
|
||||
rest: total - shown.len(),
|
||||
});
|
||||
}
|
||||
rows
|
||||
}
|
||||
|
||||
/// Whether a file's lines show on their own, absent an explicit choice.
|
||||
pub(crate) fn file_expanded(
|
||||
file: &FileDiff,
|
||||
expanded: &HashMap<String, bool>,
|
||||
collapse_all: bool,
|
||||
) -> bool {
|
||||
if let Some(&want) = expanded.get(&file.path) {
|
||||
return want;
|
||||
}
|
||||
!collapse_all && file.added + file.removed <= AUTO_COLLAPSE_LINES
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::terminal::git_diff::{DiffLine, Hunk, LineKind};
|
||||
|
||||
fn line(kind: LineKind, old: Option<u32>, new: Option<u32>) -> DiffLine {
|
||||
DiffLine {
|
||||
kind,
|
||||
old_no: old,
|
||||
new_no: new,
|
||||
text: "x".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// One removal answered by one addition, wrapped in a context line either
|
||||
/// side — four lines unified, three rows split.
|
||||
fn hunk() -> Hunk {
|
||||
Hunk {
|
||||
header: "@@ -1,3 +1,3 @@".to_string(),
|
||||
lines: vec![
|
||||
line(LineKind::Context, Some(1), Some(1)),
|
||||
line(LineKind::Removed, Some(2), None),
|
||||
line(LineKind::Added, None, Some(2)),
|
||||
line(LineKind::Context, Some(3), Some(3)),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn file(path: &str, hunks: usize) -> FileDiff {
|
||||
FileDiff {
|
||||
path: path.to_string(),
|
||||
old_path: None,
|
||||
status: FileStatus::Modified,
|
||||
added: 1,
|
||||
removed: 1,
|
||||
binary: false,
|
||||
truncated: None,
|
||||
hunks: std::iter::repeat_n(hunk(), hunks).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn snapshot(files: Vec<FileDiff>) -> DiffSnapshot {
|
||||
DiffSnapshot {
|
||||
files,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn shape(rows: &[DiffRow]) -> Vec<&'static str> {
|
||||
rows.iter()
|
||||
.map(|row| match row {
|
||||
DiffRow::Gap => "gap",
|
||||
DiffRow::Oversized => "oversized",
|
||||
DiffRow::FileHeader(_) => "file",
|
||||
DiffRow::HunkHeader { .. } => "hunk",
|
||||
DiffRow::Split(_) => "split",
|
||||
DiffRow::Unified(_) => "unified",
|
||||
DiffRow::Truncated(_) => "truncated",
|
||||
DiffRow::MoreFiles { .. } => "more-files",
|
||||
DiffRow::UntrackedHeader { .. } => "untracked-header",
|
||||
DiffRow::Untracked { .. } => "untracked",
|
||||
DiffRow::MoreUntracked { .. } => "more-untracked",
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn open(paths: [&str; 1]) -> HashMap<String, bool> {
|
||||
paths.into_iter().map(|p| (p.to_string(), true)).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_open_file_flattens_to_a_header_a_hunk_header_and_a_row_per_line() {
|
||||
let snap = snapshot(vec![file("a.rs", 1)]);
|
||||
let rows = build_rows(&snap, &HashMap::new(), None, DiffViewMode::Unified, false);
|
||||
assert_eq!(
|
||||
shape(&rows),
|
||||
["file", "hunk", "unified", "unified", "unified", "unified"]
|
||||
);
|
||||
|
||||
let rows = build_rows(&snap, &HashMap::new(), None, DiffViewMode::Split, false);
|
||||
assert_eq!(
|
||||
shape(&rows),
|
||||
["file", "hunk", "split", "split", "split"],
|
||||
"the split view pairs the removal with the addition that replaced it"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_the_first_hunk_of_a_file_follows_its_header() {
|
||||
let rows = build_rows(
|
||||
&snapshot(vec![file("a.rs", 2)]),
|
||||
&HashMap::new(),
|
||||
None,
|
||||
DiffViewMode::Unified,
|
||||
false,
|
||||
);
|
||||
let leads: Vec<bool> = rows
|
||||
.iter()
|
||||
.filter_map(|r| match r {
|
||||
DiffRow::HunkHeader { leads, .. } => Some(*leads),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(
|
||||
leads,
|
||||
[true, false],
|
||||
"the second hunk draws the rule that separates it from the first"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_collapsed_file_is_a_single_row() {
|
||||
let snap = snapshot(vec![file("a.rs", 1)]);
|
||||
let shut: HashMap<String, bool> = [("a.rs".to_string(), false)].into_iter().collect();
|
||||
let rows = build_rows(&snap, &shut, None, DiffViewMode::Unified, false);
|
||||
assert_eq!(shape(&rows), ["file"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_truncation_note_lands_under_the_lines_it_is_about() {
|
||||
let mut f = file("a.rs", 1);
|
||||
f.truncated = Some(Truncation::Budget);
|
||||
let rows = build_rows(
|
||||
&snapshot(vec![f]),
|
||||
&HashMap::new(),
|
||||
None,
|
||||
DiffViewMode::Unified,
|
||||
false,
|
||||
);
|
||||
assert_eq!(*shape(&rows).last().unwrap(), "truncated");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn files_are_separated_by_a_gap_and_the_list_never_opens_with_one() {
|
||||
let snap = snapshot(vec![file("a.rs", 1), file("b.rs", 1)]);
|
||||
let shut: HashMap<String, bool> =
|
||||
snap.files.iter().map(|f| (f.path.clone(), false)).collect();
|
||||
let rows = build_rows(&snap, &shut, None, DiffViewMode::Unified, false);
|
||||
assert_eq!(shape(&rows), ["file", "gap", "file"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_oversized_banner_leads_and_collapses_what_was_not_asked_for() {
|
||||
let snap = snapshot(vec![file("a.rs", 1), file("b.rs", 1)]);
|
||||
let rows = build_rows(&snap, &open(["b.rs"]), None, DiffViewMode::Unified, true);
|
||||
assert_eq!(
|
||||
shape(&rows),
|
||||
[
|
||||
"oversized",
|
||||
"gap",
|
||||
"file",
|
||||
"gap",
|
||||
"file",
|
||||
"hunk",
|
||||
"unified",
|
||||
"unified",
|
||||
"unified",
|
||||
"unified"
|
||||
],
|
||||
"the file the reader opened stays open; the other one does not"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_focused_file_is_the_only_one_flattened() {
|
||||
let snap = snapshot(vec![file("a.rs", 1), file("b.rs", 1)]);
|
||||
let rows = build_rows(
|
||||
&snap,
|
||||
&HashMap::new(),
|
||||
Some(1),
|
||||
DiffViewMode::Unified,
|
||||
false,
|
||||
);
|
||||
assert_eq!(
|
||||
shape(&rows),
|
||||
["file", "hunk", "unified", "unified", "unified", "unified"]
|
||||
);
|
||||
let DiffRow::FileHeader(head) = &rows[0] else {
|
||||
panic!("the focused file leads");
|
||||
};
|
||||
assert_eq!(head.path, "b.rs");
|
||||
assert_eq!(head.index, 1, "the element id still points into `files`");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_file_list_is_capped_and_the_tail_gets_one_line() {
|
||||
let snap = snapshot(
|
||||
(0..MAX_RENDERED_FILES + 25)
|
||||
.map(|i| {
|
||||
let mut f = file(&format!("f{i}.rs"), 1);
|
||||
f.hunks.clear();
|
||||
f
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
let rows = build_rows(&snap, &HashMap::new(), None, DiffViewMode::Unified, false);
|
||||
let files = rows
|
||||
.iter()
|
||||
.filter(|r| matches!(r, DiffRow::FileHeader(_)))
|
||||
.count();
|
||||
assert_eq!(files, MAX_RENDERED_FILES);
|
||||
assert!(matches!(rows.last(), Some(DiffRow::MoreFiles { rest: 25 })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_untracked_list_is_capped_but_its_count_stays_true() {
|
||||
let mut snap = snapshot(Vec::new());
|
||||
snap.untracked = (0..MAX_RENDERED_FILES + 5)
|
||||
.map(|i| format!("p{i}"))
|
||||
.collect();
|
||||
snap.untracked_total = 9_000;
|
||||
let rows = build_rows(&snap, &HashMap::new(), None, DiffViewMode::Unified, false);
|
||||
|
||||
assert!(matches!(
|
||||
rows.first(),
|
||||
Some(DiffRow::UntrackedHeader { total: 9_000 })
|
||||
));
|
||||
let shown = rows
|
||||
.iter()
|
||||
.filter(|r| matches!(r, DiffRow::Untracked { .. }))
|
||||
.count();
|
||||
assert_eq!(shown, MAX_RENDERED_FILES);
|
||||
assert!(matches!(
|
||||
rows.last(),
|
||||
Some(DiffRow::MoreUntracked { rest }) if *rest == 9_000 - MAX_RENDERED_FILES
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_preview_is_one_open_file() {
|
||||
let rows = preview_rows(&file("new.md", 1), DiffViewMode::Unified);
|
||||
assert_eq!(
|
||||
shape(&rows),
|
||||
["file", "hunk", "unified", "unified", "unified", "unified"]
|
||||
);
|
||||
let DiffRow::FileHeader(head) = &rows[0] else {
|
||||
panic!("a file opens with its header");
|
||||
};
|
||||
assert_eq!(
|
||||
head.index,
|
||||
usize::MAX,
|
||||
"its element ids stay clear of the real list's"
|
||||
);
|
||||
assert!(head.expanded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_rename_shows_both_names_and_a_binary_file_cannot_be_opened() {
|
||||
let mut renamed = file("new.rs", 1);
|
||||
renamed.old_path = Some("old.rs".to_string());
|
||||
let rows = preview_rows(&renamed, DiffViewMode::Unified);
|
||||
let DiffRow::FileHeader(head) = &rows[0] else {
|
||||
unreachable!()
|
||||
};
|
||||
assert_eq!(head.shown_path, "old.rs → new.rs");
|
||||
assert_eq!(head.path, "new.rs", "the key is still the file itself");
|
||||
|
||||
let mut binary = file("logo.png", 0);
|
||||
binary.binary = true;
|
||||
let rows = preview_rows(&binary, DiffViewMode::Unified);
|
||||
let DiffRow::FileHeader(head) = &rows[0] else {
|
||||
unreachable!()
|
||||
};
|
||||
assert!(!head.expandable);
|
||||
}
|
||||
|
||||
/// The threshold that decides whether a file opens on its own, kept where
|
||||
/// the flattening that reads it lives.
|
||||
#[test]
|
||||
fn a_file_over_the_line_count_opens_only_when_asked() {
|
||||
let mut big = file("big.rs", 1);
|
||||
big.added = AUTO_COLLAPSE_LINES + 1;
|
||||
let none = HashMap::new();
|
||||
assert!(!file_expanded(&big, &none, false));
|
||||
assert!(file_expanded(&big, &open(["big.rs"]), true));
|
||||
assert!(file_expanded(&file("small.rs", 1), &none, false));
|
||||
}
|
||||
}
|
||||
+900
-580
File diff suppressed because it is too large
Load Diff
@@ -23,12 +23,14 @@ pub(crate) enum Side {
|
||||
New,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub(crate) struct SplitCell {
|
||||
pub(crate) no: Option<u32>,
|
||||
pub(crate) text: String,
|
||||
pub(crate) changed: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub(crate) struct SplitRow {
|
||||
pub(crate) left: Option<SplitCell>,
|
||||
pub(crate) right: Option<SplitCell>,
|
||||
@@ -85,6 +87,7 @@ pub(crate) fn split_hunk(lines: &[DiffLine]) -> Vec<SplitRow> {
|
||||
rows
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub(crate) struct UnifiedRow {
|
||||
pub(crate) old: Option<u32>,
|
||||
pub(crate) new: Option<u32>,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pub mod app;
|
||||
pub mod assets;
|
||||
pub mod code_editor;
|
||||
pub mod diff_list;
|
||||
pub mod diff_overlay;
|
||||
pub mod diff_rows;
|
||||
pub mod document_column;
|
||||
|
||||
@@ -40,24 +40,6 @@ pub(crate) fn segment_corners(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn stack_corners(
|
||||
i: usize,
|
||||
count: usize,
|
||||
outer: Pixels,
|
||||
border: Pixels,
|
||||
) -> Corners<Pixels> {
|
||||
let r = inner_radius(outer, border);
|
||||
let zero = px(0.);
|
||||
let first = i < count && i == 0;
|
||||
let last = i < count && i + 1 == count;
|
||||
Corners {
|
||||
top_left: if first { r } else { zero },
|
||||
top_right: if first { r } else { zero },
|
||||
bottom_left: if last { r } else { zero },
|
||||
bottom_right: if last { r } else { zero },
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -109,20 +91,4 @@ mod tests {
|
||||
Corners::all(px(0.))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_stack_caps_its_first_and_last_band() {
|
||||
let r = inner_radius(CARD_RADIUS, HAIRLINE);
|
||||
let zero = px(0.);
|
||||
|
||||
let top = stack_corners(0, 2, CARD_RADIUS, HAIRLINE);
|
||||
assert_eq!((top.top_left, top.top_right), (r, r));
|
||||
assert_eq!((top.bottom_left, top.bottom_right), (zero, zero));
|
||||
|
||||
let bottom = stack_corners(1, 2, CARD_RADIUS, HAIRLINE);
|
||||
assert_eq!((bottom.bottom_left, bottom.bottom_right), (r, r));
|
||||
assert_eq!((bottom.top_left, bottom.top_right), (zero, zero));
|
||||
|
||||
assert_eq!(stack_corners(0, 1, CARD_RADIUS, HAIRLINE), Corners::all(r));
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
use gpui::{AnyElement, ElementId, Pixels, ScrollHandle, div, prelude::*, px};
|
||||
use gpui_component::scroll::Scrollbar;
|
||||
use gpui_component::scroll::{Scrollbar, ScrollbarHandle};
|
||||
use gpui_component::v_flex;
|
||||
|
||||
/// Overlays the shared vertical scrollbar on a scroll area.
|
||||
@@ -9,10 +9,10 @@ use gpui_component::v_flex;
|
||||
/// dropping it straight into whatever is around it. Without that the wrapper
|
||||
/// sizes to its content, the `size_full` scroll area inside grows with it, and
|
||||
/// the pane stops scrolling because nothing overflows any more.
|
||||
pub(crate) fn with_vertical_scrollbar(
|
||||
pub(crate) fn with_vertical_scrollbar<H: ScrollbarHandle + Clone>(
|
||||
id: impl Into<ElementId>,
|
||||
scroll_area: impl IntoElement,
|
||||
handle: &ScrollHandle,
|
||||
handle: &H,
|
||||
) -> AnyElement {
|
||||
with_inset_vertical_scrollbar(id, scroll_area, handle, px(0.))
|
||||
}
|
||||
@@ -23,10 +23,10 @@ pub(crate) fn with_vertical_scrollbar(
|
||||
/// the border is already the edge. A scroll area that *is* the window has no
|
||||
/// such edge, and a bar that runs to the last pixel lands on the rounded
|
||||
/// corner and reads as if it had been clipped.
|
||||
pub(crate) fn with_inset_vertical_scrollbar(
|
||||
pub(crate) fn with_inset_vertical_scrollbar<H: ScrollbarHandle + Clone>(
|
||||
id: impl Into<ElementId>,
|
||||
scroll_area: impl IntoElement,
|
||||
handle: &ScrollHandle,
|
||||
handle: &H,
|
||||
inset_y: Pixels,
|
||||
) -> AnyElement {
|
||||
v_flex()
|
||||
|
||||
Reference in New Issue
Block a user