From f4576de615c43eb39a1c3a059cce7ad8ca55acd8 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 6 Nov 2026 13:48:24 -0800 Subject: [PATCH] Artboards: the panel menu's New Artboard makes the new artboard active The panel menu's item added the artboard and left the active artboard where it was, while the panel's button, the Control bar and Properties made the new one active. All four now call one helper. With the Artboard tool, the new artboard is then the only one selected. --- crates/ui-egui/src/panels/artboards.rs | 19 ++++++--- crates/ui-egui/src/panels/artboards/tests.rs | 44 ++++++++++++++++++++ crates/ui-egui/src/panels/properties.rs | 4 +- docs/target-app-parity.md | 1 + 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/crates/ui-egui/src/panels/artboards.rs b/crates/ui-egui/src/panels/artboards.rs index 54a90ddc7..438cc6ba6 100644 --- a/crates/ui-egui/src/panels/artboards.rs +++ b/crates/ui-egui/src/panels/artboards.rs @@ -67,8 +67,8 @@ pub(crate) fn control_bar(app: &mut VectorcraftApp, ui: &mut Ui) -> bool { let Some(ab) = app.session.active().and_then(|d| d.doc.artboards.get(i)).cloned() else { return true }; let r = ab.rect; super::properties::artboard_size_row(app, ui, i, r.width(), r.height()); - if widgets::icon_button(ui, "dc-new-item", tl!("New Artboard"), false, 24.0).clicked() && app.run("artboard.new", json!({})).is_ok() { - select(app, n); + if widgets::icon_button(ui, "dc-new-item", tl!("New Artboard"), false, 24.0).clicked() { + add_new(app); } if widgets::icon_button_enabled(ui, "trash-2", tl!("Delete Artboard"), false, n > 1, 24.0).clicked() { delete_selected(app); @@ -370,6 +370,15 @@ pub(crate) fn select(app: &mut VectorcraftApp, i: usize) { } } +/// Add an artboard with the default settings (`artboard.new`) and make it the active artboard. +pub(crate) fn add_new(app: &mut VectorcraftApp) { + if let Ok(r) = app.run("artboard.new", json!({})) + && let Some(new) = r["index"].as_u64() + { + select(app, new as usize); + } +} + /// A row dragged in the list: the artboard's index. #[derive(Clone, Copy)] struct RowDrag(usize); @@ -577,8 +586,8 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { } else if new.clicked() && alt_held(ui) { // Alt-click: Artboard Options for the new artboard, which OK adds. app.run("ui.newArtboard", json!({})).ok(); - } else if new.clicked() && app.run("artboard.new", json!({})).is_ok() { - select(app, n); + } else if new.clicked() { + add_new(app); } if widgets::icon_button_enabled(ui, "trash-2", tl!("Delete Artboard"), false, n > 1 && !deletable(app, n).is_empty(), 24.0).clicked() { delete_selected(app); @@ -595,7 +604,7 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) { let n = app.session.active().map(|d| d.doc.artboards.len()).unwrap_or(0); let sel = selected(app, n); if menu_item(ui, tl!("New Artboard"), n > 0, false) { - app.run("artboard.new", json!({})).ok(); + add_new(app); } if menu_item(ui, tl!("Duplicate Artboards"), n > 0, false) { duplicate_picked(app); diff --git a/crates/ui-egui/src/panels/artboards/tests.rs b/crates/ui-egui/src/panels/artboards/tests.rs index bf2b47050..aa92c40e7 100644 --- a/crates/ui-egui/src/panels/artboards/tests.rs +++ b/crates/ui-egui/src/panels/artboards/tests.rs @@ -632,6 +632,50 @@ fn the_panel_menus_duplicate_artboards_copies_every_selected_artboard() { assert_eq!((d.artboards.len(), d.layers[0].children().unwrap().len()), (3, 2), "one undo step"); } +/// The panel menu's New Artboard adds one artboard in one undo step and makes it the active one, as +/// the panel's button does, with any tool. With the Artboard tool, the new artboard is then the only +/// one selected. +#[test] +fn the_panel_menus_new_artboard_makes_the_new_artboard_active() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 200, "height": 150, "artboards": 2})).unwrap(); + app.view_mut().unwrap().artboard = 0; + let ctx = egui::Context::default(); + let screen = Some(egui::Rect::from_min_size(egui::Pos2::ZERO, vec2(240.0, 400.0))); + let frame = |app: &mut VectorcraftApp, events: Vec| { + let mut out = ctx.run_ui(egui::RawInput { events, screen_rect: screen, ..Default::default() }, |ui| menu(app, ui)); + out.textures_delta.clear(); + out.shapes + }; + let click = |app: &mut VectorcraftApp| { + let item = frame(app, vec![]) + .iter() + .find_map(|c| match &c.shape { + egui::Shape::Text(t) if t.galley.text().trim_start() == "New Artboard" => Some(t.visual_bounding_rect().center()), + _ => None, + }) + .unwrap(); + let button = |pressed| egui::Event::PointerButton { pos: item, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() }; + frame(app, vec![egui::Event::PointerMoved(item), button(true), button(false)]); + }; + click(&mut app); + assert_eq!(boards(&app).2.len(), 3); + assert_eq!(selected(&app, 3), 2, "the new artboard is active with the Selection tool"); + let undo_steps = app.session.active().unwrap().history.undo.len(); + app.run("edit.undo", json!({})).unwrap(); + assert_eq!(boards(&app).2.len(), 2, "one undo step removes the new artboard"); + assert_eq!(app.session.active().unwrap().history.undo.len(), undo_steps - 1); + + app.select_tool("artboard"); + select(&mut app, 1); + app.session.set_tool_option("selected", &json!([1, 0])); + click(&mut app); + assert_eq!(boards(&app).2.len(), 3); + assert_eq!(app.view().unwrap().artboard, 2, "the new artboard is active with the Artboard tool"); + assert_eq!(app.session.tool_options()["active"], json!(2)); + assert_eq!(app.session.tool_options()["selected"], json!([2]), "and is the only one selected"); +} + /// The panel menu's Convert to Artboards turns the geometric bounds of each selected rectangle /// into a new artboard and removes the rectangles, in one undo step, as Object › Artboards › /// Convert to Artboards does; with nothing selected it is off. diff --git a/crates/ui-egui/src/panels/properties.rs b/crates/ui-egui/src/panels/properties.rs index 306a9323f..8f3af21b0 100644 --- a/crates/ui-egui/src/panels/properties.rs +++ b/crates/ui-egui/src/panels/properties.rs @@ -507,8 +507,8 @@ fn artboard_sections(app: &mut VectorcraftApp, ui: &mut Ui) { ui.add_space(4.0); let w = (ui.available_width() - 6.0) / 2.0; ui.horizontal(|ui| { - if widgets::flat_button(ui, tl!("New Artboard"), w).clicked() && app.run("artboard.new", json!({})).is_ok() { - super::artboards::select(app, n); + if widgets::flat_button(ui, tl!("New Artboard"), w).clicked() { + super::artboards::add_new(app); } let deletable = n > 1 && !super::artboards::deletable(app, n).is_empty(); if ui.add_enabled_ui(deletable, |ui| widgets::flat_button(ui, tl!("Delete Artboard"), w)).inner.clicked() { diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 7c87e03a8..64a9598bf 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -923,6 +923,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur Object › Artboards › Fit to Selected Art resizes the active artboard (`artboard.fitToSelection {index}`). It used to resize artboard 1 whichever artboard was active. When an action plays, its Fit to Artwork Bounds and Fit to Selected Art steps that name no artboard resize the artboard that is active then. Object › Artboards › Fit to Artwork Bounds and the Artboards panel menu's Fit to Artwork Bounds fit each selected artboard (those the Artboard tool has selected, else the active one) to the art on it, as the public [Use the artboard context menu](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/use-artboard-context-menu.html) page describes (`artboard.fitToArt {indices}`). An object partly on an artboard counts for each artboard it overlaps, and an artboard with no art on it keeps its size and place. They used to fit the active artboard to all the art in the document. The Artboards panel menu's Duplicate Artboards copies each selected artboard (those the Artboard tool has selected, else the active one) with its art, in one undo step, and the copies are then selected, as the public [Use the artboard context menu](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/use-artboard-context-menu.html) page describes. With the Artboard tool, Copy and Cut take every selected artboard with its art, as the public [Cut, copy, and paste artboards](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/cut-copy-and-paste-artboards.html) page describes, and Paste brings them all back in one undo step (`artboard.duplicate`, `artboard.copy` and `artboard.cut` with `indices`). The copies keep their places relative to each other, together right of the last artboard. All three used to act on the active artboard only. + The Artboards panel menu's New Artboard adds the artboard in one undo step and makes it the active one, as the panel's button, the Control bar and Properties do; with the Artboard tool it is then the only one selected. This is VectorCraft's own rule. The menu item used to leave the active artboard where it was. With the Artboard tool, Paste and Paste in Place make the first pasted artboard the active one (the navigator's and the Artboards panel's), and Cut makes the artboard before the first cut one active (the new first artboard when artboard 1 was cut). A command that chooses another document (`document.activate`) starts the tool on that document's active artboard, as choosing its tab does, and File › Revert keeps the active artboard and starts the tool on it. Paste used to leave the active artboard where it was while the tool had the pasted artboard active, and Cut left the active artboard on the artboard after the cut one while the tool had the one before it. With the Artboard tool, an action that reverts the document or switches to another document and back (`command.batch`), and the Actions panel menu's Batch… over a folder whose files it closes or over data sets with a Revert step (`action.batch`), keep the active artboard and start the tool on it, as File › Revert does. They used to make artboard 1 the active one. The Artboards panel menu's Convert to Artboards turns the geometric bounds of each selected object that has an area into a new artboard and removes those objects, in one undo step, as Object › Artboards › Convert to Artboards does (`artboard.convertToArtboards`). It is disabled when nothing is selected. It used to be disabled always.