diff --git a/crates/story/src/stories/settings_story.rs b/crates/story/src/stories/settings_story.rs index 252605ee75..9c8ba4f08d 100644 --- a/crates/story/src/stories/settings_story.rs +++ b/crates/story/src/stories/settings_story.rs @@ -61,6 +61,7 @@ pub struct SettingsStory { focus_handle: FocusHandle, group_variant: GroupBoxVariant, size: Size, + last_page_index: usize, } struct OpenURLSettingField { @@ -121,6 +122,7 @@ impl SettingsStory { focus_handle: cx.focus_handle(), group_variant: GroupBoxVariant::Outline, size: Size::default(), + last_page_index: 0, } } @@ -455,9 +457,20 @@ impl Focusable for SettingsStory { impl Render for SettingsStory { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { + let entity = cx.entity(); Settings::new("app-settings") .with_size(self.size) .with_group_variant(self.group_variant) + .default_selected_index(gpui_component::setting::SelectIndex { + page_ix: self.last_page_index, + group_ix: None, + }) + .on_page_change(move |ix, cx| { + entity.update(cx, |this, cx| { + this.last_page_index = ix; + cx.notify(); + }); + }) .pages(self.setting_pages(window, cx)) } } diff --git a/crates/ui/src/setting/settings.rs b/crates/ui/src/setting/settings.rs index 6ec2496bfc..86eb4b9c05 100644 --- a/crates/ui/src/setting/settings.rs +++ b/crates/ui/src/setting/settings.rs @@ -11,6 +11,7 @@ use gpui::{ RenderOnce, StyleRefinement, Styled, Window, div, prelude::FluentBuilder as _, px, relative, }; use rust_i18n::t; +use std::rc::Rc; /// The settings structure containing multiple pages for app settings. /// @@ -34,6 +35,7 @@ pub struct Settings { sidebar_style: StyleRefinement, default_selected_index: SelectIndex, header_style: StyleRefinement, + on_page_change: Option>, } impl Settings { @@ -48,6 +50,7 @@ impl Settings { sidebar_style: StyleRefinement::default(), default_selected_index: SelectIndex::default(), header_style: StyleRefinement::default(), + on_page_change: None, } } @@ -95,6 +98,15 @@ impl Settings { self } + /// Set a callback invoked when the user selects a top-level page in the sidebar. + /// + /// The first argument is the zero-based page index. This is useful for persisting + /// the last-visited page so it can be restored via [`Self::default_selected_index`]. + pub fn on_page_change(mut self, f: impl Fn(usize, &mut App) + 'static) -> Self { + self.on_page_change = Some(Rc::new(f)); + self + } + fn filtered_pages(&self, query: &str, cx: &App) -> Vec { self.pages .iter() @@ -153,6 +165,7 @@ impl Settings { &self, state: &Entity, pages: &Vec, + on_page_change: Option>, _: &mut Window, cx: &mut App, ) -> impl IntoElement { @@ -182,6 +195,7 @@ impl Settings { .active(is_page_active) .on_click({ let state = state.clone(); + let on_page_change = on_page_change.clone(); move |_, _, cx| { state.update(cx, |state, cx| { state.selected_index = SelectIndex { @@ -189,7 +203,10 @@ impl Settings { ..Default::default() }; cx.notify(); - }) + }); + if let Some(f) = &on_page_change { + f(page_ix, cx); + } } }) .when(page.groups.len() > 1, |this| { @@ -288,7 +305,7 @@ impl RenderOnce for Settings { .child( resizable_panel() .size(self.sidebar_width) - .child(self.render_sidebar(&state, &filtered_pages, window, cx)), + .child(self.render_sidebar(&state, &filtered_pages, self.on_page_change.clone(), window, cx)), ) .child(resizable_panel().child(self.render_active_page( &state,