From 3a68d1bb29201d0a460dd5e793c0b680f9a72cac 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, 18 Oct 2026 21:52:16 -0400 Subject: [PATCH] Navigation: the chosen tab follows a strip that changes width 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 --- src/navigation/tabs.rs | 21 +++++++++------------ src/navigation/tests/tabs.rs | 25 ++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/navigation/tabs.rs b/src/navigation/tabs.rs index 44a2444..e1b78f0 100644 --- a/src/navigation/tabs.rs +++ b/src/navigation/tabs.rs @@ -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::, - ); - 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(); diff --git a/src/navigation/tests/tabs.rs b/src/navigation/tests/tabs.rs index 325ef58..91f686b 100644 --- a/src/navigation/tests/tabs.rs +++ b/src/navigation/tests/tabs.rs @@ -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, + 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"); +}