Repository navigation
[Feature]: Ask to save unsaved changes before quitting or replacing the project #906
Description
Before you start
- I searched the existing issues and the roadmap and did not find a duplicate request.
What problem does this solve?
The desktop app never asks before it discards unsaved changes. Quit (ui.quit sets quit_requested), the window's close button, New (Mod+N, the toolbar, the backstage), Open, the World Atlas sample, a template, a recent project, and a project dropped on the window all replace the project without a word. The app has no close_requested handling, so it can't stop a close. The browser app already warns before a page with unsaved changes is left (beforeunload).
Autosave (#873, #896) now covers crashes, but on purpose not a clean quit: its copy is removed when the app closes normally. One accidental Ctrl+Q therefore still loses the work. The automated review on #896 flagged this twice. ArcGIS Pro and QGIS ask before discarding unsaved changes.
Proposed solution
A "Save changes to Parcels?" dialog with Save, Don't Save and Cancel, shown by the desktop interface before an action would discard unsaved changes:
- Quit and closing the window. Handle the viewport's close request: cancel it, ask, then close once the user has chosen Save (and the save succeeded) or Don't Save. Cmd+Q on macOS should take the same path; I'll verify it does.
- Replacing the project: New, Open, the sample, a template, a recent project, a dropped project file, and Command Search's routes to those.
- Save saves first, using Save As when the project has no file. If that is cancelled or fails, nothing is discarded. Don't Save goes ahead. Cancel goes back to the project.
Commands still never open dialogs. project.new, project.open and the rest keep working silently for the CLI, Python, scripts, the AI Assistant and MCP clients. The check sits at the interface's dispatch, so it covers keyboard shortcuts and the ribbon in one place, not one prompt per button. Calls from the AI Assistant and the relay stay unprompted. Whether the Assistant should also ask before replacing a project with unsaved changes is a separate question.
Unsaved means unsaved. Today undoing back to the saved state still counts as unsaved (Session::dirty is a single flag that undo doesn't clear), so the dialog would ask when nothing changed. The session would remember the undo position at the last save and treat the project as saved when undo or redo returns there. This also makes the title's "•" accurate.
With autosave. Don't Save removes the recovery copy, as a clean quit already does. A crash is unaffected.
Crates: rustgis-engine (the saved undo position), rustgis-ui-egui (the dialog and the dispatch check), apps/rustgis (the close request). No new dependency, no format or command changes.
Tests: headless UI tests that click through each choice for Quit, New and Open. They cover Save on a never-saved project (Save As cancelled keeps the project), a failed save (nothing discarded), and undo back to the saved state (no prompt). They also check that commands run directly don't prompt. Plus a ui_shot of the dialog.
I'm working on this. I'll start once #896 is merged, since both change the quit path.
Estimated scope
Medium: a broader feature that is feasible in a reasonable timeframe
Alternatives considered
- Hot exit (VS Code-style): never ask, keep the recovery copy on quit and reopen the work at the next start. It reuses feat(ui): autosave unsaved work and offer it back after a crash #896, but the recovery dialog would then appear after ordinary quits, and it needs a final copy written at exit, not the last interval's. It could be an opt-in setting later.
- A prompt in each command: this would break the rule that commands never open dialogs, and would stop scripts and agents.
Activity
feat(ui): ask to save unsaved changes before quitting or replacing th…