bos-settings: single-source-of-truth default page

sidebar.rs and window.rs each independently hardcoded "about" as the
default selected/visible page — harmless while both literals happened to
match, but a real latent bug: changing one without the other silently
desyncs the sidebar highlight from the actually-displayed panel. Found
while capturing screenshots of every panel for a design review, where
exactly that happened. window.rs now owns DEFAULT_PAGE and passes it to
sidebar::build().
This commit is contained in:
Breadway 2026-07-04 00:00:10 +08:00
parent eb740a3724
commit 9c2c95089c
2 changed files with 15 additions and 7 deletions

View file

@ -55,7 +55,12 @@ pub const MAINTENANCE_ITEMS: &[SidebarItem] = &[
pub const ABOUT_ITEMS: &[SidebarItem] = &[item("about", "About", "help-about-symbolic")]; pub const ABOUT_ITEMS: &[SidebarItem] = &[item("about", "About", "help-about-symbolic")];
pub fn build() -> (GBox, ListBox) { /// `default_id` must match whatever page `window.rs` sets as the `Stack`'s
/// initial visible child — previously these were two independent hardcoded
/// "about" literals in different files with no link between them, so
/// changing one without the other silently desynced the sidebar highlight
/// from the actually-displayed page.
pub fn build(default_id: &str) -> (GBox, ListBox) {
let vbox = GBox::new(Orientation::Vertical, 0); let vbox = GBox::new(Orientation::Vertical, 0);
vbox.add_css_class("sidebar"); vbox.add_css_class("sidebar");
vbox.set_width_request(210); vbox.set_width_request(210);
@ -69,12 +74,11 @@ pub fn build() -> (GBox, ListBox) {
append_section(&list, "Maintenance", MAINTENANCE_ITEMS); append_section(&list, "Maintenance", MAINTENANCE_ITEMS);
append_section(&list, None, ABOUT_ITEMS); append_section(&list, None, ABOUT_ITEMS);
// Select About so it matches the default stack page.
let mut i = 0; let mut i = 0;
loop { loop {
match list.row_at_index(i) { match list.row_at_index(i) {
None => break, None => break,
Some(row) if row.widget_name() == "about" => { Some(row) if row.widget_name() == default_id => {
list.select_row(Some(&row)); list.select_row(Some(&row));
break; break;
} }

View file

@ -4,6 +4,12 @@ use gtk4::{Application, ApplicationWindow, Orientation, Paned, Stack};
use super::sidebar; use super::sidebar;
use super::views; use super::views;
// About is a settings app's most GNOME-Settings-like landing page —
// identifies the machine, no config editor thrown at you first. Defined
// once here and threaded through to both the `Stack` and the sidebar's
// initial selection, so the two can't independently drift out of sync.
const DEFAULT_PAGE: &str = "about";
pub fn build_ui(app: &Application) { pub fn build_ui(app: &Application) {
let window = ApplicationWindow::builder() let window = ApplicationWindow::builder()
.application(app) .application(app)
@ -19,7 +25,7 @@ pub fn build_ui(app: &Application) {
hpaned.set_shrink_start_child(false); hpaned.set_shrink_start_child(false);
hpaned.set_resize_start_child(false); hpaned.set_resize_start_child(false);
let (sidebar_box, list) = sidebar::build(); let (sidebar_box, list) = sidebar::build(DEFAULT_PAGE);
let stack = Stack::new(); let stack = Stack::new();
stack.set_hexpand(true); stack.set_hexpand(true);
@ -41,9 +47,7 @@ pub fn build_ui(app: &Application) {
stack.add_named(&views::breadsearch::build(), Some("breadsearch")); stack.add_named(&views::breadsearch::build(), Some("breadsearch"));
stack.add_named(&views::hyprland::build(), Some("hyprland")); stack.add_named(&views::hyprland::build(), Some("hyprland"));
// Default to About — a settings app's most GNOME-Settings-like landing stack.set_visible_child_name(DEFAULT_PAGE);
// page: identifies the machine, no config editor thrown at you first.
stack.set_visible_child_name("about");
{ {
let stack = stack.clone(); let stack = stack.clone();