From 6bce3578ca9b1511d51a08d39c3f8b4a0c0436dc Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Tue, 20 Oct 2026 23:36:06 -0700 Subject: [PATCH] Artboards: the panel menu's Convert to Artboards converts the selected objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Artboards panel menu showed Convert to Artboards disabled whatever was selected, and choosing it did nothing, although Object › Artboards › Convert to Artboards runs artboard.convertToArtboards on the selection. The panel menu's item now uses the same check as Object › Artboards › Convert to Artboards, so it is enabled when something is selected, and it runs the same command. Each selected object that has an area becomes a new artboard of its geometric bounds and is removed, in one undo step. Docs: docs/target-app-parity.md. --- crates/ui-egui/src/panels/artboards.rs | 50 +++++++++++++++++++++++++- docs/target-app-parity.md | 1 + 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/panels/artboards.rs b/crates/ui-egui/src/panels/artboards.rs index 81f5e443d..ed573df95 100644 --- a/crates/ui-egui/src/panels/artboards.rs +++ b/crates/ui-egui/src/panels/artboards.rs @@ -472,7 +472,9 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) { } menu_item(ui, tl!("Delete Empty Artboards"), false, false); ui.separator(); - menu_item(ui, tl!("Convert to Artboards"), false, false); + if menu_item(ui, tl!("Convert to Artboards"), crate::menus::enabled(app, "artboard.convertToArtboards"), false) { + crate::menus::invoke(app, "artboard.convertToArtboards", json!({})); + } if menu_item(ui, tl!("Artboard Options…"), n > 0, false) { app.run("tool.options", json!({ "tool": "artboard" })).ok(); } @@ -869,6 +871,52 @@ mod tests { assert_eq!((d.artboards.len(), d.layers[0].children().unwrap().len()), (3, 2), "one undo step"); } + /// 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. + #[test] + fn the_panel_menus_convert_to_artboards_converts_the_selected_objects() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 200, "height": 150})).unwrap(); + for x in [10, 60] { + app.session.execute("shape.rectangle", &json!({"x": x, "y": 10, "width": 20, "height": 30})).unwrap(); + } + 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 item = frame(&mut app, vec![]) + .iter() + .find_map(|c| match &c.shape { + egui::Shape::Text(t) if t.galley.text().trim_start() == "Convert to Artboards" => Some(t.visual_bounding_rect().center()), + _ => None, + }) + .unwrap(); + let button = |pressed| egui::Event::PointerButton { pos: item, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() }; + let click = |app: &mut VectorcraftApp| frame(app, vec![egui::Event::PointerMoved(item), button(true), button(false)]); + let boards = |app: &VectorcraftApp| app.session.active().unwrap().doc.artboards.iter().map(|a| a.rect).collect::>(); + let first = boards(&app)[0]; + app.session.execute("select.none", &json!({})).unwrap(); + frame(&mut app, vec![]); + click(&mut app); + // An enabled item would run the command, and its refusal would show in the status bar. + assert_eq!((boards(&app), app.ui.status.as_str()), (vec![first], ""), "off with nothing selected"); + app.session.execute("select.all", &json!({})).unwrap(); + // Draw one frame with the item enabled, as the app redraws the panel after the selection changes. + frame(&mut app, vec![]); + click(&mut app); + let rect = vectorcraft_geom::Rect::new; + assert_eq!(boards(&app), [first, rect(10.0, 10.0, 30.0, 40.0), rect(60.0, 10.0, 80.0, 40.0)]); + let d = app.session.active().unwrap(); + assert_eq!((d.doc.layers[0].children().unwrap().len(), d.selection.objects.len()), (0, 0), "the rectangles are removed"); + app.run("edit.undo", json!({})).unwrap(); + let d = &app.session.active().unwrap().doc; + assert_eq!((d.artboards.len(), d.layers[0].children().unwrap().len()), (1, 2), "one undo step"); + } + /// One headless frame of the Control bar, 2,000 points wide, with `events` → the texts painted, /// with their rects. fn bar_frame(app: &mut VectorcraftApp, ctx: &egui::Context, events: Vec) -> Vec<(String, egui::Rect)> { diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 7682c1d0c..438cc378b 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -609,6 +609,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 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. The canvas draws the active artboard's edge in black, as the public [Select artboards](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/add-edit-artboards/select-artboards.html) page describes. It draws the other artboards' edges in gray. With several artboards selected by the Artboard tool, the canvas draws the black edge around the active one, which the tool shows with handles and its name. It used to draw artboard 1's edge in black whichever artboard was active. View › Rotate View turns the view to the status bar's preset angles (`view.rotate {angle}`, which takes any angle). View › Rotate View to Selection turns the view to the angle of the selected objects' bounding box, so the box is square to the window (`view.rotateToSelection`). When the view angle changes (a Rotate View drag, the status bar's angle menu, Reset Rotate View or a saved view) and the desktop app renders the frame at the new angle on the render worker, the canvas draws the last frame turned to the new angle until that frame is ready. It used to draw no art until then.