Continues reducing terminal-reliance for graphical system control. Five new panels, plus three GUI apps shipped for things better served by an existing dedicated tool than reimplemented in bos-settings (gnome-disk-utility, gufw, mission-center). - Power: battery status/health, brightness, charge-limit thresholds where the hardware exposes them, TLP profile shown read-only by design (no Balanced/Performance switcher — TLP auto-selects by power source, and power-profiles-daemon isn't installed because it conflicts with tlp). - Firewall: ufw enable/disable, add/remove rules, view active rules. ufw's own status check requires root (confirmed against the installed script — not just changes, reads too), so unlike every other panel this one does NOT query state in build(): every view is constructed eagerly at app launch, and an unconditional privileged read here would mean a polkit prompt on every single bos-settings open. Starts blank with a "Status not loaded" placeholder; state loads only on an explicit Refresh click, with a guard so refresh's own set_active() doesn't loop back into triggering ufw enable/disable. - Users: add/remove accounts, change passwords. All through pkexec on a background thread. Can't remove the account you're currently running as. - AUR: search via yay. Installing deliberately opens a terminal instead of a silent --noconfirm install — yay's interactive PKGBUILD diff review and sudo prompt are the actual safety mechanism against a malicious AUR package, not a formality worth automating away. - Firmware: fwupd device list + updates, same stream-output-then-refresh pattern as Packages. packages.x86_64: gnome-disk-utility, gufw, mission-center for disk/firewall/ task-manager GUIs that don't need reinventing inside bos-settings.
74 lines
2.8 KiB
Rust
74 lines
2.8 KiB
Rust
use gtk4::prelude::*;
|
|
use gtk4::{Application, ApplicationWindow, Orientation, Paned, Stack};
|
|
|
|
use super::sidebar;
|
|
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) {
|
|
let window = ApplicationWindow::builder()
|
|
.application(app)
|
|
.title("BOS Settings")
|
|
.default_width(960)
|
|
.default_height(640)
|
|
.build();
|
|
|
|
crate::theme::load(&WidgetExt::display(&window));
|
|
|
|
let hpaned = Paned::new(Orientation::Horizontal);
|
|
hpaned.set_position(210);
|
|
hpaned.set_shrink_start_child(false);
|
|
hpaned.set_resize_start_child(false);
|
|
|
|
let (sidebar_box, list) = sidebar::build(DEFAULT_PAGE);
|
|
|
|
let stack = Stack::new();
|
|
stack.set_hexpand(true);
|
|
stack.set_vexpand(true);
|
|
|
|
stack.add_named(&views::about::build(), Some("about"));
|
|
stack.add_named(&views::network::build(), Some("network"));
|
|
stack.add_named(&views::sound::build(), Some("sound"));
|
|
stack.add_named(&views::datetime::build(), Some("datetime"));
|
|
stack.add_named(&views::power::build(), Some("power"));
|
|
stack.add_named(&views::firewall::build(), Some("firewall"));
|
|
stack.add_named(&views::users::build(), Some("users"));
|
|
stack.add_named(&views::snapshots::build(), Some("snapshots"));
|
|
stack.add_named(&views::packages::build(), Some("packages"));
|
|
stack.add_named(&views::firmware::build(), Some("firmware"));
|
|
stack.add_named(&views::aur::build(), Some("aur"));
|
|
stack.add_named(&views::bread::build(), Some("bread"));
|
|
stack.add_named(&views::breadbar::build(), Some("breadbar"));
|
|
stack.add_named(&views::breadbox::build(), Some("breadbox"));
|
|
stack.add_named(&views::breadclip::build(), Some("breadclip"));
|
|
stack.add_named(&views::breadcrumbs::build(), Some("breadcrumbs"));
|
|
stack.add_named(&views::breadpad::build(), Some("breadpad"));
|
|
stack.add_named(&views::breadpaper::build(), Some("breadpaper"));
|
|
stack.add_named(&views::breadsearch::build(), Some("breadsearch"));
|
|
stack.add_named(&views::hyprland::build(), Some("hyprland"));
|
|
|
|
stack.set_visible_child_name(DEFAULT_PAGE);
|
|
|
|
{
|
|
let stack = stack.clone();
|
|
list.connect_row_selected(move |_, row| {
|
|
if let Some(row) = row {
|
|
let name = row.widget_name();
|
|
if !name.is_empty() {
|
|
stack.set_visible_child_name(&name);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
hpaned.set_start_child(Some(&sidebar_box));
|
|
hpaned.set_end_child(Some(&stack));
|
|
|
|
window.set_child(Some(&hpaned));
|
|
window.present();
|
|
}
|