Lists · tree guides fall from the parent's chevron and run unbroken across rows (#2)

The chevron takes its indent as a margin, so no gap parts it from its guide column; the cursor's ring is an overlay, so the guides fill the row through its border.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-03 01:21:26 -04:00
1 parent a6119f1e4e
commit 5ff7c219ef
4 files changed
+45 -11

No files matched your search

+17 -11
View File
@@ -48,14 +48,14 @@ pub(super) fn draw(
}
/// A faint line down each level a row sits under.
fn guides(depth: usize, cx: &App) -> impl IntoElement {
fn guides(name: &str, depth: usize, cx: &App) -> impl IntoElement {
let theme = cx.theme();
div()
.absolute()
.inset_0()
.px_2()
.flex()
.children((0..depth).map(|_| {
.children((0..depth).map(|level| {
div()
.flex_none()
.w(theme.tree_indent())
@@ -64,6 +64,7 @@ fn guides(depth: usize, cx: &App) -> impl IntoElement {
.justify_center()
.child(
div()
.debug_selector(|| format!("tree-guide-{name}-{level}"))
.h_full()
.border_l_1()
.border_color(theme.colors.border),
@@ -121,8 +122,10 @@ fn row(rows: &Rows, ix: usize, window: &mut Window, cx: &mut App) -> AnyElement
);
let (dropped_on, dragged_over, shut) = (key.clone(), key.clone(), key.clone());
let chevron = div()
.debug_selector(|| format!("tree-chevron-{key}"))
.flex_none()
.w(indent)
.ml(indent * item.depth as f32)
.flex()
.justify_center()
.when(opens, |slot| {
@@ -176,12 +179,6 @@ fn row(rows: &Rows, ix: usize, window: &mut Window, cx: &mut App) -> AnyElement
.gap_1()
.px_2()
.rounded(theme.radius(Radius::Sm))
.border_1()
.border_color(if rows.current == Some(ix) {
colors.focus
} else {
gpui::transparent_black()
})
.text_size(theme.text_size(TextSize::Sm))
.text_color(colors.fg)
.when(selected, |row| row.bg(colors.active))
@@ -189,8 +186,7 @@ fn row(rows: &Rows, ix: usize, window: &mut Window, cx: &mut App) -> AnyElement
.when(landing == Some(DropAt::Inside), |row| {
row.bg(colors.selection)
})
.child(guides(item.depth, cx))
.child(div().flex_none().w(indent * item.depth as f32))
.child(guides(&key, item.depth, cx))
.child(chevron)
.children(
checked
@@ -218,6 +214,16 @@ fn row(rows: &Rows, ix: usize, window: &mut Window, cx: &mut App) -> AnyElement
.child(Ellipsis::new(note))
}))
.children(landing.filter(|at| *at != DropAt::Inside).map(line))
.when(rows.current == Some(ix), |row| {
row.child(
div()
.absolute()
.inset_0()
.rounded(theme.radius(Radius::Sm))
.border_1()
.border_color(colors.focus),
)
})
.on_click(move |event, window, cx| {
window.focus(&focus, cx);
if event.click_count() == 2 {
@@ -289,7 +295,7 @@ fn waiting(rows: &Rows, ix: usize, depth: usize, cx: &App) -> AnyElement {
.flex()
.items_center()
.px_2()
.child(guides(depth, cx))
.child(guides(&format!("waiting-{ix}"), depth, cx))
.child(
div()
.flex_none()
+20
View File
@@ -220,3 +220,23 @@ fn a_drop_in_a_folders_middle_moves_the_node_inside(cx: &mut TestAppContext) {
[format!("move readme {:?} ui", DropAt::Inside)]
);
}
#[gpui::test]
fn guides_run_unbroken_under_the_parents_chevron(cx: &mut TestAppContext) {
let (_, cx) = explorer(false, cx);
let bounds = |name: &'static str, cx: &mut VisualTestContext| {
cx.debug_bounds(name).unwrap_or_else(|| panic!("no {name}"))
};
let chevron = bounds("tree-chevron-src", cx);
let (lib, ui) = (
bounds("tree-guide-lib-0", cx),
bounds("tree-guide-ui-0", cx),
);
assert_eq!(
lib.center().x,
chevron.center().x,
"the guide falls from the chevron's middle"
);
assert_eq!(lib.bottom(), ui.top(), "one row's guide meets the next");
assert_eq!(lib.size.height, px(28.0), "a guide takes its row's height");
}
+6
View File
@@ -0,0 +1,6 @@
# GitHub round, 2026-10-03
- [x] G1 PR #1: build on Windows with `escape_args`; review, verify, merge, answer CodeRabbit
- [x] G2 Issue #2: tree guides line up under the chevron and run unbroken across rows
- [ ] G3 Issue #3: Checkbox, Switch, Radio, Slider and Select name their role, label and state
- [ ] G4 Push, comment on #2 and #3, close #2
+2
View File
@@ -347,3 +347,5 @@
| T48m | 1 | FAIL | the every-guide test never checked the title |
| T48m | 2 | FAIL | the in-place link test used a hash link, which survives without preventDefault |
| T48m | 3 | PASS | |
| G1 | 1 | PASS | PR #1 merged; CodeRabbit's opt-out declined: Launch is argv |
| G2 | 1 | PASS | |