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"); +}