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 <noreply@anthropic.com>
This commit is contained in:
2 files changed
+33
-13
No files matched your search
+9
-12
@@ -108,20 +108,16 @@ impl RenderOnce for Tabs {
|
|||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
// A wide strip scrolls; the chosen tab scrolls in once.
|
// The chosen tab scrolls in per choice and width.
|
||||||
let scroll = window
|
let scroll = window
|
||||||
.use_keyed_state((self.id.clone(), "scroll"), cx, |_, _| ScrollHandle::new())
|
.use_keyed_state((self.id.clone(), "scroll"), cx, |_, _| ScrollHandle::new())
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.clone();
|
.clone();
|
||||||
let followed =
|
let followed = window.use_keyed_state((self.id.clone(), "followed"), cx, |_, _| {
|
||||||
window.use_keyed_state(
|
None::<(SharedString, Pixels)>
|
||||||
(self.id.clone(), "followed"),
|
});
|
||||||
cx,
|
let follow =
|
||||||
|_, _| None::<SharedString>,
|
(axis == Axis::Horizontal).then(|| (followed, self.selected.clone(), scroll.clone()));
|
||||||
);
|
|
||||||
let follow = (axis == Axis::Horizontal
|
|
||||||
&& followed.read(cx).as_ref() != Some(&self.selected))
|
|
||||||
.then(|| (followed, self.selected.clone(), scroll.clone()));
|
|
||||||
let focus = tab_stop((self.id.clone(), "focus").into(), true, window, cx);
|
let focus = tab_stop((self.id.clone(), "focus").into(), true, window, cx);
|
||||||
let focused = focus.is_focused(window);
|
let focused = focus.is_focused(window);
|
||||||
let tabs = Rc::new(self.tabs);
|
let tabs = Rc::new(self.tabs);
|
||||||
@@ -295,10 +291,11 @@ impl RenderOnce for Tabs {
|
|||||||
.children(follow.map(|(followed, selected, scroll)| {
|
.children(follow.map(|(followed, selected, scroll)| {
|
||||||
canvas(
|
canvas(
|
||||||
move |_, window, cx| {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
followed.update(cx, |followed, _| *followed = Some(selected));
|
followed.update(cx, |followed, _| *followed = Some(now));
|
||||||
if let Some(ix) = chosen {
|
if let Some(ix) = chosen {
|
||||||
scroll.scroll_to_item(ix);
|
scroll.scroll_to_item(ix);
|
||||||
window.request_animation_frame();
|
window.request_animation_frame();
|
||||||
|
|||||||
@@ -184,6 +184,7 @@ fn tabs_whose_choice_closed_mark_none_and_arrows_start_at_the_ends(cx: &mut Test
|
|||||||
struct Sheets {
|
struct Sheets {
|
||||||
chosen: SharedString,
|
chosen: SharedString,
|
||||||
clicked: Option<SharedString>,
|
clicked: Option<SharedString>,
|
||||||
|
width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for Sheets {
|
impl Render for Sheets {
|
||||||
@@ -191,7 +192,7 @@ impl Render for Sheets {
|
|||||||
let view = cx.entity();
|
let view = cx.entity();
|
||||||
let tabs = (0..30).map(|ix| Choice::new(format!("s{ix}"), format!("Sheet {ix}")));
|
let tabs = (0..30).map(|ix| Choice::new(format!("s{ix}"), format!("Sheet {ix}")));
|
||||||
div()
|
div()
|
||||||
.w(px(300.0))
|
.w(px(self.width))
|
||||||
.child(
|
.child(
|
||||||
Tabs::new("sheets", tabs, self.chosen.clone()).on_change(move |value, _, cx| {
|
Tabs::new("sheets", tabs, self.chosen.clone()).on_change(move |value, _, cx| {
|
||||||
view.update(cx, |view, _| view.clicked = Some(value.clone()))
|
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 {
|
let (view, cx) = cx.add_window_view(|_, _| Sheets {
|
||||||
chosen: "s29".into(),
|
chosen: "s29".into(),
|
||||||
clicked: None,
|
clicked: None,
|
||||||
|
width: 300.0,
|
||||||
});
|
});
|
||||||
settle(cx);
|
settle(cx);
|
||||||
assert!(
|
assert!(
|
||||||
@@ -242,3 +244,24 @@ fn a_wide_strip_scrolls_its_chosen_tab_in_and_the_wheel_reaches_the_rest(cx: &mu
|
|||||||
settle(cx);
|
settle(cx);
|
||||||
assert!(picked_at(10.0, &view, cx) > 15, "a new choice scrolls in");
|
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");
|
||||||
|
}
|
||||||
Reference in new issue
Block a user