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 <noreply@anthropic.com>
This commit is contained in:
ruchirguitar
2026-11-04 08:13:04 +07:00
committed by bflatastic
co-authored by Claude Opus 5.5
parent 72cf1a9b3c
commit e2c8c503c1
2 changed files with 45 additions and 4 deletions
+10 -4
View File
@@ -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));
}
+35
View File
@@ -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);
}