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"));