Navigation: the chosen tab follows a strip that changes width
CI / platforms (ubuntu-24.04) (push) Waiting to run
CI / site (push) Successful in 35s
CI / platforms (windows-latest) (push) Successful in 5m7s
CI / crate (push) Failing after 11m12s

The strip scrolled its chosen tab in once, at its first width; a
panel opening beside it then narrowed it and pushed the tab out
of view (ElyTable on a 30-sheet workbook). The strip now follows
its chosen tab whenever the choice or its width changes, checked
at paint with the width just laid out; a manual scroll stays
until either changes.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude committed 2026-10-18 21:52:16 -04:00
1 parent e56a34301d
commit 3a68d1bb29
2 files changed
+33 -13

No files matched your search

+9 -12
View File
@@ -108,20 +108,16 @@ impl RenderOnce for Tabs {
window,
cx,
);
// A wide strip scrolls; the chosen tab scrolls in once.
// The chosen tab scrolls in per choice and width.
let scroll = window
.use_keyed_state((self.id.clone(), "scroll"), cx, |_, _| ScrollHandle::new())
.read(cx)
.clone();
let followed =
window.use_keyed_state(
(self.id.clone(), "followed"),
cx,
|_, _| None::<SharedString>,
);
let follow = (axis == Axis::Horizontal
&& followed.read(cx).as_ref() != Some(&self.selected))
.then(|| (followed, self.selected.clone(), scroll.clone()));
let followed = window.use_keyed_state((self.id.clone(), "followed"), cx, |_, _| {
None::<(SharedString, Pixels)>
});
let follow =
(axis == Axis::Horizontal).then(|| (followed, self.selected.clone(), scroll.clone()));
let focus = tab_stop((self.id.clone(), "focus").into(), true, window, cx);
let focused = focus.is_focused(window);
let tabs = Rc::new(self.tabs);
@@ -295,10 +291,11 @@ impl RenderOnce for Tabs {
.children(follow.map(|(followed, selected, scroll)| {
canvas(
move |_, window, cx| {
if scroll.bounds().size.width <= Pixels::ZERO {
let now = (selected, scroll.bounds().size.width);
if now.1 <= Pixels::ZERO || followed.read(cx).as_ref() == Some(&now) {
return;
}
followed.update(cx, |followed, _| *followed = Some(selected));
followed.update(cx, |followed, _| *followed = Some(now));
if let Some(ix) = chosen {
scroll.scroll_to_item(ix);
window.request_animation_frame();
+24 -1
View File
@@ -184,6 +184,7 @@ fn tabs_whose_choice_closed_mark_none_and_arrows_start_at_the_ends(cx: &mut Test
struct Sheets {
chosen: SharedString,
clicked: Option<SharedString>,
width: f32,
}
impl Render for Sheets {
@@ -191,7 +192,7 @@ impl Render for Sheets {
let view = cx.entity();
let tabs = (0..30).map(|ix| Choice::new(format!("s{ix}"), format!("Sheet {ix}")));
div()
.w(px(300.0))
.w(px(self.width))
.child(
Tabs::new("sheets", tabs, self.chosen.clone()).on_change(move |value, _, cx| {
view.update(cx, |view, _| view.clicked = Some(value.clone()))
@@ -216,6 +217,7 @@ fn a_wide_strip_scrolls_its_chosen_tab_in_and_the_wheel_reaches_the_rest(cx: &mu
let (view, cx) = cx.add_window_view(|_, _| Sheets {
chosen: "s29".into(),
clicked: None,
width: 300.0,
});
settle(cx);
assert!(
@@ -242,3 +244,24 @@ fn a_wide_strip_scrolls_its_chosen_tab_in_and_the_wheel_reaches_the_rest(cx: &mu
settle(cx);
assert!(picked_at(10.0, &view, cx) > 15, "a new choice scrolls in");
}
#[gpui::test]
fn a_strip_that_narrows_keeps_its_chosen_tab_in_view(cx: &mut TestAppContext) {
setup(cx);
let (view, cx) = cx.add_window_view(|_, _| Sheets {
chosen: "s29".into(),
clicked: None,
width: 700.0,
});
settle(cx);
view.update(cx, |view, cx| {
view.width = 250.0;
cx.notify();
});
settle(cx);
assert!(picked_at(10.0, &view, cx) > 25, "the strip shows its end");
view.update(cx, |view, _| view.clicked = None);
click_at(245.0, cx);
let clicked = view.read_with(cx, |view, _| view.clicked.clone());
assert_eq!(clicked, None, "the chosen tab ends the strip");
}