bos-settings 0.5.0: GNOME-Settings-style redesign + live app control

New panels: About, Network (Wi-Fi/Ethernet via nmcli), Sound (PipeWire
volume/device via pactl), Date & Time (timedatectl), and Clipboard
(breadclip previously had no bos-settings presence at all despite running
its own daemon).

Layout: every panel now shares one scaffold (6 views were hand-rolling their
own, inconsistently) with a centered, width-capped content column instead of
rows stretching edge-to-edge on a wide window with the control stranded far
from its label. Rows render as individual cards. Sidebar got human labels +
icons + task-based grouping (System/Personalization/Maintenance/About)
instead of raw binary names under "Apps"/"System".

New capability: a shared service_control() widget gives every bread app
backed by a systemd --user daemon (breadd, breadbox-sync, breadcrumbs,
breadmill, breadclipd) live status plus Start/Stop/Restart/View logs —
previously these panels only ever edited a TOML file and hoped the running
process picked it up. breadbar (no systemd unit) gets an equivalent: Save
now actually sends the SIGHUP its own reload mechanism needs, instead of
just claiming to in the hint text.

Fixed two real bugs surfaced while building About: CPU model string kept a
stray leading tab+colon (trim_start_matches was missing '\t'), and GPU
showed "unknown" on hardware whose lspci entry says "Display controller"
instead of "VGA compatible controller".

Local CSS patches (bos-settings/src/theme.rs) for two upstream bread-theme
gaps: suggested/destructive buttons and scale widgets don't clear Adwaita's
default background-image, so flat colour overrides were invisible; and
destructive-action red must not be pywal-derived (it can land on gold
depending on the wallpaper, indistinguishable from a primary action).
This commit is contained in:
Breadway 2026-07-03 23:50:26 +08:00
parent f43bfbb680
commit eb740a3724
22 changed files with 1310 additions and 250 deletions

View file

@ -15,6 +15,7 @@ use gtk4::{
use toml_edit::{value, Array, ArrayOfTables, DocumentMut, Item, Table};
use crate::config;
use crate::ui::widgets as w;
#[derive(Clone, Default)]
struct Context {
@ -62,6 +63,17 @@ fn rebuild_list(list: &ListBox, model: &Rc<RefCell<Vec<Context>>>) {
while let Some(child) = list.first_child() {
list.remove(&child);
}
if model.borrow().is_empty() {
let row = ListBoxRow::new();
row.set_selectable(false);
row.set_child(Some(&w::empty_state(
"view-app-grid-symbolic",
"No launcher contexts yet",
"Add one to control which apps/categories breadbox surfaces first.",
)));
list.append(&row);
return;
}
for (i, ctx) in model.borrow().iter().enumerate() {
let row = ListBoxRow::new();
row.set_selectable(false);
@ -128,21 +140,13 @@ pub fn build() -> GBox {
let doc = Rc::new(RefCell::new(config::load_doc(&path)));
let model = Rc::new(RefCell::new(read_contexts(&doc.borrow())));
let vbox = GBox::new(Orientation::Vertical, 12);
vbox.add_css_class("view-content");
let (outer, content) = w::view_scaffold("Launcher");
content.append(&w::service_control("breadbox-sync.service"));
let title = Label::new(Some("breadbox"));
title.add_css_class("title");
title.set_xalign(0.0);
vbox.append(&title);
let subtitle = Label::new(Some(
content.append(&w::section("Contexts"));
content.append(&w::hint(
"Launcher contexts — each lists, in priority order, the apps/categories surfaced first.",
));
subtitle.set_xalign(0.0);
subtitle.set_wrap(true);
subtitle.set_margin_bottom(8);
vbox.append(&subtitle);
let list = ListBox::new();
list.set_selection_mode(gtk4::SelectionMode::None);
@ -151,7 +155,7 @@ pub fn build() -> GBox {
let scroll = ScrolledWindow::new();
scroll.set_vexpand(true);
scroll.set_child(Some(&list));
vbox.append(&scroll);
content.append(&scroll);
let btn_row = GBox::new(Orientation::Horizontal, 8);
btn_row.set_margin_top(8);
@ -198,7 +202,7 @@ pub fn build() -> GBox {
btn_row.append(&add_btn);
btn_row.append(&save_btn);
btn_row.append(&status_lbl);
vbox.append(&btn_row);
outer.append(&btn_row);
vbox
outer
}