From 4e77bd2549f2ffb47606d4c6ada63b6ac4c8422c Mon Sep 17 00:00:00 2001 From: Bruno-Alumn <171815746+Bruno-Alumn@users.noreply.github.com> Date: Fri, 30 Oct 2026 20:01:36 +0100 Subject: [PATCH] Quit asks about an import handed its files this tick, before it is listed, and about files still waiting --- crates/ui-egui/src/dialogs/import/task.rs | 5 +++++ crates/ui-egui/src/panels/notices.rs | 12 +++++++++--- crates/ui-egui/src/tests/library_problem.rs | 18 ++++++++++++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/crates/ui-egui/src/dialogs/import/task.rs b/crates/ui-egui/src/dialogs/import/task.rs index e1f506d5b..5c2798340 100644 --- a/crates/ui-egui/src/dialogs/import/task.rs +++ b/crates/ui-egui/src/dialogs/import/task.rs @@ -51,6 +51,11 @@ pub struct ImportTask { } impl ImportTask { + /// Whether it has its row in the activity list yet (it gets one on its first tick). + pub fn listed(&self) -> bool { + self.guard.is_some() + } + /// An import of `paths` (files or folders) with `library.import` params (without `paths`). pub fn new(paths: Vec, params: Value, undo0: usize, browse: bool) -> Self { ImportTask { total: paths.len(), queue: paths, params, undo0, browse, ..Default::default() } diff --git a/crates/ui-egui/src/panels/notices.rs b/crates/ui-egui/src/panels/notices.rs index 96e356bb4..6d3a1fbe3 100644 --- a/crates/ui-egui/src/panels/notices.rs +++ b/crates/ui-egui/src/panels/notices.rs @@ -65,9 +65,15 @@ pub fn may_close(app: &mut LightcraftApp) -> bool { } // before the library is touched: a Cancel here keeps working with it let running = app.session.activity.running_cancellable(); - if !running.is_empty() { - let names = running.iter().map(|t| crate::i18n::tr(&t.label)).collect::>().join(", "); - app.quit_prompt = Some(QuitPrompt::Tasks(tasks_message(running.len(), &names))); + let mut names = running.iter().map(|t| crate::i18n::tr(&t.label)).collect::>(); + // an import handed its files this tick (files that waited for a library, #2779) is listed only + // from its first tick on, and files still waiting will be imported too + let unlisted = app.import.as_ref().is_some_and(|t| !t.listed()) || !app.waiting_import.files.is_empty(); + if unlisted && !running.iter().any(|t| t.kind == "import") { + names.push(crate::i18n::tr("Importing")); + } + if !names.is_empty() { + app.quit_prompt = Some(QuitPrompt::Tasks(tasks_message(names.len(), &names.join(", ")))); return false; } if app.session.unsaved().is_none() { diff --git a/crates/ui-egui/src/tests/library_problem.rs b/crates/ui-egui/src/tests/library_problem.rs index d3f8eb78a..cdc3b7c8f 100644 --- a/crates/ui-egui/src/tests/library_problem.rs +++ b/crates/ui-egui/src/tests/library_problem.rs @@ -852,9 +852,23 @@ fn waiting_files_wait_for_a_running_import_instead_of_being_dropped() { assert!(r.as_ref().is_err_and(|e| e.contains("waiting")), "{r:?}"); assert_eq!(h.app.session.library.as_ref().map(|l| l.dir.clone()), Some(lib.clone())); - // the window is hidden from here on: only the background logic runs, and the waiting file - // still goes in once the first import ends + // quitting while the file waits asks first + assert!(!crate::panels::notices::may_close(&mut h.app)); + h.app.quit_prompt = None; + + // the window is hidden from here on: only the background logic runs. The tick that hands the + // file to a new import (once the first one ends) leaves that import unlisted until its own + // first tick, and quitting right then still asks let t0 = std::time::Instant::now(); + while !h.app.waiting_import.files.is_empty() && t0.elapsed() < T { + let ctx = h.view.ctx.clone(); + h.app.logic(&ctx); + std::thread::sleep(Duration::from_millis(2)); + } + assert!(h.app.waiting_import.files.is_empty(), "the file was handed to a new import"); + assert!(h.app.import.as_ref().is_some_and(|t| !t.listed()), "not listed before its first tick"); + assert!(!crate::panels::notices::may_close(&mut h.app), "quitting asks about the import just handed over"); + h.app.quit_prompt = None; while !(h.app.import.is_none() && h.app.session.catalog.len() == 101) && t0.elapsed() < T { let ctx = h.view.ctx.clone(); h.app.logic(&ctx);