From 494ce39de5a4a9c6f9ae8156690726c39becd085 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Wed, 14 Oct 2026 07:09:08 +0700 Subject: [PATCH] Fix #1747: Preferences are written when confirmed, not only at exit The preferences file was written only when the app quit normally, so settings confirmed with OK were lost when the session ended any other way (the system shutting down, the process being ended, a crash). OK now asks the host to write the file at once. --- apps/vectorcraft/src/main.rs | 5 +++++ crates/ui-egui/src/lib.rs | 9 +++++++++ crates/ui-egui/src/prefs_dialog.rs | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/apps/vectorcraft/src/main.rs b/apps/vectorcraft/src/main.rs index e5a3736d..8d827d24 100644 --- a/apps/vectorcraft/src/main.rs +++ b/apps/vectorcraft/src/main.rs @@ -95,6 +95,11 @@ impl eframe::App for App { self.app.titlebar_inset = inset.unwrap_or(mac_window::DEFAULT_INSET); } file_access::unconfined(|| self.app.ui(ui)); + // Preferences are written when confirmed, not only at exit: a session ended any other way + // (the system shutting down, the process ended or crashing) used to lose them (#1747). + if self.app.take_prefs_changed() { + file_access::unconfined(|| save_prefs(&self.app)); + } // Not while a file dialog's sheet has the keyboard: its name field's composition would go. #[cfg(target_os = "macos")] if !self.app.file_dialog_open() && self.app.take_ime_discard() { diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 6ed6b440..ebc4c123 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -425,6 +425,8 @@ pub struct VectorcraftApp { pub(crate) ime_marked: Option, /// The IME must drop its marked text (see [`Self::take_ime_discard`]). pub(crate) ime_discard: bool, + /// Preferences were confirmed (see [`Self::take_prefs_changed`]). + pub(crate) prefs_changed: bool, /// A numeric field is being scrubbed: the document's edits meanwhile are one undo step /// ([`scrub::begin_frame`]). scrub_group: bool, @@ -518,6 +520,7 @@ impl VectorcraftApp { synthetic_modifiers: false, ime_marked: None, ime_discard: false, + prefs_changed: false, scrub_group: false, automation_roots: None, synthetic_frame: false, @@ -857,6 +860,12 @@ impl VectorcraftApp { std::mem::take(&mut self.ime_discard) } + /// Was Preferences confirmed since the last call? The host then writes the preferences file, + /// so what the user just set survives a session that doesn't end with a normal quit (#1747). + pub fn take_prefs_changed(&mut self) -> bool { + std::mem::take(&mut self.prefs_changed) + } + /// Linked files another app changed while their document is open: every two seconds, and as /// soon as the window comes back to the front, stamp the active document's linked files on a /// worker thread and act on the changed ones as Preferences › File Handling › Update Links diff --git a/crates/ui-egui/src/prefs_dialog.rs b/crates/ui-egui/src/prefs_dialog.rs index 0e2bb93a..a4b049c9 100644 --- a/crates/ui-egui/src/prefs_dialog.rs +++ b/crates/ui-egui/src/prefs_dialog.rs @@ -45,6 +45,7 @@ pub fn confirm(app: &mut VectorcraftApp) -> Result { app.ui.task_bar = d.fields.get("__taskBar").and_then(Value::as_bool).unwrap_or(app.ui.task_bar); app.ui.dialog = None; app.ui.engine_prefs = app.session.prefs.to_json(); + app.prefs_changed = true; Ok(Value::Null) } @@ -557,7 +558,10 @@ mod tests { assert!(shown.iter().any(|t| t == "Automatic"), "{shown:?}"); assert!(shown.iter().any(|t| t == "Applies the next time VectorCraft starts."), "{shown:?}"); a.ui.dialog.as_mut().unwrap().fields.insert("gpuPreference".into(), json!("highPerformance")); + assert!(!a.take_prefs_changed(), "nothing to write before OK"); confirm(&mut a).unwrap(); + assert!(a.take_prefs_changed(), "OK asks the host to write the preferences file"); + assert!(!a.take_prefs_changed()); assert_eq!(a.session.prefs.gpu_preference, "highPerformance"); let saved: Value = serde_json::from_slice(&serde_json::to_vec(&a.ui).unwrap()).unwrap(); assert_eq!(saved["engine_prefs"]["gpuPreference"], json!("highPerformance"));