commit fe6aab40ad6c705d49851cd3bc325522a57cdc44
parent 46c4144ea4c4d645ff3528e544e98c1471eecab8
Author: triesap <tyson@radroots.org>
Date: Fri, 17 Apr 2026 18:33:45 +0000
ui: refactor the radroots_app settings shell posture
- move the native app menu onto the shared settings window opener for the utility-window shell
- replace the sidebar settings navigation with the approved segmented top-row navigation chrome
- keep the placeholder panel bodies mounted under the new settings shell without changing their content contract
- verify cargo check -p radroots_app from the mounted repo root
Diffstat:
2 files changed, 71 insertions(+), 117 deletions(-)
diff --git a/crates/launchers/desktop/src/menus.rs b/crates/launchers/desktop/src/menus.rs
@@ -1,21 +1,16 @@
-use gpui::{
- App, AppContext, Bounds, KeyBinding, Menu, MenuItem, SystemMenuType, WindowBounds,
- WindowOptions, actions, px, size,
-};
+use gpui::{App, KeyBinding, Menu, MenuItem, SystemMenuType, actions};
use radroots_app_i18n::{AppTextKey, app_text};
-use radroots_app_ui::APP_UI_THEME;
-use crate::window::{SettingsPanelViewKey, SettingsWindowView, settings_titlebar_options};
+use crate::window::{SettingsPanelViewKey, open_settings_window};
actions!(radroots_app, [OpenAboutWindow, QuitApp]);
pub fn install_native_app_menu(cx: &mut App) {
- cx.on_action(|_: &OpenAboutWindow, cx| open_settings_window(cx));
+ cx.on_action(|_: &OpenAboutWindow, cx| {
+ open_settings_window(cx, SettingsPanelViewKey::default());
+ });
cx.on_action(|_: &QuitApp, cx| cx.quit());
- cx.bind_keys([
- KeyBinding::new("cmd-,", OpenAboutWindow, None),
- KeyBinding::new("cmd-q", QuitApp, None),
- ]);
+ cx.bind_keys([KeyBinding::new("cmd-q", QuitApp, None)]);
let app_name = app_text(AppTextKey::AppName);
cx.set_menus(vec![Menu {
@@ -29,28 +24,3 @@ pub fn install_native_app_menu(cx: &mut App) {
],
}]);
}
-
-fn open_settings_window(cx: &mut App) {
- let bounds = Bounds::centered(
- None,
- size(
- px(APP_UI_THEME.windows.settings_width_px),
- px(APP_UI_THEME.windows.settings_height_px),
- ),
- cx,
- );
-
- cx.open_window(
- WindowOptions {
- window_bounds: Some(WindowBounds::Windowed(bounds)),
- window_min_size: Some(size(
- px(APP_UI_THEME.windows.settings_width_px),
- px(APP_UI_THEME.windows.settings_height_px),
- )),
- titlebar: Some(settings_titlebar_options()),
- ..Default::default()
- },
- |_, cx| cx.new(|_| SettingsWindowView::new(SettingsPanelViewKey::default())),
- )
- .expect("settings window should open");
-}
diff --git a/crates/launchers/desktop/src/window.rs b/crates/launchers/desktop/src/window.rs
@@ -1,13 +1,16 @@
use gpui::{
- AnyElement, Context, InteractiveElement, IntoElement, ParentElement, Render,
- StatefulInteractiveElement, Styled, Window, div, px, rgb,
+ AnyElement, App, AppContext, Bounds, Context, InteractiveElement, IntoElement, ParentElement,
+ Render, StatefulInteractiveElement, Styled, Window, WindowBounds, WindowOptions, div, px, rgb,
+ size,
};
+use gpui_component::IconName;
use radroots_app_core::AppRuntimeSnapshot;
use radroots_app_i18n::AppTextKey;
use radroots_app_ui::{
- APP_UI_THEME, LabelValueRow, app_card, app_shared_text, app_window_shell, label_value_list,
- runtime_metadata_rows, section_divider, settings_about_status_rows,
- settings_account_profile_rows, settings_preferences_general_rows, utility_title_row,
+ APP_UI_THEME, IconSegmentButtonSpec, LabelValueRow, app_card, app_shared_text,
+ app_window_shell, icon_segment_button, label_value_list, runtime_metadata_rows,
+ section_divider, settings_about_status_rows, settings_account_profile_rows,
+ settings_preferences_general_rows, utility_title_row,
};
pub fn home_titlebar_options() -> gpui::TitlebarOptions {
@@ -26,6 +29,31 @@ pub fn settings_titlebar_options() -> gpui::TitlebarOptions {
}
}
+pub fn open_settings_window(cx: &mut App, initial_view: SettingsPanelViewKey) {
+ let bounds = Bounds::centered(
+ None,
+ size(
+ px(APP_UI_THEME.windows.settings_width_px),
+ px(APP_UI_THEME.windows.settings_height_px),
+ ),
+ cx,
+ );
+
+ cx.open_window(
+ WindowOptions {
+ window_bounds: Some(WindowBounds::Windowed(bounds)),
+ window_min_size: Some(size(
+ px(APP_UI_THEME.windows.settings_width_px),
+ px(APP_UI_THEME.windows.settings_height_px),
+ )),
+ titlebar: Some(settings_titlebar_options()),
+ ..Default::default()
+ },
+ |_, cx| cx.new(|_| SettingsWindowView::new(initial_view)),
+ )
+ .expect("settings window should open");
+}
+
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum SettingsPanelViewKey {
#[default]
@@ -42,6 +70,14 @@ impl SettingsPanelViewKey {
Self::About => AppTextKey::SettingsNavAbout,
}
}
+
+ fn spec(self) -> (&'static str, IconName) {
+ match self {
+ Self::Account => ("settings-nav-accounts", IconName::CircleUser),
+ Self::Settings => ("settings-nav-settings", IconName::Settings2),
+ Self::About => ("settings-nav-about", IconName::Info),
+ }
+ }
}
pub struct HomeView {
@@ -133,36 +169,17 @@ impl SettingsWindowView {
view: SettingsPanelViewKey,
cx: &mut Context<Self>,
) -> impl IntoElement {
- let is_selected = self.selected_view == view;
- let background = if is_selected {
- APP_UI_THEME.surfaces.card_background
- } else {
- APP_UI_THEME.surfaces.panel_background
- };
- let foreground = if is_selected {
- APP_UI_THEME.text.primary
- } else {
- APP_UI_THEME.text.secondary
- };
-
- div()
- .w_full()
- .px(px(APP_UI_THEME.layout.settings_navigation_row_padding_px))
- .py(px(APP_UI_THEME.layout.settings_navigation_row_padding_px))
- .bg(rgb(background))
- .rounded(px(8.0))
- .cursor_pointer()
- .text_size(px(APP_UI_THEME.typography.body_text_px))
- .text_color(rgb(foreground))
- .child(app_shared_text(view.label_key()))
- .id(match view {
- SettingsPanelViewKey::Account => "settings-nav-accounts",
- SettingsPanelViewKey::Settings => "settings-nav-settings",
- SettingsPanelViewKey::About => "settings-nav-about",
- })
- .on_click(cx.listener(move |this, _, _, cx| {
- this.select_view(view, cx);
- }))
+ let (navigation_id, navigation_icon) = view.spec();
+ icon_segment_button(
+ IconSegmentButtonSpec::new(
+ navigation_id,
+ app_shared_text(view.label_key()),
+ navigation_icon,
+ ),
+ self.selected_view == view,
+ cx.listener(move |this, _, _, cx| this.select_view(view, cx)),
+ cx,
+ )
}
fn detail_card(&self, title: AppTextKey, rows: Vec<LabelValueRow>) -> impl IntoElement {
@@ -262,34 +279,21 @@ impl Render for SettingsWindowView {
.bg(rgb(APP_UI_THEME.surfaces.chrome_background))
.p(px(APP_UI_THEME.layout.settings_content_padding_px))
.flex()
- .items_center()
- .justify_between()
+ .flex_col()
+ .gap(px(APP_UI_THEME.layout.settings_section_gap_px))
+ .child(utility_title_row(app_shared_text(
+ AppTextKey::SettingsTitle,
+ )))
.child(
div()
+ .w_full()
.flex()
- .flex_col()
- .gap(px(APP_UI_THEME.layout.settings_section_gap_px))
- .child(
- div()
- .text_size(px(APP_UI_THEME.typography.brand_text_px))
- .font_weight(gpui::FontWeight::SEMIBOLD)
- .text_color(rgb(APP_UI_THEME.text.primary))
- .child(app_shared_text(AppTextKey::SettingsTitle)),
- )
- .child(
- div()
- .text_size(px(APP_UI_THEME
- .typography
- .utility_title_text_px))
- .text_color(rgb(APP_UI_THEME.text.secondary))
- .child(app_shared_text(self.selected_view.label_key())),
- ),
- )
- .child(
- div()
- .text_size(px(APP_UI_THEME.typography.utility_title_text_px))
- .text_color(rgb(APP_UI_THEME.text.secondary))
- .child(app_shared_text(AppTextKey::AppName)),
+ .justify_center()
+ .items_center()
+ .gap(px(APP_UI_THEME.layout.settings_navigation_row_gap_px))
+ .child(self.navigation_button(SettingsPanelViewKey::Account, cx))
+ .child(self.navigation_button(SettingsPanelViewKey::Settings, cx))
+ .child(self.navigation_button(SettingsPanelViewKey::About, cx)),
),
)
.child(section_divider())
@@ -297,27 +301,7 @@ impl Render for SettingsWindowView {
div()
.flex_1()
.overflow_hidden()
- .flex()
- .child(
- div()
- .h_full()
- .w(px(APP_UI_THEME.layout.settings_navigation_width_px))
- .p(px(APP_UI_THEME.layout.settings_content_padding_px))
- .flex()
- .flex_col()
- .gap(px(APP_UI_THEME.layout.settings_navigation_row_gap_px))
- .bg(rgb(APP_UI_THEME.surfaces.panel_background))
- .child(self.navigation_button(SettingsPanelViewKey::Account, cx))
- .child(self.navigation_button(SettingsPanelViewKey::Settings, cx))
- .child(self.navigation_button(SettingsPanelViewKey::About, cx)),
- )
- .child(
- div()
- .h_full()
- .w(px(APP_UI_THEME.layout.divider_thickness_px))
- .bg(rgb(APP_UI_THEME.surfaces.divider)),
- )
- .child(div().flex_1().h_full().child(self.settings_panel_content())),
+ .child(self.settings_panel_content()),
),
)
}