diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 3e635ea2f..d27712da2 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -570,7 +570,13 @@ impl CadApp { /// Add the files whose background read has finished; report the ones that failed as a failed /// OPEN is reported. Called every frame. pub fn finish_opening(&mut self) { - for (_, outcome) in self.opening.poll() { + for (path, outcome) in self.opening.poll() { + // A recent file that is gone can never be opened again: drop it from the list. A path + // whose existence can't be checked (a folder that denies access) stays. + if outcome.is_err() && matches!(path.try_exists(), Ok(false)) { + let path = path.to_string_lossy(); + self.ui.recent_files.retain(|p| *p != path); + } let r = match outcome { Ok(f) => self.session.finish_open(f).map_err(|e| e.to_string()), Err(e) => Err(e), diff --git a/crates/ui-egui/tests/recent_files.rs b/crates/ui-egui/tests/recent_files.rs index 34d460255..34aa6e2e8 100644 --- a/crates/ui-egui/tests/recent_files.rs +++ b/crates/ui-egui/tests/recent_files.rs @@ -135,3 +135,15 @@ fn start_page_lists_recent_files_and_opens_one_on_click() { assert_eq!(app.session.state().ok().and_then(|s| s.path.clone()), Some(plan.clone())); assert_eq!(app.ui.recent_files, [plan, section]); } + +#[test] +fn a_recent_file_that_no_longer_exists_is_dropped_when_opening_it_fails() { + let (_dir, plan, _section) = drawings("missing"); + let mut app = CadApp::new(Session::empty(), Services::default()); + let gone = "/nonexistent/dir/gone.dxf".to_string(); + app.ui.recent_files = vec![gone.clone(), plan.clone()]; + let id = open_recent(&app).into_iter().find(|(l, _)| l == "gone.dxf").map(|(_, id)| id).unwrap_or_default(); + menus::activate(&mut app, &id); + settle(&mut app); + assert_eq!(app.ui.recent_files, [plan], "the stale entry is removed, the others stay"); +}