From 079f5389a6b6ffd808bc6c110ce193019fd4f998 Mon Sep 17 00:00:00 2001 From: ruchirguitar <113586074+ruchirguitar@users.noreply.github.com> Date: Wed, 4 Nov 2026 01:53:19 +0530 Subject: [PATCH] opening::poll stops once a file it opens asks a question Of two files read off the UI thread and ready in the same frame, the first could open and ask about its links or fonts, and the second then became the active document under that dialog. poll now checks may_switch after each file it opens too, and leaves the rest for a later frame. The queued-reopen test also checks that a later queued activation stays queued. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/opening.rs | 4 ++++ crates/ui-egui/src/opening/tests.rs | 36 +++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/opening.rs b/crates/ui-egui/src/opening.rs index 1a816f2d8..fb2949dd4 100644 --- a/crates/ui-egui/src/opening.rs +++ b/crates/ui-egui/src/opening.rs @@ -132,6 +132,10 @@ pub fn poll(app: &mut VectorcraftApp) { app.ui.status.clear(); } p.scope.run(|| finish(app, &p.path, r)); + // A file that opened and asks about its links or fonts holds back the others too. + if !may_switch(app) { + return; + } } } diff --git a/crates/ui-egui/src/opening/tests.rs b/crates/ui-egui/src/opening/tests.rs index ee5d0a224..fa90d3cd5 100644 --- a/crates/ui-egui/src/opening/tests.rs +++ b/crates/ui-egui/src/opening/tests.rs @@ -359,14 +359,19 @@ fn a_queued_reopen_that_asks_holds_back_files_ready_to_open() { 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(); + let (doc, other) = (dir.join("doc.vectorcraft"), dir.join("other.vectorcraft")); + let (doc, other) = (doc.to_string_lossy().into_owned(), other.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.run("document.save", json!({"path": other})).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); + crate::io::open_reporting(&mut app, &other).unwrap(); + assert_eq!(app.opening.activate.len(), 2); + app.session.execute("file.close", &json!({"index": 1})).unwrap(); 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. @@ -380,6 +385,33 @@ fn a_queued_reopen_that_asks_holds_back_files_ready_to_open() { 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.activate.first().map(|(p, _)| p), Some(&other), "the later activation waits"); assert_eq!(app.opening.pending.len(), 1, "the other file waits"); let _ = std::fs::remove_dir_all(dir); } + +/// Of two files read off the UI thread and ready in the same frame, the first that opens and asks +/// about its fonts holds back the second, so the question acts on its own document (#5369). +#[test] +fn a_file_that_opens_and_asks_holds_back_the_next_one_ready() { + let mut app = VectorcraftApp::new(Session::new(), Services::default()); + 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(); + let asks = vectorcraft_format::save(&app.session.doc().unwrap().doc, false); + app.session.execute("file.close", &json!({})).unwrap(); + app.ui.dialog = Some(crate::state::Dialog::new(crate::dialogs::save_options::KIND, json!({}))); + start(&mut app, "/art/Asks.vectorcraft", asks).unwrap(); + start(&mut app, "/art/Plain.vectorcraft", vectorcraft_format::save(&vectorcraft_doc::Document::new(120.0, 80.0), false)).unwrap(); + // Time for both files to be read; they wait 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(), 2); + 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("/art/Asks.vectorcraft"), "the dialog's document stays active"); + assert_eq!(app.opening.pending.len(), 1, "the next file waits"); +}