From 5ce8ce486e9d4d3ac9fa207aefab1321fb8b1d0f Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 6 Nov 2026 14:09:16 -0800 Subject: [PATCH] .ai save: Export All names each document's own active artboard Export All Open Documents read the active artboard recorded for each document, which was kept current only for the active one. A document whose artboard changed in its view while another tab was active wrote that stale artboard as the file's active one. Before each command the app now records every open document's active artboard from that document's own view. --- crates/ui-egui/src/addons/document/tests.rs | 33 +++++++++++++++++++++ crates/ui-egui/src/io/save.rs | 19 +++++++----- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/crates/ui-egui/src/addons/document/tests.rs b/crates/ui-egui/src/addons/document/tests.rs index 7ef678308..6e637c718 100644 --- a/crates/ui-egui/src/addons/document/tests.rs +++ b/crates/ui-egui/src/addons/document/tests.rs @@ -71,6 +71,39 @@ fn ai_copies_name_the_views_active_artboard() { } } +/// Export All Open Documents as `.ai` names each document's own active artboard, also for a +/// document whose artboard changed in its view after the last command and that is not active. +#[test] +fn export_all_names_each_documents_active_artboard() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + for name in ["first", "second"] { + app.run("file.new", json!({"width": 60, "height": 40, "artboards": 3, "name": name})).unwrap(); + app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 20, "height": 20})).unwrap(); + } + app.sync_views(); + // Choosing a tab and an artboard runs no command. + assert!(app.session.set_active(0)); + if let Some(v) = app.view_mut() { + v.artboard = 2; + } + assert!(app.session.set_active(1)); + if let Some(v) = app.view_mut() { + v.artboard = 1; + } + let dir = folder("export-all-active-artboards"); + let r = app.run(EXPORT_ALL, json!({"folder": dir, "format": "ai"})).unwrap(); + let opts = vectorcraft_engine::cmd::fileio::LoadOptions { native: false, ..Default::default() }; + let written = r["files"].as_array().unwrap(); + assert_eq!(written.len(), 2, "{r}"); + for (f, artboard) in written.iter().zip([2, 1]) { + let bytes = std::fs::read(f["path"].as_str().unwrap()).unwrap(); + let l = vectorcraft_engine::cmd::fileio::load_with("a.ai", &bytes, &opts).unwrap(); + assert!(l.editing_data && !l.restored, "{:?}", l.warnings); + assert_eq!(l.doc.opened_artboard.and_then(|id| l.doc.artboards.iter().position(|a| a.id == id)), Some(artboard), "{}", f["title"]); + } + let _ = std::fs::remove_dir_all(&dir); +} + #[test] fn an_unsaved_document_asks_for_a_folder() { let dir = folder("picked"); diff --git a/crates/ui-egui/src/io/save.rs b/crates/ui-egui/src/io/save.rs index 3d81aff54..98e52a2c3 100644 --- a/crates/ui-egui/src/io/save.rs +++ b/crates/ui-egui/src/io/save.rs @@ -172,7 +172,7 @@ pub(super) fn target_path_keeping( } /// Keep the active document's saved view current before a save (native files reopen at it), and -/// its active artboard ([`remember_active_artboard`]). +/// each document's active artboard ([`remember_active_artboard`]). pub fn remember_view(app: &mut VectorcraftApp) { remember_active_artboard(app); let Some(v) = app.view().copied().filter(|v| v.fitted) else { return }; @@ -181,14 +181,17 @@ pub fn remember_view(app: &mut VectorcraftApp) { } } -/// Keep the active document's active artboard current (a `.ai` file saved or exported names it -/// as its active one): the view's, an index past the last artboard being the last one as in the -/// Artboards panel; `None` without a view. +/// Keep every open document's active artboard current (a `.ai` file saved or exported names it as +/// its active one, also in Export All Open Documents): its own view's, an index past the last +/// artboard being the last one as in the Artboards panel; `None` without a view. pub fn remember_active_artboard(app: &mut VectorcraftApp) { - let index = app.view().map(|v| v.artboard); - if let Some(st) = app.session.active_mut() { - let last = st.doc.artboards.len().saturating_sub(1); - st.active_artboard = index.and_then(|i| st.doc.artboards.get(i.min(last))).map(|a| a.id); + let uids: Vec = app.session.documents().iter().map(|d| d.uid).collect(); + for uid in uids { + let index = app.view_uids.iter().position(|u| *u == uid).and_then(|k| app.views.get(k)).map(|v| v.artboard); + if let Some(st) = app.session.document_mut(uid) { + let last = st.doc.artboards.len().saturating_sub(1); + st.active_artboard = index.and_then(|i| st.doc.artboards.get(i.min(last))).map(|a| a.id); + } } }