From 8c44a35398abdacd5521e4726db3093fa3fee1b6 Mon Sep 17 00:00:00 2001 From: "Wyatt J. Miller" Date: Thu, 27 Aug 2026 22:50:40 -0400 Subject: [PATCH] mod: adjustments --- resources/appmenu.xml | 17 ++++++++++++ resources/style.css | 17 ++++++++---- src/main.rs | 62 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 90 insertions(+), 6 deletions(-) diff --git a/resources/appmenu.xml b/resources/appmenu.xml index 683d574..7777d12 100644 --- a/resources/appmenu.xml +++ b/resources/appmenu.xml @@ -2,6 +2,23 @@ +
+ + _Appearance +
+ + Follow _System + app.theme + system + + + _Dark + app.theme + dark + +
+
+
_About SNES Patcher diff --git a/resources/style.css b/resources/style.css index 370c1f3..831e67e 100644 --- a/resources/style.css +++ b/resources/style.css @@ -2,11 +2,6 @@ min-width: 356px; } -.title { - font-size: 1.5rem; - font-weight: 700; -} - .heading { font-weight: 600; } @@ -21,3 +16,15 @@ min-height: 48px; padding: 10px 12px; } + +.intro-title { + font-size: 1.5rem; + font-weight: 700; +} + +headerbar.titlebar { + background-color: transparent; + background-image: none; + border-bottom: none; + box-shadow: none; +} diff --git a/src/main.rs b/src/main.rs index eb574dc..bb32dc9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use std::rc::Rc; use gtk4::gio; use gtk4::glib; +use gtk4::glib::ToVariant; use gtk4::pango::EllipsizeMode; use gtk4::prelude::*; use gtk4::{ @@ -17,6 +18,8 @@ use gtk4::{ const APP_ID: &str = "com.wyattjmiller.snespatcher"; const APP_NAME: &str = "SNES Patcher"; +const THEME_SYSTEM: &str = "system"; +const THEME_DARK: &str = "dark"; #[derive(Clone)] struct SelectionState { @@ -71,7 +74,7 @@ fn build_ui(app: &Application) { let introduction = GtkBox::new(Orientation::Vertical, 8); let heading = Label::new(Some("Patch a Super Nintendo ROM")); - heading.add_css_class("title"); + heading.add_css_class("intro-title"); heading.set_justify(Justification::Center); heading.set_wrap(true); @@ -192,6 +195,8 @@ fn build_header_bar( header_bar.pack_start(rom_info_button); header_bar.pack_end(&menu_button); + add_theme_action(app); + let about = gio::SimpleAction::new("about", None); about.connect_activate(glib::clone!(@weak window => move |_, _| { let dialog = AboutDialog::builder() @@ -219,6 +224,61 @@ fn build_header_bar( header_bar } +fn add_theme_action(app: &Application) { + let preference = load_theme_preference(); + let settings = gtk4::Settings::default().expect("Could not access GTK settings"); + apply_theme_preference(&settings, &preference); + + let theme = gio::SimpleAction::new_stateful( + "theme", + Some(glib::VariantTy::STRING), + &preference.to_variant(), + ); + theme.connect_activate(move |action, parameter| { + let Some(preference) = parameter.and_then(|value| value.get::()) else { + return; + }; + if preference != THEME_SYSTEM && preference != THEME_DARK { + return; + } + + apply_theme_preference(&settings, &preference); + action.set_state(&preference.to_variant()); + save_theme_preference(&preference); + }); + app.add_action(&theme); +} + +fn apply_theme_preference(settings: >k4::Settings, preference: &str) { + settings.set_gtk_application_prefer_dark_theme(preference == THEME_DARK); +} + +fn theme_preference_path() -> PathBuf { + glib::user_config_dir().join(APP_ID).join("theme") +} + +fn load_theme_preference() -> String { + match std::fs::read_to_string(theme_preference_path()) { + Ok(preference) if preference.trim() == THEME_DARK => THEME_DARK.to_string(), + _ => THEME_SYSTEM.to_string(), + } +} + +fn save_theme_preference(preference: &str) { + let path = theme_preference_path(); + let Some(parent) = path.parent() else { + return; + }; + + if let Err(error) = std::fs::create_dir_all(parent) { + eprintln!("Could not create the preferences directory: {error}"); + return; + } + if let Err(error) = std::fs::write(path, preference) { + eprintln!("Could not save the theme preference: {error}"); + } +} + fn selection_row(title: &str, placeholder: &str, icon_name: &str) -> SelectionRow { let icon = Image::from_icon_name(icon_name); icon.set_pixel_size(24);