fix(ui): persist the AutoSave preference across restarts

Closes #1901

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
bfenty
2026-10-19 19:07:18 +00:00
co-authored by Claude
parent 49fe41afbf
commit e34b0aa345
5 changed files with 41 additions and 15 deletions
+2 -2
View File
@@ -969,9 +969,9 @@ fn options(app: &mut SheetApp, ui: &mut Ui) {
}
ui.add_space(18.0);
section(ui, lang, "Options|Save");
let mut autosave = app.grid.autosave();
let mut autosave = app.ui.autosave;
if ui.checkbox(&mut autosave, lang.tr("Save .xlsx workbooks automatically after every change")).changed() {
app.grid.toggle_autosave();
app.toggle_autosave();
}
let Some(calc) = app.session.active().map(|d| d.wb.calc.clone()) else { return };
ui.add_space(18.0);
-10
View File
@@ -119,7 +119,6 @@ pub struct GridState {
pub glide: crate::selection_glide::SelectionGlide,
pub last_click: Option<(CellRef, f64)>,
pub hover_cell: Option<CellRef>,
pub autosave: bool,
pub last_autosave: f64,
pub last_border: String,
pub last_fill: String,
@@ -179,15 +178,6 @@ impl WheelZoom {
}
}
impl GridState {
pub fn autosave(&self) -> bool {
self.autosave
}
pub fn toggle_autosave(&mut self) {
self.autosave = !self.autosave;
}
}
/// Geometry of the grid for one frame (handles zoom and frozen panes).
#[derive(Clone)]
pub struct Geo {
+11 -1
View File
@@ -60,6 +60,9 @@ pub struct UiState {
/// Enable AutoComplete for cell values (File ▸ Options ▸ Editing): typed text is completed
/// from the one matching entry in the same column.
pub autocomplete: bool,
/// AutoSave (the title-bar switch): save saved-to-file .xlsx workbooks shortly after every
/// change. A preference like the rest of this struct — it stays on across restarts (#1901).
pub autosave: bool,
pub recent: Vec<String>,
/// One-shot keytip request for a ribbon control: set when a keytip action resolves to
/// [`keytips::Action::Control`], consumed (and cleared) by the control's render site.
@@ -103,6 +106,7 @@ impl Default for UiState {
status_bar: true,
animations: true,
autocomplete: true,
autosave: false,
recent: vec![],
keytip_invoke: None,
language: i18n::Language::En,
@@ -1128,6 +1132,12 @@ impl SheetApp {
}
}
/// Flip AutoSave (title-bar switch / Options ▸ Save). Persisted with the other UI
/// preferences, so it stays as the user left it across restarts (#1901).
pub fn toggle_autosave(&mut self) {
self.ui.autosave = !self.ui.autosave;
}
/// Lays out the whole window.
pub fn ui(&mut self, ui: &mut egui::Ui) {
let ctx = ui.ctx().clone();
@@ -1190,7 +1200,7 @@ impl SheetApp {
}
self.perf.last = now;
// AutoSave: save workbooks that have a file shortly after they change.
if self.grid.autosave
if self.ui.autosave
&& self.editor.is_none()
&& self.text_box_editor.is_none()
&& now - self.grid.last_autosave > 2000.0
+2 -2
View File
@@ -53,14 +53,14 @@ pub fn title_bar(app: &mut SheetApp, ui: &mut Ui) {
ui.add_space(TITLE_LEFT_PAD);
// AutoSave toggle (saves after each change when the workbook has a file).
ui.label(egui::RichText::new("AutoSave").font(theme::ui_font(12.5)).color(t.text_dim));
let on = app.grid.autosave();
let on = app.ui.autosave;
let (r, resp) = ui.allocate_exact_size(vec2(30.0, 16.0), Sense::click());
ui.painter().rect_filled(r, 8.0, if on { t.accent } else { Color32::TRANSPARENT });
ui.painter().rect_stroke(r, 8.0, Stroke::new(1.0, if on { t.accent } else { t.text_dim }), StrokeKind::Inside);
let knob = if on { pos2(r.right() - 8.0, r.center().y) } else { pos2(r.left() + 8.0, r.center().y) };
ui.painter().circle_filled(knob, 5.0, if on { Color32::WHITE } else { t.text_dim });
if resp.on_hover_text("AutoSave: save after every change (workbooks saved to a file)").clicked() {
app.grid.toggle_autosave();
app.toggle_autosave();
}
ui.add_space(6.0);
let __k = icon_button(ui, Icon::Home, t.text_dim, "Home", vec2(26.0, ch));
+26
View File
@@ -0,0 +1,26 @@
//! #1901: AutoSave is a preference — it must survive a restart like the other UI settings.
use gridcraft_engine::Session;
use gridcraft_ui_egui::SheetApp;
#[test]
fn autosave_survives_a_restart() {
let mut app = SheetApp::new(Session::new(), Default::default());
assert!(!app.ui.autosave, "off by default");
app.toggle_autosave();
assert!(app.ui.autosave, "the toggle flips the persisted flag");
// What the desktop app writes to ui.json, and reads back on the next launch.
let saved = serde_json::to_string(&app.ui).unwrap();
let restored: gridcraft_ui_egui::UiState = serde_json::from_str(&saved).unwrap();
assert!(restored.autosave, "a saved ui.json restores AutoSave on (#1901)");
app.toggle_autosave();
assert!(!app.ui.autosave, "toggling back persists off too");
}
#[test]
fn an_old_ui_json_without_autosave_still_loads() {
// Files saved by earlier versions have no autosave key; serde(default) reads them as off.
let restored: gridcraft_ui_egui::UiState = serde_json::from_str(r#"{"dark": true}"#).unwrap();
assert!(!restored.autosave);
assert!(restored.dark, "the rest of the old file still applies");
}