diff --git a/crates/ui-egui/src/control.rs b/crates/ui-egui/src/control.rs index 724f111e..43d4cf59 100644 --- a/crates/ui-egui/src/control.rs +++ b/crates/ui-egui/src/control.rs @@ -335,10 +335,9 @@ pub fn handle(app: &mut VectorcraftApp, ctx: &egui::Context, req: &ControlReques // No save dialog for an agent: the bytes come back, as in headless mode. None => wrap(app.run("document.export", p.clone())), }, - "app.quit" => { - ctx.send_viewport_cmd(egui::ViewportCommand::Close); - ok(Value::Null) - } + // As the menu's Quit: `{"pending": "saveChanges"}` while a modified document is asked + // about; otherwise null, and the host closes the window (#830). + "app.quit" => wrap(app.run("app.quit", json!({}))), other => err(format!("unknown method `{other}`")), } } diff --git a/crates/ui-egui/src/unsaved.rs b/crates/ui-egui/src/unsaved.rs index 35c52dca..829d426f 100644 --- a/crates/ui-egui/src/unsaved.rs +++ b/crates/ui-egui/src/unsaved.rs @@ -206,4 +206,23 @@ mod tests { app.run("app.quit", json!({})).unwrap(); assert_eq!(app.ui.status, "quit"); } + + #[test] + fn the_control_channels_quit_says_when_it_asks_first() { + // #830: it answered null at once, before the question. + let (mut app, _) = app(); + let ctx = egui::Context::default(); + let quit = |app: &mut VectorcraftApp| { + let (req, _rx) = crate::control::ControlRequest::new("app.quit", json!({})); + let crate::control::Outcome::Done(r) = crate::control::handle(app, &ctx, &req) else { panic!("not done") }; + r + }; + new_doc(&mut app, true); + assert_eq!(quit(&mut app), json!({"ok": true, "result": {"pending": KIND}})); + assert_eq!((pending(&app), app.ui.status.as_str()), (Some(0), "")); + app.ui.dialog = None; + app.run("file.save", json!({"path": "/docs/saved.vectorcraft"})).unwrap(); + assert_eq!(quit(&mut app), json!({"ok": true, "result": null})); + assert_eq!(app.ui.status, "quit"); + } }