Fix #341: the close decision runs in App::logic

eframe runs App::logic without App::ui while the window is minimized or
hidden, and handles a close request then too. The unsaved-changes check, the
device-loss close and craft_quit::on_close_request ran from ui, so such a
close skipped the prompt and the macOS quit through terminate:. They now run
at the end of logic, with the platform hook last, as craft-quit asks.
This commit is contained in:
dotCooCoo committed 2026-10-24 02:50:22 -07:00
1 parent aa296cf273
commit eff747897d
2 files changed
+25 -5

No files matched your search

+10 -5
View File
@@ -1668,7 +1668,6 @@ impl EffectcraftApp {
self.run_script_tasks(&ctx);
self.apply_prefs(&ctx);
self.handle_events(&ctx);
panels::unsaved::close_or_quit(self, &ctx);
let title = panels::unsaved::window_title(self);
if title != self.window_title {
ctx.send_viewport_cmd(egui::ViewportCommand::Title(title.clone()));
@@ -1913,14 +1912,20 @@ impl eframe::App for EffectcraftApp {
}
self.issue_screenshots(ctx);
self.collect_screenshots(ctx);
// eframe runs `logic` without `ui` while the window is minimized or hidden, and handles a
// close then too, so the close decision is made here, with the platform hook last.
if self.gpu_failure_applied == Some(true) {
self.close_after_device_loss(ctx);
// On macOS the app quits through `-[NSApplication terminate:]` instead of closing the
// window while AppKit's run loop is running (#341).
craft_quit::on_close_request(ctx);
} else {
panels::unsaved::close_or_quit(self, ctx);
}
}
fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
if self.gpu_failure_applied == Some(true) {
self.close_after_device_loss(ui.ctx());
// On macOS the app quits through `-[NSApplication terminate:]` instead of closing the
// window while AppKit's run loop is running (#341).
craft_quit::on_close_request(ui.ctx());
return;
}
self.init_gpu(frame);
+15
View File
@@ -391,4 +391,19 @@ mod tests {
let macos = cfg!(target_os = "macos");
assert_eq!(close_frame(&mut app, &ctx), (macos, macos), "Don't Save quits");
}
/// While the window is minimized or hidden, eframe runs `App::logic` without `App::ui` and
/// still handles a close: `logic` asks about a modified project.
#[test]
fn a_close_while_only_logic_runs_asks_about_a_modified_project() {
let (mut app, ctx) = app();
modified(&mut app);
let mut raw = egui::RawInput::default();
raw.viewports.entry(egui::ViewportId::ROOT).or_default().events.push(egui::ViewportEvent::Close);
let mut frame = eframe::Frame::_new_kittest();
let out = ctx.run_logic(&raw, |ctx| eframe::App::logic(&mut app, ctx, &mut frame));
let canceled = out.viewport_commands.get(&egui::ViewportId::ROOT).is_some_and(|c| c.contains(&egui::ViewportCommand::CancelClose));
assert!(canceled, "the close is canceled");
assert_eq!(app.dialog, Some(Dialog::UnsavedChanges));
}
}