From e2c8c503c1dc35673b3a2c636677c3be166cf974 Mon Sep 17 00:00:00 2001 From: ruchirguitar <113586074+ruchirguitar@users.noreply.github.com> Date: Wed, 4 Nov 2026 01:28:47 +0530 Subject: [PATCH] opening::poll stops once a queued reopen asks a question A document asked for again while a dialog was open, and closed meanwhile, opens again when the dialog closes and may ask about its links or fonts. poll went on to finish the files read meanwhile, and each became the active document under that dialog. It now checks may_switch after each queued activation and leaves the rest, and the files ready to open, for a later frame. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/opening.rs | 14 ++++++++---- crates/ui-egui/src/opening/tests.rs | 35 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/crates/ui-egui/src/opening.rs b/crates/ui-egui/src/opening.rs index e23463493..1a816f2d8 100644 --- a/crates/ui-egui/src/opening.rs +++ b/crates/ui-egui/src/opening.rs @@ -103,10 +103,16 @@ pub(crate) fn may_switch(app: &VectorcraftApp) -> bool { /// waiting between its clicks (the Dimension tool after its first point): the file opens, and the /// tool starts afresh, as when the user switches documents. pub fn poll(app: &mut VectorcraftApp) { - if !may_switch(app) { - return; - } - for (path, scope) in std::mem::take(&mut app.opening.activate) { + // One at a time: a document that opens again (closed meanwhile) may ask about its links or + // fonts, and the rest wait for that dialog too. + loop { + if !may_switch(app) { + return; + } + if app.opening.activate.is_empty() { + break; + } + let (path, scope) = app.opening.activate.remove(0); // Closed meanwhile, it opens again; a failure is reported to the user there. let _ = scope.run(|| crate::io::open_reporting(app, &path)); } diff --git a/crates/ui-egui/src/opening/tests.rs b/crates/ui-egui/src/opening/tests.rs index 3b5d3a404..ee5d0a224 100644 --- a/crates/ui-egui/src/opening/tests.rs +++ b/crates/ui-egui/src/opening/tests.rs @@ -348,3 +348,38 @@ fn a_file_asked_for_again_while_it_is_read_opens_once() { } assert_eq!(app.session.documents().len(), 1); } + +/// A document asked for again while a dialog was open, closed meanwhile, opens again once the +/// dialog closes and asks about its fonts; a file read meanwhile waits for that question too, so +/// the dialog acts on its own document (#5369). +#[test] +fn a_queued_reopen_that_asks_holds_back_files_ready_to_open() { + let dir = std::env::temp_dir().join(format!("vectorcraft-opening-queued-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + std::fs::create_dir_all(&dir).unwrap(); + let mut app = VectorcraftApp::new(Session::new(), Services::default()); + app.services.read = Some(Box::new(|p: &str| std::fs::read(p).map_err(|e| e.to_string()))); + let doc = dir.join("doc.vectorcraft").to_string_lossy().into_owned(); + app.run("file.new", json!({"width": 400, "height": 300})).unwrap(); + app.run("text.create", json!({"x": 10, "y": 40, "text": "Type", "font": "No Such Font Family"})).unwrap(); + app.run("document.save", json!({"path": doc})).unwrap(); + app.run("file.new", json!({})).unwrap(); + app.ui.dialog = Some(crate::state::Dialog::new(crate::dialogs::save_options::KIND, json!({}))); + crate::io::open_reporting(&mut app, &doc).unwrap(); + assert_eq!(app.opening.activate.len(), 1); + app.session.execute("file.close", &json!({"index": 0})).unwrap(); + start(&mut app, "/art/Other.vectorcraft", vectorcraft_format::save(&vectorcraft_doc::Document::new(120.0, 80.0), false)).unwrap(); + // Time for the other file to be read; it waits for the dialog meanwhile. + let t0 = std::time::Instant::now(); + while t0.elapsed().as_millis() < 500 { + poll(&mut app); + std::thread::yield_now(); + } + assert_eq!(app.opening.pending.len(), 1); + app.ui.dialog = None; + poll(&mut app); + assert_eq!(app.ui.dialog.as_ref().map(|d| d.kind.as_str()), Some(crate::dialogs::missing_fonts::KIND)); + assert_eq!(app.session.doc().unwrap().path.as_deref(), Some(&*doc), "the dialog's document stays active"); + assert_eq!(app.opening.pending.len(), 1, "the other file waits"); + let _ = std::fs::remove_dir_all(dir); +}