From f1f169ede1a14948fcbe5a7ee065597ba96ca400 Mon Sep 17 00:00:00 2001 From: francoisgn <32835330+francoisgn@users.noreply.github.com> Date: Sat, 24 Oct 2026 10:39:09 +0300 Subject: [PATCH] #1290: a launch-list row waits for a re-check's answer FredApps on #2689: a re-check started while the launch list is up (a library not found looked for again, the window back to the front) never marked its row as checking, so `apply_answers` skipped the new answer: a library plugged back in stayed "not found" in the list. A row is re-armed when its path is asked again about an answer it has (not after a failed open, whose reason stays on the row). Test a_library_found_again_comes_back_to_the_launch_list fails without it. Fixes #2774 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JbBu4MRiaKxEbSrMTZiLj8 --- crates/ui-egui/src/panels/library_problem.rs | 13 +++++++ crates/ui-egui/src/tests_launch_library.rs | 39 ++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/crates/ui-egui/src/panels/library_problem.rs b/crates/ui-egui/src/panels/library_problem.rs index 2cec27801..3081bc80c 100644 --- a/crates/ui-egui/src/panels/library_problem.rs +++ b/crates/ui-egui/src/panels/library_problem.rs @@ -345,6 +345,9 @@ fn start_checks(app: &mut LightcraftApp, paths: Vec) { if app.recent_checks.pending.contains_key(&path) { continue; } + // asked again about an answer it has (not after a failed open, which dropped it and says why + // on its row) + let recheck = app.recent_checks.known.contains_key(&path); let check = app.recent_checks.check.clone(); let p = path.clone(); let started = crate::tasks::spawn(app, CHECK_LABEL, Some("library"), move || check(&p), { @@ -358,6 +361,16 @@ fn start_checks(app: &mut LightcraftApp, paths: Vec) { }); match started { Ok(()) => { + // a row of the launch list waits for this answer again (a library not found looked for + // again, a window back to the front): without it, the row kept its old answer + if recheck + && let Some(l) = + app.library_problem.as_mut().and_then(|p| p.chooser.as_mut()).and_then(|c| c.libraries.iter_mut().find(|l| l.path == path)) + { + l.checking = true; + l.slow = false; + l.unavailable = None; + } app.recent_checks.pending.insert(path, web_time::Instant::now()); } // no worker thread: say so on the row, and don't try again every frame diff --git a/crates/ui-egui/src/tests_launch_library.rs b/crates/ui-egui/src/tests_launch_library.rs index 54491beb6..b0b3bec6b 100644 --- a/crates/ui-egui/src/tests_launch_library.rs +++ b/crates/ui-egui/src/tests_launch_library.rs @@ -950,3 +950,42 @@ fn recent_libraries_are_checked_again_only_when_the_window_comes_back() { drop(h); let _ = std::fs::remove_dir_all(&dir); } + +/// A library the launch list showed as not found comes back to it once found again (a drive plugged +/// back in while the list is up): the row waits for the new check instead of keeping its old answer +/// (FredApps on #2689, #2774). +#[test] +fn a_library_found_again_comes_back_to_the_launch_list() { + use std::sync::atomic::{AtomicBool, Ordering}; + let dir = temp("found-again"); + let a = make_library(&dir.join("A")); + let plugged = std::sync::Arc::new(AtomicBool::new(false)); + let p = plugged.clone(); + let mut app = LightcraftApp::new(lightcraft_engine::Session::new().with_fs(), Services::default()); + app.recent_checks = crate::panels::library_problem::RecentChecks::with_check(move |_| { + (!p.load(Ordering::SeqCst)).then_some(crate::panels::library_problem::Unavailable::NotFound) + }) + .recheck_after(Duration::ZERO); + app.ui.settings.recent_libraries = vec![a.clone()]; + app.library_problem = Some(LibraryProblem::choose_later(&app.ui.settings)); + let mut h = Headless::new(app, [1300.0, 900.0], 1.0); + let row = |h: &Headless| h.app.library_problem.as_ref().and_then(|p| p.chooser.as_ref()).map(|c| c.libraries[0].clone()).unwrap(); + assert!( + h.step_until(T, |h| { + let r = row(h); + !r.checking && r.unavailable.is_some() + }), + "not found first" + ); + plugged.store(true, Ordering::SeqCst); + assert!( + h.step_until(T, |h| { + let r = row(h); + !r.checking && r.unavailable.is_none() + }), + "found again: it can be opened, {:?}", + row(&h) + ); + drop(h); + let _ = std::fs::remove_dir_all(&dir); +}