From d49e02ceeea35e5f0ececfddc2dd45a9a7287fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sun, 27 Sep 2026 16:58:23 -0400 Subject: [PATCH] =?UTF-8?q?Theme=20=C2=B7=20Editing:=20segments=20sized=20?= =?UTF-8?q?by=20their=20labels,=20a=20thumb=20that=20covers=20its=20segmen?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A segmented control's segments now take their labels' widths first and share the spare room, so a label ends in an ellipsis only when the whole strip lacks room. The thumb's insets meet the strip's padding again, and each segment is measured from its borderless wrapper, so the thumb covers the chosen segment exactly. Tests hold Comfortable whole at 280px and the thumb to the chosen segment's bounds. Co-Authored-By: Claude Opus 5.5 --- TASKS.md | 1 + src/buttons/segmented.rs | 22 ++++++++++++++-------- src/settings/tests/theme.rs | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/TASKS.md b/TASKS.md index 59c1dd1..0728901 100644 --- a/TASKS.md +++ b/TASKS.md @@ -420,3 +420,4 @@ Per-component lines live in `tasks/`. Tags there: | T38a | 2 | PASS | | | T38b | 1 | PASS | | | T39a | 1 | FAIL | a larger text size cut the density strip at 280px; ten palette colors had no name; the density and sliders untested | +| T39a | 2 | FAIL | segments split the strip equally and cut a longer label; the thumb lost its padding's height | diff --git a/src/buttons/segmented.rs b/src/buttons/segmented.rs index 2e59da1..7ee2d4d 100644 --- a/src/buttons/segmented.rs +++ b/src/buttons/segmented.rs @@ -110,8 +110,9 @@ impl RenderOnce for SegmentedControl { }| { div() .absolute() - .top_0p5() - .bottom_0p5() + .top_0() + .bottom_0() + .debug_selector(|| "segment-thumb".into()) .rounded(theme.radius(Radius::Md)) .bg(colors.surface) .border_1() @@ -135,13 +136,13 @@ impl RenderOnce for SegmentedControl { let on = ix == chosen; let fg = if on { colors.fg } else { colors.fg_muted }; let (focus, reveal) = stops.remove(0); - let named = value.clone(); + let (named, labeled) = (value.clone(), value.clone()); let (change, measure) = (self.on_change.clone(), state.clone()); let segment = div() .id(("segment", ix)) .relative() .flex() - .flex_1() + .flex_auto() .min_w_0() .items_center() .justify_center() @@ -176,15 +177,20 @@ impl RenderOnce for SegmentedControl { segment.child(Icon::new(icon).size(icon_size).color(fg)) }) .when(!label.is_empty(), |segment| { - segment.child(div().min_w_0().child(Ellipsis::new(label))) - }) - .child(measure_item(measure, ix, Axis::Horizontal)); + segment.child( + div() + .min_w_0() + .debug_selector(move || format!("segment-label {labeled}")) + .child(Ellipsis::new(label)), + ) + }); div() .relative() .flex() - .flex_1() + .flex_auto() .min_w_0() .child(segment) + .child(measure_item(measure, ix, Axis::Horizontal)) .child(reveal) }); let control = div() diff --git a/src/settings/tests/theme.rs b/src/settings/tests/theme.rs index 4c12ef2..8b50931 100644 --- a/src/settings/tests/theme.rs +++ b/src/settings/tests/theme.rs @@ -118,3 +118,36 @@ fn the_density_strip_fits_a_narrow_box_with_larger_text(cx: &mut TestAppContext) .expect("the last segment"); assert!(last.right() <= frame.right(), "{last:?} inside {frame:?}"); } + +/// At 280px and the base size the strip has room, so each label keeps its whole width: eleven letters to seven. +#[gpui::test] +fn a_label_gives_way_only_when_the_strip_lacks_room(cx: &mut TestAppContext) { + let (_, cx) = desk(narrow, cx); + settle(cx); + let compact = cx + .debug_bounds("segment-label Compact") + .expect("Compact's label"); + let comfortable = cx + .debug_bounds("segment-label Comfortable") + .expect("Comfortable's label"); + let ratio = comfortable.size.width / compact.size.width; + assert!( + (ratio - 11.0 / 7.0).abs() < 0.01, + "{comfortable:?} to {compact:?}" + ); +} + +/// The thumb covers the chosen segment, top to bottom and side to side. +#[gpui::test] +fn the_thumb_covers_the_chosen_segment(cx: &mut TestAppContext) { + let (_, cx) = desk(editor, cx); + for _ in 0..3 { + std::thread::sleep(std::time::Duration::from_millis(2)); + settle(cx); + } + let thumb = cx.debug_bounds("segment-thumb").expect("the thumb"); + let chosen = cx + .debug_bounds("segment Standard") + .expect("the chosen segment"); + assert_eq!(thumb, chosen); +}