mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-30 05:37:00 -06:00
UI: closing the window asks about the macro step editor first
A window close (the close button, Alt+F4, quitting) let the app go when every workbook was saved, losing the editor's unsaved steps, and with an unsaved workbook it made that workbook active under the editor and asked for a Save Changes question the editor's window would not let open, so nothing happened. The close now waits for the editor: it asks about its debug run and unsaved steps as its own close button does, Cancel keeps the app open, and once the editor has closed the window close goes on and asks about unsaved workbooks as before.
This commit is contained in:
@@ -1144,6 +1144,13 @@ impl SheetApp {
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose);
|
||||
return;
|
||||
}
|
||||
// The macro step editor asks about its own unsaved steps and debug run first; once it has
|
||||
// closed, the window close goes on.
|
||||
if self.dialog.as_ref().is_some_and(|d| d.name == "macroEditor") {
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose);
|
||||
macro_editor::request_close(self, true);
|
||||
return;
|
||||
}
|
||||
if !self.session.documents().iter().any(|d| d.is_dirty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,10 @@ pub struct Editor {
|
||||
/// Asking whether to keep what a stopped debug run did (and then close, if `closing`).
|
||||
pub ask_stop: bool,
|
||||
pub closing: bool,
|
||||
/// Close as the title bar's close button does (asking first) on the next frame.
|
||||
pub close_requested: bool,
|
||||
/// The window is quitting: once the editor has closed, the window close goes on.
|
||||
pub quit: bool,
|
||||
/// Asking whether to save before closing.
|
||||
pub ask_close: bool,
|
||||
/// Start debugging on the first frame (Macros › Step Into).
|
||||
@@ -503,6 +507,8 @@ pub fn open(app: &mut SheetApp, params: &Json) -> Option<Dialog> {
|
||||
errors: BTreeMap::new(),
|
||||
ask_stop: false,
|
||||
closing: false,
|
||||
close_requested: false,
|
||||
quit: false,
|
||||
ask_close: false,
|
||||
start_debug,
|
||||
start_check: params["check"].as_bool() == Some(true),
|
||||
@@ -815,7 +821,7 @@ pub fn window(app: &mut SheetApp, ctx: &egui::Context, d: &mut Dialog, open: &mu
|
||||
if ed.refs.pick.is_some() && !ed.refs.drawn {
|
||||
ed.refs.pick = None;
|
||||
}
|
||||
if !win_open {
|
||||
if !win_open || std::mem::take(&mut ed.close_requested) {
|
||||
if app.session.macro_debug.is_some() {
|
||||
ed.ask_stop = true;
|
||||
ed.closing = true;
|
||||
@@ -833,8 +839,23 @@ pub fn window(app: &mut SheetApp, ctx: &egui::Context, d: &mut Dialog, open: &mu
|
||||
app.grid.macro_overlay = if *open && home { overlay(app, &ed) } else { None };
|
||||
if *open {
|
||||
app.macros.editor = Some(ed);
|
||||
} else if home {
|
||||
restore_home(app, &ed.refs);
|
||||
} else {
|
||||
if home {
|
||||
restore_home(app, &ed.refs);
|
||||
}
|
||||
if ed.quit {
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Asks the open step editor to close, as its close button does: it asks about a debug run or
|
||||
/// unsaved steps first. With `quit` (the window is closing), the window close goes on once the
|
||||
/// editor has closed; a Cancel in its questions keeps both open.
|
||||
pub fn request_close(app: &mut SheetApp, quit: bool) {
|
||||
if let Some(ed) = app.macros.editor.as_mut() {
|
||||
ed.close_requested = true;
|
||||
ed.quit = quit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -966,6 +987,7 @@ fn prompts(app: &mut SheetApp, ctx: &egui::Context, ed: &mut Editor, open: &mut
|
||||
Some(None) => {
|
||||
ed.ask_stop = false;
|
||||
ed.closing = false;
|
||||
ed.quit = false;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
@@ -1000,7 +1022,10 @@ fn prompts(app: &mut SheetApp, ctx: &egui::Context, ed: &mut Editor, open: &mut
|
||||
ed.ask_close = false;
|
||||
*open = false;
|
||||
}
|
||||
Some(None) => ed.ask_close = false,
|
||||
Some(None) => {
|
||||
ed.ask_close = false;
|
||||
ed.quit = false;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,6 +403,67 @@ fn the_editor_works_only_on_the_workbook_it_opened_in() {
|
||||
assert!(h.state().dialog.as_ref().is_none_or(|d| d.name() != "macroEditor"));
|
||||
}
|
||||
|
||||
/// Asks the window to close (the close button, Alt+F4, quitting) and returns the commands the
|
||||
/// app answered with over the next frames.
|
||||
fn request_window_close(h: &mut Harness) -> Vec<egui::ViewportCommand> {
|
||||
h.input_mut().viewports.entry(egui::ViewportId::ROOT).or_default().events.push(egui::ViewportEvent::Close);
|
||||
step_commands(h)
|
||||
}
|
||||
|
||||
fn step_commands(h: &mut Harness) -> Vec<egui::ViewportCommand> {
|
||||
let mut out = Vec::new();
|
||||
for _ in 0..4 {
|
||||
h.step();
|
||||
out.extend(h.output().viewport_output.get(&egui::ViewportId::ROOT).map(|v| v.commands.clone()).unwrap_or_default());
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quitting_asks_about_the_editors_unsaved_steps_first() {
|
||||
let mut h = harness();
|
||||
three(&mut h);
|
||||
h.state_mut().macros.editor.as_mut().unwrap().draft.name = "Unsaved".into();
|
||||
h.state_mut().macros.editor.as_mut().unwrap().dirty = true;
|
||||
// As if the workbook had just been saved.
|
||||
let d = h.state_mut().session.active_mut().unwrap();
|
||||
d.saved = d.wb.clone();
|
||||
h.run_steps(2);
|
||||
// Every workbook is saved, but the draft is not: the window stays open and the editor asks.
|
||||
let sent = request_window_close(&mut h);
|
||||
assert!(sent.contains(&egui::ViewportCommand::CancelClose), "{sent:?}");
|
||||
assert!(!sent.contains(&egui::ViewportCommand::Close), "{sent:?}");
|
||||
assert!(h.query_by_label_contains("Save changes to macro “Unsaved”").is_some());
|
||||
// Cancel keeps the app and the draft.
|
||||
h.get_by_label("Cancel").click();
|
||||
let sent = step_commands(&mut h);
|
||||
assert!(!sent.contains(&egui::ViewportCommand::Close), "{sent:?}");
|
||||
assert_eq!(h.state().macros.editor.as_ref().map(|e| e.draft.name.as_str()), Some("Unsaved"));
|
||||
// Asked again, Don't Save closes the editor and then the window.
|
||||
request_window_close(&mut h);
|
||||
h.get_by_label("Don't Save").click();
|
||||
let sent = step_commands(&mut h);
|
||||
assert!(h.state().macros.editor.is_none());
|
||||
assert!(sent.contains(&egui::ViewportCommand::Close), "{sent:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quitting_with_the_editor_open_still_asks_about_unsaved_workbooks() {
|
||||
let mut h = harness();
|
||||
three(&mut h);
|
||||
h.state_mut().run("cell.set", json!({"cell": "Z9", "input": "unsaved"})).unwrap();
|
||||
h.run_steps(2);
|
||||
let sent = request_window_close(&mut h);
|
||||
assert!(sent.contains(&egui::ViewportCommand::CancelClose), "{sent:?}");
|
||||
// The saved, unchanged editor closes and the window close goes on (the window then asks to
|
||||
// close again, which a test harness has to do by hand); the workbook is asked about as
|
||||
// without the editor.
|
||||
assert!(h.state().macros.editor.is_none());
|
||||
assert!(sent.contains(&egui::ViewportCommand::Close), "{sent:?}");
|
||||
request_window_close(&mut h);
|
||||
assert_eq!(h.state().dialog.as_ref().map(|d| d.name()), Some("saveChanges"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_dialog_asked_for_over_the_editor_says_why_it_did_not_open() {
|
||||
let mut h = harness();
|
||||
|
||||
+3
-1
@@ -293,7 +293,9 @@ other macros open; a macro that was left out is not in the saved file.
|
||||
the Output tab sets the anchor and what to do when results would land on data. The editor
|
||||
belongs to the workbook it opened in: while another workbook is active it says so and Save,
|
||||
Check, Run and Debug wait until that workbook is active again, and closing the workbook closes
|
||||
the editor.
|
||||
the editor. It keeps its window until it closes itself: another dialog asked for meanwhile is
|
||||
refused with a message, and closing the app first asks about the editor's debug run and unsaved
|
||||
steps, then about unsaved workbooks.
|
||||
- **Debug bar.** Check dry-runs the macro and colours the grid: inputs blue, the cells it writes
|
||||
green, the cells among them that hold data red. Run runs it as one undo step. Step, Continue and
|
||||
Run to Here start the debugger when needed (`macro.debug` with the editor's breakpoints and
|
||||
|
||||
Reference in New Issue
Block a user