Merge pull request #2739 from guillehr2/fix/1876-artboard-options-xy

Fix #1876: Artboard Options has X and Y with a reference point
This commit is contained in:
BFlat authored and GitHub committed 2026-10-18 01:48:09 +07:00
commit 079cd4983e
6 files changed
+203 -9

No files matched your search

+67 -3
View File
@@ -1,13 +1,77 @@
//! Artboard Options (double-click with the Artboard tool, double-click the tool, or the Artboards
//! panel's options icon or menu): the artboard's fields, sizes parsed with units.
//! panel's options icon or menu): the artboard's fields, sizes parsed with units, and its position
//! from a reference point.
use serde_json::{Value, json};
use vectorcraft_doc::Unit;
use vectorcraft_geom::{Point, Rect};
use super::{DialogSpec, run_and_close};
use super::{DialogSpec, form, run_and_close};
use crate::VectorcraftApp;
use crate::panels::transform::{artboard_rect, reference_index, reference_locator};
use crate::state::Dialog;
pub(super) const SPEC: DialogSpec = DialogSpec { heading: |_| tl!("Artboard Options").into(), confirm, ..DialogSpec::FORM };
pub(super) const SPEC: DialogSpec = DialogSpec { heading: |_| tl!("Artboard Options").into(), body, confirm, ..DialogSpec::FORM };
/// Where the fields put the artboard: `x` and `y` are its top-left corner in document points, as
/// `artboard.setProps` and `artboard.new` take them.
fn placed(d: &Dialog) -> Rect {
let (x, y) = (d.f64("x", 0.0), d.f64("y", 0.0));
Rect::new(x, y, x + d.f64("width", 0.0), y + d.f64("height", 0.0))
}
/// The form's Name, Width and Height, then X and Y in the same grid, the reference point locator
/// before X (#1876). The locator is the one the Transform panel, the Control bar and Properties
/// share, and X and Y are where its point lies on the rulers, counted from the ruler origin (#1212),
/// as the Transform panel and the Control bar show an artboard's. A new Width or Height keeps the
/// reference point where it was, as their sizes do.
fn body(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool {
let (unit, origin, refi) = (app.session.general_unit(), app.ruler_origin(), reference_index(ui));
let (was, w0, h0) = (placed(d), d.f64("width", 0.0), d.f64("height", 0.0));
form::grid_then(ui, d, unit, |ui, d| {
let (w, h) = (d.f64("width", 0.0), d.f64("height", 0.0));
let n = artboard_rect(was, refi, None, None, Some(w), Some(h), false);
// Only the side that changed moves, so an untouched coordinate stays exactly as it was.
for (key, changed, at) in [("x", w != w0, n.x0), ("y", h != h0, n.y0)] {
if changed {
d.fields.insert(key.into(), json!(at));
}
}
position_rows(ui, d, refi, unit, origin);
});
false
}
/// The X and Y rows: where reference point `refi` lies, counted from the ruler origin `origin`,
/// with the locator before X's label. A typed X or Y moves the artboard so that its reference point
/// lands there.
fn position_rows(ui: &mut egui::Ui, d: &mut Dialog, refi: usize, unit: Unit, origin: Point) {
let r = placed(d);
let rp = vectorcraft_geom::reference_point(r, refi);
let mut indent = 0.0;
for (key, label, at, zero) in [("x", "X:", rp.x, origin.x), ("y", "Y:", rp.y, origin.y)] {
ui.horizontal(|ui| {
// Y's label lines up with X's, after the locator.
if key == "x" {
let left = ui.cursor().min.x;
reference_locator(ui, refi);
indent = ui.min_rect().right() - left;
} else {
ui.add_space(indent);
}
form::row_label(ui, label);
});
if let Some(v) = crate::widgets::num_field(ui, ("dlg-ab-xy", key), Some(at - zero), unit, form::FIELD_W) {
let n = if key == "x" {
artboard_rect(r, refi, Some(v + zero), None, None, None, false).x0
} else {
artboard_rect(r, refi, None, Some(v + zero), None, None, false).y0
};
d.fields.insert(key.into(), json!(n));
}
ui.end_row();
}
}
/// The dialog kind of Artboard Options.
pub const KIND: &str = "artboardOptions";
+8 -1
View File
@@ -8,7 +8,7 @@ use crate::state::Dialog;
use crate::theme::Tokens;
/// Width of a dialog's value fields ([`text`] and [`length`], frame included).
const FIELD_W: f32 = 132.0;
pub(super) const FIELD_W: f32 = 132.0;
/// A labelled text field bound to `d.fields[key]` (one grid row).
pub(super) fn field(ui: &mut egui::Ui, d: &mut Dialog, key: &str, label: &str) {
@@ -193,6 +193,12 @@ fn dropdown(ui: &mut egui::Ui, d: &mut Dialog, key: &str, width: f32, options: &
/// hidden) in the kind's [`order`], the distances in `unit`, the [`choices`] as dropdowns and the
/// [`skew_ends`] fields as sliders.
pub(super) fn grid(ui: &mut egui::Ui, d: &mut Dialog, unit: Unit) {
grid_then(ui, d, unit, |_, _| {});
}
/// [`grid`], then the rows `more` draws in the same grid, so that their labels and fields line up
/// with the generic ones.
pub(super) fn grid_then(ui: &mut egui::Ui, d: &mut Dialog, unit: Unit, more: impl FnOnce(&mut egui::Ui, &mut Dialog)) {
let rail = Tokens::get(ui.ctx()).input_border;
let (lengths, first) = (lengths(&d.kind), order(&d.kind));
let mut keys: Vec<String> = d
@@ -237,6 +243,7 @@ pub(super) fn grid(ui: &mut egui::Ui, d: &mut Dialog, unit: Unit) {
ui.end_row();
}
}
more(ui, d);
});
}
+119 -2
View File
@@ -1,6 +1,7 @@
//! The dialog registry and the shared frame, drawn headlessly.
use serde_json::json;
use vectorcraft_doc::Unit;
use vectorcraft_engine::Session;
use super::*;
@@ -1018,6 +1019,111 @@ fn artboard_options_position_follows_the_tool_option_with_another_tool() {
}
}
/// The strings a frame painted, each with its rectangle.
fn painted(out: &egui::FullOutput) -> Vec<(String, egui::Rect)> {
fn walk(s: &egui::Shape, v: &mut Vec<(String, egui::Rect)>) {
match s {
egui::Shape::Text(t) => v.push((t.galley.text().to_string(), egui::Rect::from_min_size(t.pos, t.galley.size()))),
egui::Shape::Vec(s) => s.iter().for_each(|s| walk(s, v)),
_ => {}
}
}
let mut v = vec![];
out.shapes.iter().for_each(|c| walk(&c.shape, &mut v));
v
}
/// Where to click in the field after the label `label` among the `shown` strings.
fn field_after(shown: &[(String, egui::Rect)], label: &str) -> egui::Pos2 {
let r = shown.iter().find(|(t, _)| t == label).unwrap_or_else(|| panic!("no {label:?} in {shown:?}")).1;
egui::pos2(r.right() + 40.0, r.center().y)
}
/// A 400 × 300 pt artboard at the page's origin with the ruler origin at (120, 50), and its
/// Artboard Options open.
fn artboard_options_from_a_ruler_origin() -> VectorcraftApp {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.new", json!({"width": 400, "height": 300})).unwrap();
app.run("document.setRulerOrigin", json!({"x": 120, "y": 50})).unwrap();
assert_eq!(app.ruler_origin(), vectorcraft_geom::Point::new(120.0, 50.0));
app.run("tool.options", json!({"tool": "artboard"})).unwrap();
app
}
/// #1876: Artboard Options has X and Y beside the reference point locator, counted from the ruler
/// origin (#1212) as the Transform panel's are. The artboard moves to a typed X and Y, its reference
/// point landing there, in one undo step. The shape dialogs still show no X and Y (theirs are where
/// the click was).
#[test]
fn artboard_options_x_and_y_count_from_the_ruler_origin_at_the_reference_point() {
let mut app = artboard_options_from_a_ruler_origin();
let before = app.session.doc().unwrap().doc.clone();
let undo = app.session.doc().unwrap().history.undo.len();
let mut f = Frames::new();
let has = |shown: &[(String, egui::Rect)], s: &str| shown.iter().any(|(t, _)| t == s);
// The center by default: (200, 150) on the page, 80 and 100 from the ruler origin.
let shown = f.texts(&mut app);
for s in ["X:".to_string(), "Y:".to_string(), Unit::Points.format(80.0), Unit::Points.format(100.0)] {
assert!(has(&shown, &s), "{s} in {shown:?}");
}
// The bottom-right corner: (400, 300) on the page, 280 and 250 from the ruler origin.
let corner = f.control(&mut app, "Bottom Right", egui::accesskit::Role::RadioButton);
f.press(&mut app, corner, true);
f.press(&mut app, corner, false);
let shown = f.texts(&mut app);
for v in [280.0, 250.0] {
assert!(has(&shown, &Unit::Points.format(v)), "{v} in {shown:?}");
}
// Typing X 300 and Y 0 puts that corner at (420, 50) on the page.
for (label, typed) in [("X:", "300"), ("Y:", "0")] {
let at = field_after(&shown, label);
f.press(&mut app, at, true);
f.press(&mut app, at, false);
f.run(&mut app, vec![egui::Event::Text(typed.into())]);
}
f.key(&mut app, egui::Key::Enter, egui::Modifiers::NONE);
assert!(app.ui.dialog.is_none(), "Enter applies the dialog");
assert_eq!(app.session.doc().unwrap().doc.artboards[0].rect, vectorcraft_geom::Rect::new(20.0, -250.0, 420.0, 50.0));
assert_eq!(app.session.doc().unwrap().history.undo.len(), undo + 1, "one undo step");
app.run("edit.undo", json!({})).unwrap();
assert_eq!(app.session.doc().unwrap().doc, before);
open(&mut app, "rectangle", json!({"x": 10, "y": 20, "width": 30, "height": 40}));
let shown = f.texts(&mut app);
assert!(has(&shown, "Width:") && !has(&shown, "X:") && !has(&shown, "Y:"), "{shown:?}");
}
/// #1876: a new Width or Height in Artboard Options keeps the reference point where it was, as the
/// Transform panel's does: about the center by default, so X and Y still read the same, and from
/// the top-left corner with that reference point.
#[test]
fn artboard_options_size_keeps_the_reference_point() {
for (point, want) in
[(None, vectorcraft_geom::Rect::new(-50.0, 0.0, 450.0, 300.0)), (Some("Top Left"), vectorcraft_geom::Rect::new(0.0, 0.0, 500.0, 300.0))]
{
let mut app = artboard_options_from_a_ruler_origin();
let mut f = Frames::new();
if let Some(point) = point {
let at = f.control(&mut app, point, egui::accesskit::Role::RadioButton);
f.press(&mut app, at, true);
f.press(&mut app, at, false);
}
let shown = f.texts(&mut app);
let (width, height) = (field_after(&shown, "Width:"), field_after(&shown, "Height:"));
f.press(&mut app, width, true);
f.press(&mut app, width, false);
f.run(&mut app, vec![egui::Event::Text("500".into())]);
// A click on Height ends the Width field's editing; X reads as before.
f.press(&mut app, height, true);
f.press(&mut app, height, false);
let shown = f.texts(&mut app);
let x = Unit::Points.format(if point.is_some() { -120.0 } else { 80.0 });
assert!(shown.iter().any(|(t, _)| *t == x), "{x} in {shown:?}");
f.key(&mut app, egui::Key::Enter, egui::Modifiers::NONE);
assert!(app.ui.dialog.is_none(), "Enter applies the dialog");
assert_eq!(app.session.doc().unwrap().doc.artboards[0].rect, want, "{point:?}");
}
}
/// Zig Zag's dialog has Size and Ridges per segment sliders, and Relative / Absolute and Smooth /
/// Corner as radio buttons, not dropdowns (#1133).
#[test]
@@ -1058,13 +1164,24 @@ impl Frames {
out
}
/// The strings the dialog layer paints once it has settled, each with its rectangle.
fn texts(&mut self, app: &mut VectorcraftApp) -> Vec<(String, egui::Rect)> {
self.run(app, vec![]);
painted(&self.run(app, vec![]))
}
/// The center of the button that screen readers name `label`.
fn button(&mut self, app: &mut VectorcraftApp, label: &str) -> egui::Pos2 {
self.control(app, label, egui::accesskit::Role::Button)
}
/// The center of the control of `role` that screen readers name `label`.
fn control(&mut self, app: &mut VectorcraftApp, label: &str, role: egui::accesskit::Role) -> egui::Pos2 {
self.run(app, vec![]);
let out = self.run(app, vec![]);
let nodes = &out.platform_output.accesskit_update.as_ref().expect("an accessibility tree").nodes;
let node = nodes.iter().find(|(_, n)| n.label() == Some(label) && n.role() == egui::accesskit::Role::Button);
let b = node.unwrap_or_else(|| panic!("no button named {label:?}")).1.bounds().expect("bounds");
let node = nodes.iter().find(|(_, n)| n.label() == Some(label) && n.role() == role);
let b = node.unwrap_or_else(|| panic!("no {role:?} named {label:?}")).1.bounds().expect("bounds");
egui::pos2((b.x0 + b.x1) as f32 * 0.5, (b.y0 + b.y1) as f32 * 0.5)
}
+3 -1
View File
@@ -3858,7 +3858,9 @@ desktop app. The Flare tool's are its Flare Tool Options (dialog `flareOptions`)
`direction` (°), and `raysOn` and `ringsOn` (the Rays and Rings checkboxes; off, the flare has none). OK keeps them
(`tool.setOption {tool: "flare", values}`) and the next flare dragged out uses them; a click with the tool opens the
same dialog, and its OK also draws a flare there. The Artboard tool's are the active artboard's Artboard Options
(dialog `artboardOptions`; the Artboard tool's active artboard while it is in use, else the window's), a graph tool's
(dialog `artboardOptions`; the Artboard tool's active artboard while it is in use, else the window's; its `x` and `y`
are the artboard's top-left corner in document points, while the dialog shows X and Y at the reference point the
Transform panel uses, counted from the ruler origin), a graph tool's
the selected graph's Graph Type (an error without one), a Symbolism tool's the Symbolism Tools Options (dialog
`symbolismOptions`: `diameter` in points, `intensity` and `density` 1–10, one brush for the eight tools) and the
Magic Wand tool's its panel. `ui.newArtboard` (Alt-click on the Artboards panel's New Artboard) opens Artboard Options
+3 -1
View File
@@ -1,6 +1,6 @@
# Parity with Adobe Illustrator
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (Image Trace: Auto Color keeps rare but distinct colours; Image Trace: large images trace with a warning; anchor limit raised to 8,000,000; Image Trace: Raster::new returns an error for a buffer of the wrong length; M12 maximal-disc graph pruning and junction merging measured; M12 crop EDT and Guo–Hall thinning measured; M12 centre-lines baseline measured; scores and hours unchanged; installed plug-ins stay installed after a restart, #2541; Group Selection picks and moves one path of a compound path, #2441 in part; the Move dialog opens on the last move and its last checkboxes; Image Trace: a higher Paths never gives fewer segments on collapsed loops; Image Trace: the notch rule reads only its own piece; Image Trace: an edge's sub-pixel shift is the same from either side; Image Trace Flat Logo traces line art on white paper instead of a blank result; score and hours unchanged; Image Trace: a simpler contour walk with no precomputed direction table; transparency grid visibility saved in native documents, #1968; MCP cancellation, progress and busy answers during long exports, #2165, #2171, #2172; EPS editing data: legacy mesh corners, vector opacity masks and compound member paint retained; raster page comparison filters minification without relaxing limits; scores and hours unchanged; native family-name fallback from English-only typographic names, #2208; scores unchanged; batch exporting, #1935; G2: Effect › Sketch › Halftone Pattern; Photoshop filters 30/57, 27 remaining (22–38 h); G5: Symbol Options Export Type (Graphic / Movie Clip); Localization: Brazilian Portuguese complete under the tests, #1914, 71% → 74%; G6 image variables: Automation ~60% → ~62% / detail ~65% → ~68%, hours lowered; Warp Options controls verified by focused checks and original-image review; estimates unchanged; G11: Asset Export Art Optimized seam (#983 / #1037) and supersampled thumbs; Export for Screens 85% → 90%; existing-guide Escape cancellation validated 2026-10-14; scores and hours unchanged; open-path anchor Delete keeps surviving runs separate; estimates unchanged; PDF.R1: shared PDF reader with windowed content scanning and explicit incomplete-content errors; scores and estimates unchanged; PDF.W1: guarded shared PDF writer; output behavior, scores and estimates unchanged; Artboard tab-switch follow-up historically validated on `a0671777`; scores and hours unchanged; Artboard clean active-tab close/Delete follow-up validated 2026-10-15; later 2026-10-14 merges: anchor X/Y/W/H, #1824; Snap to Grid over Snap to Pixel, #1816; toolbar and All Tools, #1751; Actions panel selection, #1629 in part; redo during a drag, #1719; preferences written on OK, #1747; type in symbols after reopening, #1738; macOS save sheets, #1855, the Format menu withdrawn; stroke outlines shared, #1667; untrusted files, #1852, #1862–#1873; failed-save dialog, #1685; effects, #1864, Dim Images, #1683; duplicated rows of the 2026-10-05 table noted for the owner; scores unchanged; Image Trace: Shapes rework, stroke caps, enhanced presets, gradients over noise, measured in `image-trace-quality.md`, score unchanged; verified PngSuite downloads, #1729; synthetic control-input budgets, #1709; 2026-10-14 merges: anchor Align and Distribute, #1636; Direct Selection and Delete fixes, #1674, #1666, #1743; Escape while typing, #1686; Pen clicks, #1663; Expand on repeats, #1756; Roughen and Scribble, #1643, #1648; file and input hardening, #1776–#1783, #1786, #1807; MCP limits and envelope, #1727, #1728; Image Trace levelling and Flat Logo on scans, #1647, #1657; scores unchanged; later 2026-10-13 merges: SVG import, #1565, #1596, #1568, #1598, #1601; type on a path from PDF and `.ai`, #1585, #1586; ICC tag alignment, #1584; saves, #1620 in part; Warp Arc, #1578; Zig Zag, #1558; Pathfinder strokes, #1570; OpenType features, #1607; intersect Smart Guides, #1602 in part; a guide's position in the Transform panel, #1597 in part, a convenience with no parity claim; scores unchanged; TIFF is available in Export for Screens and Asset Export with shared format settings; scores and hours unchanged; G1/M11: Artboard deletion and history selection normalization; history regressions and full workspace CI pass; scores and hours unchanged; Character styles retain explicit Auto leading, propagate resets and preserve local leading overrides; scores and hours unchanged; Width Point Edit: Shift-double-click enables adjoining adjustment; plain double-click keeps it off; scores and hours unchanged; Units › Identify Objects By (`identifyObjectsBy`) acts on Layers, Variables and Layer Options, #394, scores unchanged; Appearance of Black › Printing / Exporting (`blackOutput`) acts on RGB raster export and Print as Bitmap, #394, scores unchanged; per-corner radius fields with a link toggle, split from #1594; control transport deadlines, #1726; Color panel: the mode picked last cLine truncated
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (Artboard Options' X and Y with a reference point, #1876; Image Trace: Auto Color keeps rare but distinct colours; Image Trace: large images trace with a warning; anchor limit raised to 8,000,000; Image Trace: Raster::new returns an error for a buffer of the wrong length; M12 maximal-disc graph pruning and junction merging measured; M12 crop EDT and Guo–Hall thinning measured; M12 centre-lines baseline measured; scores and hours unchanged; installed plug-ins stay installed after a restart, #2541; Group Selection picks and moves one path of a compound path, #2441 in part; the Move dialog opens on the last move and its last checkboxes; Image Trace: a higher Paths never gives fewer segments on collapsed loops; Image Trace: the notch rule reads only its own piece; Image Trace: an edge's sub-pixel shift is the same from either side; Image Trace Flat Logo traces line art on white paper instead of a blank result; score and hours unchanged; Image Trace: a simpler contour walk with no precomputed direction table; transparency grid visibility saved in native documents, #1968; MCP cancellation, progress and busy answers during long exports, #2165, #2171, #2172; EPS editing data: legacy mesh corners, vector opacity masks and compound member paint retained; raster page comparison filters minification without relaxing limits; scores and hours unchanged; native family-name fallback from English-only typographic names, #2208; scores unchanged; batch exporting, #1935; G2: Effect › Sketch › Halftone Pattern; Photoshop filters 30/57, 27 remaining (22–38 h); G5: Symbol Options Export Type (Graphic / Movie Clip); Localization: Brazilian Portuguese complete under the tests, #1914, 71% → 74%; G6 image variables: Automation ~60% → ~62% / detail ~65% → ~68%, hours lowered; Warp Options controls verified by focused checks and original-image review; estimates unchanged; G11: Asset Export Art Optimized seam (#983 / #1037) and supersampled thumbs; Export for Screens 85% → 90%; existing-guide Escape cancellation validated 2026-10-14; scores and hours unchanged; open-path anchor Delete keeps surviving runs separate; estimates unchanged; PDF.R1: shared PDF reader with windowed content scanning and explicit incomplete-content errors; scores and estimates unchanged; PDF.W1: guarded shared PDF writer; output behavior, scores and estimates unchanged; Artboard tab-switch follow-up historically validated on `a0671777`; scores and hours unchanged; Artboard clean active-tab close/Delete follow-up validated 2026-10-15; later 2026-10-14 merges: anchor X/Y/W/H, #1824; Snap to Grid over Snap to Pixel, #1816; toolbar and All Tools, #1751; Actions panel selection, #1629 in part; redo during a drag, #1719; preferences written on OK, #1747; type in symbols after reopening, #1738; macOS save sheets, #1855, the Format menu withdrawn; stroke outlines shared, #1667; untrusted files, #1852, #1862–#1873; failed-save dialog, #1685; effects, #1864, Dim Images, #1683; duplicated rows of the 2026-10-05 table noted for the owner; scores unchanged; Image Trace: Shapes rework, stroke caps, enhanced presets, gradients over noise, measured in `image-trace-quality.md`, score unchanged; verified PngSuite downloads, #1729; synthetic control-input budgets, #1709; 2026-10-14 merges: anchor Align and Distribute, #1636; Direct Selection and Delete fixes, #1674, #1666, #1743; Escape while typing, #1686; Pen clicks, #1663; Expand on repeats, #1756; Roughen and Scribble, #1643, #1648; file and input hardening, #1776–#1783, #1786, #1807; MCP limits and envelope, #1727, #1728; Image Trace levelling and Flat Logo on scans, #1647, #1657; scores unchanged; later 2026-10-13 merges: SVG import, #1565, #1596, #1568, #1598, #1601; type on a path from PDF and `.ai`, #1585, #1586; ICC tag alignment, #1584; saves, #1620 in part; Warp Arc, #1578; Zig Zag, #1558; Pathfinder strokes, #1570; OpenType features, #1607; intersect Smart Guides, #1602 in part; a guide's position in the Transform panel, #1597 in part, a convenience with no parity claim; scores unchanged; TIFF is available in Export for Screens and Asset Export with shared format settings; scores and hours unchanged; G1/M11: Artboard deletion and history selection normalization; history regressions and full workspace CI pass; scores and hours unchanged; Character styles retain explicit Auto leading, propagate resets and preserve local leading overrides; scores and hours unchanged; Width Point Edit: Shift-double-click enables adjoining adjustment; plain double-click keeps it off; scores and hours unchanged; Units › Identify Objects By (`identifyObjectsBy`) acts on Layers, Variables and Layer Options, #394, scores unchanged; Appearance of Black › Printing / Exporting (`blackOutput`) acts on RGB raster export and Print as Bitmap, #394, scores unchanged; per-corner radius fields with a link toggle, split from #1594; control transpLine truncated
The authoritative parity assessment. [`ROADMAP.md`](../ROADMAP.md) summarizes it; [`gaps.md`](gaps.md) is
the work list made from it. Deep areas have their own checklists: [UI and interaction](ui-parity.md),
@@ -560,6 +560,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
Choosing the Artboard tool (Shift+O, its toolbar button, or Edit Artboards in Properties or Document Setup) starts it on the active artboard: the canvas outlines that artboard, and Delete, Cut, Copy, the Transform panel and Artboard Options act on it.
With the Artboard tool in use, a Shift-click on a row of the Artboards panel selects the artboards from the last row clicked without Shift to the clicked row, and the clicked row's artboard becomes the active one. When the artboard of the last row clicked without Shift is no longer selected, the range starts from the active artboard, and later Shift-clicks start from there too. A Ctrl/Cmd-click still adds or removes one artboard. A Shift-click used to add or remove one artboard as well. The public [Select artboards](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/add-edit-artboards/select-artboards.html) page lists Shift-click in the Artboards panel for selecting several artboards.
In the Artboards panel, a click on the active artboard's options icon, or a double-click on another row's icon, opens Artboard Options for that row's artboard, and the panel menu's Artboard Options… opens it for the active artboard (part of #1876). Both used to choose the Artboard tool instead. Opened while another tool is in use, the dialog moves the art with a new X or Y when the Artboard tool's Move Artwork with Artboard option is on.
Artboard Options has X and Y fields beside a reference point locator (#1876), so an artboard's position can be typed as well as its size. X and Y are where the reference point lies, counted from the ruler origin (#1212), as the Transform panel and the Control bar show an artboard's position, and the locator is the one they and Properties share. A typed X or Y moves the artboard so that its reference point lands there, and a new Width or Height keeps the reference point in place; OK applies everything in one undo step. The shape dialogs still keep their X and Y to themselves (the click position). Pointer and key tests in `dialogs/tests.rs` cover both. Public references: [Resize artboards](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/add-edit-artboards/resize-artboards.html) and [Create and add new artboards](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/add-edit-artboards/add-new-artboards.html) (a reference point in a nine-point grid, then X and Y).
Alt-clicking New Artboard in the Artboards panel opens Artboard Options for the new artboard, and OK adds it and makes it the active artboard (`ui.newArtboard`; behavior reference: [Default keyboard shortcuts](https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html), Use panels). A plain click still adds the artboard at once.
With Align to Artboard chosen, the Align panel's Distribute Objects and Distribute Spacing buttons use the active artboard (`object.distribute` and `object.distributeSpacing` with `to: "artboard"` and `artboard`). The first object's left or top edge goes to the artboard's left or top edge, and the last object's right or bottom edge goes to the artboard's right or bottom edge. Distribute Objects spaces the other objects evenly by its button's line, and two objects are enough. Distribute Spacing makes the gaps between the objects equal. With anchor points selected, the buttons space the points among themselves and do not use the artboard. When an action plays, its Distribute Objects and Distribute Spacing steps with Align to Artboard use the artboard that is active then.
With a key object, the Align panel's Distribute Objects buttons use the Distribute Spacing value (`object.distribute {spacing}`): the key object, or the compound path or selected group that contains it, stays in place, and the objects' left edges, centers or right edges (top edges, centers or bottom edges) end up that far apart, outward from the key object's, in their order. With the field's default value of 0, a click puts every object on the key object's line (behavior reference: [Ask Mordy: Distributing Objects](http://rwillustrator.blogspot.com/2008/01/ask-mordy-distribute.html)).
@@ -770,6 +771,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
| Date | Change | Summary |
|---|---|---|
| 2026-10-17 | minor | Evidence: Artboard Options has X and Y beside a reference point locator, counted from the ruler origin (#1876); scores unchanged |
| 2026-10-17 | minor | Image Trace: Auto Color keeps rare but distinct colours drawn as regions of their own (stems, highlights, near-white on pale backgrounds), at most six, protected from mixed-colour merging and Noise: every key colour on 66 of 66 flat test logos (was 39); scans unchanged. Scores and hours unchanged. |
| 2026-10-17 | minor | Image Trace: images over 1500 px on a side trace normally with a warning that they are slower (as the public documentation describes), images over 64 MP say they were scaled down, and the memory guard is 8,000,000 anchors (2,000,000 on the web) instead of 2,000,000, measured at about 90 bytes an anchor. Scores and hours unchanged. |
| 2026-10-17 | minor | Image Trace: `Raster::new` returns an error for RGBA bytes of the wrong length instead of failing an assertion, and `Raster::pixel` reads nothing past the raster; no behaviour change for valid images. Scores and hours unchanged. |
+3 -1
View File
@@ -1,6 +1,6 @@
# UI and interaction parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (library panels: one per library, dockable and tabbable, with Persistent, #2603; the Selection tool threads text from the out port, #2572 in part; scores unchanged; Group Selection picks and moves one path of a compound path, #2441 in part; the Move dialog opens on the last move and its last checkboxes; scores unchanged; transparency grid visibility saved in native documents, #1968; Live Corners radius up to the neighbouring cuts, #2332; Image Trace quick presets: wording corrected to the shipped behaviour, the Image Trace button opens the controls; batch exporting, #1935; document window scroll bars, #2322; Anchor Point tool: a click on a handle end retracts that handle only, #2267; scores unchanged; Smart Guides: the dragged end of a straight segment keeps to its extension at any angle, #2120; Warp Options controls verified by focused checks and original-image review; estimates unchanged; existing-guide Escape cancellation validated 2026-10-14; scores and hours unchanged; open-path anchor Delete keeps surviving runs separate; estimates unchanged; Artboard tab-switch follow-up historically validated on `a0671777`; scores and hours unchanged; Artboard clean active-tab close/Delete follow-up validated 2026-10-15; Curvature curve geometry, #1236 in part; Width-point finite-arithmetic follow-up validated; scores and hours unchanged; Image Trace adjustment discovery; later 2026-10-14 merges: macOS save sheet Format menu back to missing, #1855, #866 reopened; anchor X/Y/W/H in the Transform panel, Properties and Control bar, #1824; Snap to Grid over Snap to Pixel, #1816; toolbar labels, columns and the All Tools drawer, #1751; Actions panel selection, #1629 in part; scores unchanged; 2026-10-14 merges: anchor Align and Distribute, #1636; Delete on closed paths and whole objects, #1674; selection inside isolation, #1666; Escape in a Direct Selection drag and while typing, #1743, #1686; Pen clicks under 3 px, #1663; [ and ] on more tools, #1661 in part; fields commit on click away, #1787; command search keys, #1757; a guide's position in the Transform panel is a convenience with no parity claim, #1659; scores unchanged; intersect Smart Guides for drawn points and dragged anchors, #1602 in part; a ruler guide's position in the Transform panel, #1597 in part; Symbol Options before New Symbol, #1560; tool flyout widths, #1616; Pathfinder Trim, Merge and Crop strokes, #1570; scores unchanged; G1/M11: Artboard deletion and history selection normalization; history regressions and full workspace CI pass; scores and hours unchanged; Width Point Edit: Shift-double-click enables adjoining adjustment; plain double-click keeps it off; scores and hours unchanged; per-corner radius fields with a link toggle, split from #1594; Join merges anchors lying on one another, #1533; correction: Average and Join with a corner or smooth choice is missing, not done; guide objects take the guide colour and style and Lock Guides, #1534; Keyboard Shortcuts lists commands outside the menus, #1539; round corners next to curves and the Smooth tool's reach, #1512; Make Compound Path on a group, #1537; dialog slider rails, #1483; template layers stay in Preview in Outline view, #1486; Direct Selection takes the selected object's anchor where anchors coincide, #1508; Tabs panel stop drags, #1458; Split Into Grid dialog, #1462; Transform Objects and Reflect's angle, #1448 and #1460; Pathfinder panel on a group, #1450; value fields: readable selection, #1465 in part, and a focused field follows its value, #1457; scores unchanged; Transform dialogs have a live Preview and step with the arrow keys, #1412; transform tools, dialogs and Move act on direct-selected anchors, #1408; the bounding box hides during a corner drag, #1229; scores unchanged; dialogs focus their first field on every open, #1367, and a first field that starts greyed out once it is enabled, #1395; scores unchanged; Rectangular Grid and Polar Grid click dialogs take the skew, frame, compound path and Fill Grid options, #1377 in part, score unchanged; Live Corners: a Direct Selection click on a widget selects its corner, #1239; custom ruler origin, #1212; Artboard tool clicks fit the topmost editable object in one undo step (#1227); scores unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; Document Setup: Edit Artboards closes the modal after applying valid settings; pointer-click regressions; scores and hours unchanged; Artboards: Escape exits editing; an active drag cancels first; shortcut-routing regression; scores and hours unchanged; Simplify preview/counts and valid rewritten-path selection (#1272); parity remains partial; Texture-effect drag redraws optimized (#1257); idle Windows interaction still unverified; scores unchanged; Curvature point display improved (#1236, in part); scores unchanged; Direct Selection: Line truncated
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (Artboard Options' X and Y with a reference point, #1876; scores unchanged; library panels: one per library, dockable and tabbable, with Persistent, #2603; the Selection tool threads text from the out port, #2572 in part; scores unchanged; Group Selection picks and moves one path of a compound path, #2441 in part; the Move dialog opens on the last move and its last checkboxes; scores unchanged; transparency grid visibility saved in native documents, #1968; Live Corners radius up to the neighbouring cuts, #2332; Image Trace quick presets: wording corrected to the shipped behaviour, the Image Trace button opens the controls; batch exporting, #1935; document window scroll bars, #2322; Anchor Point tool: a click on a handle end retracts that handle only, #2267; scores unchanged; Smart Guides: the dragged end of a straight segment keeps to its extension at any angle, #2120; Warp Options controls verified by focused checks and original-image review; estimates unchanged; existing-guide Escape cancellation validated 2026-10-14; scores and hours unchanged; open-path anchor Delete keeps surviving runs separate; estimates unchanged; Artboard tab-switch follow-up historically validated on `a0671777`; scores and hours unchanged; Artboard clean active-tab close/Delete follow-up validated 2026-10-15; Curvature curve geometry, #1236 in part; Width-point finite-arithmetic follow-up validated; scores and hours unchanged; Image Trace adjustment discovery; later 2026-10-14 merges: macOS save sheet Format menu back to missing, #1855, #866 reopened; anchor X/Y/W/H in the Transform panel, Properties and Control bar, #1824; Snap to Grid over Snap to Pixel, #1816; toolbar labels, columns and the All Tools drawer, #1751; Actions panel selection, #1629 in part; scores unchanged; 2026-10-14 merges: anchor Align and Distribute, #1636; Delete on closed paths and whole objects, #1674; selection inside isolation, #1666; Escape in a Direct Selection drag and while typing, #1743, #1686; Pen clicks under 3 px, #1663; [ and ] on more tools, #1661 in part; fields commit on click away, #1787; command search keys, #1757; a guide's position in the Transform panel is a convenience with no parity claim, #1659; scores unchanged; intersect Smart Guides for drawn points and dragged anchors, #1602 in part; a ruler guide's position in the Transform panel, #1597 in part; Symbol Options before New Symbol, #1560; tool flyout widths, #1616; Pathfinder Trim, Merge and Crop strokes, #1570; scores unchanged; G1/M11: Artboard deletion and history selection normalization; history regressions and full workspace CI pass; scores and hours unchanged; Width Point Edit: Shift-double-click enables adjoining adjustment; plain double-click keeps it off; scores and hours unchanged; per-corner radius fields with a link toggle, split from #1594; Join merges anchors lying on one another, #1533; correction: Average and Join with a corner or smooth choice is missing, not done; guide objects take the guide colour and style and Lock Guides, #1534; Keyboard Shortcuts lists commands outside the menus, #1539; round corners next to curves and the Smooth tool's reach, #1512; Make Compound Path on a group, #1537; dialog slider rails, #1483; template layers stay in Preview in Outline view, #1486; Direct Selection takes the selected object's anchor where anchors coincide, #1508; Tabs panel stop drags, #1458; Split Into Grid dialog, #1462; Transform Objects and Reflect's angle, #1448 and #1460; Pathfinder panel on a group, #1450; value fields: readable selection, #1465 in part, and a focused field follows its value, #1457; scores unchanged; Transform dialogs have a live Preview and step with the arrow keys, #1412; transform tools, dialogs and Move act on direct-selected anchors, #1408; the bounding box hides during a corner drag, #1229; scores unchanged; dialogs focus their first field on every open, #1367, and a first field that starts greyed out once it is enabled, #1395; scores unchanged; Rectangular Grid and Polar Grid click dialogs take the skew, frame, compound path and Fill Grid options, #1377 in part, score unchanged; Live Corners: a Direct Selection click on a widget selects its corner, #1239; custom ruler origin, #1212; Artboard tool clicks fit the topmost editable object in one undo step (#1227); scores unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; Document Setup: Edit Artboards closes the modal after applying valid settings; pointer-click regressions; scores and hours unchanged; Artboards: Escape exits editing; an active drag cancels first; shortcut-routing regression; scores and hours unchanged; Simplify preview/counts and valid rewritten-path selection (#1272); parity remains partial; Texture-effect drag redraws optimized (#1257); idle Windows interaction still unverified; scores unchanged; Curvature poLine truncated
How VectorCraft's tools, handles, snapping and panels behave next to Illustrator's. A vector illustrator's
trust is in the details: where a handle lands, what Shift and Alt do mid-drag, what a smart guide snaps to,
@@ -175,6 +175,7 @@ tools, never checked side by side) isn't captured row by row.
| Artboard navigator synchronizes the active tool selection before deletion | done | Actual status-bar Next → Delete/Backspace and Next → Properties Delete regressions in `chrome.rs` cover surviving board records, preserved artwork and Undo/Redo; other tools keep object selection and history. **Tab-switch follow-up:** document-tab changes align the Artboard tool with the displayed board. Actual A → B → A tab clicks followed by Properties Delete and controls for same-tab multiselection, Move/Scale Artwork options and other tools passed, along with historical full CI on `a0671777`. [Validation evidence](target-app-parity.md#evidence-what-ships-today-by-area). **Clean-tab close:** with the Artboard tool active, closing a clean active tab restores the surviving document's displayed board before Properties Delete. |
| Artboard history prunes removed indices while preserving valid multiselection and tool options | tested | Headless pointer/key tests in `panels/properties.rs` cover panel New → Undo → Delete/Backspace/Properties, Duplicate → Undo → Delete, partial pruning and valid multiselection. They check surviving board records, artwork, object selection, Move/Scale Artwork options, saved state and Undo/Redo. Full workspace CI passes. |
| Click an object with the Artboard tool to create an artboard fitted to its visible bounds | done | Topmost editable object or group; drag and modified clicks retain their meaning; one undo step (`artboard_click.rs`, #1227) |
| Artboard Options: X and Y beside a reference point locator, counted from the ruler origin; a typed X or Y moves the artboard's reference point there, a new Width or Height keeps it in place | done | #1876; as the Transform panel's and the Control bar's artboard X and Y, with the locator they and Properties share; the shape dialogs' X and Y (the click position) stay hidden. Pointer and key tests in `dialogs/tests.rs` |
| Shape tools: Shift constrains, Alt from centre, Space moves while dragging, arrows change sides/points/rows | done | |
| Tool double-click opens its options | done | |
| Per-platform mapping (Cmd ↔ Ctrl, Option ↔ Alt) | done | |
@@ -243,6 +244,7 @@ re-measure of the panels added since. Slider rails show on Medium Dark (#1808, #
| Date | Change | Summary |
|---|---|---|
| 2026-10-17 | minor | Artboard Options has X and Y beside a reference point locator, counted from the ruler origin as the Transform panel's are; a typed X or Y moves the artboard's reference point there and a new Width or Height keeps it in place (#1876); scores unchanged (estimated) |
| 2026-10-17 | minor | Library panels (#2603): each swatch or graphic style library opens in its own dockable, tabbable panel, several at once; the panel menu's Persistent reopens a panel at startup with its view and find field, a library loaded from a file read again from its path; scores unchanged (estimated) |
| 2026-10-17 | minor | Selection tool: clicking the red out port of overflowing area type, then a closed path, empty canvas or a dragged rectangle threads the text there (#2572 in part); Escape cancels. Scores and hours unchanged. |
| 2026-10-17 | minor | Group Selection picks one path of a compound path (the outer shape or a hole) and moves it on its own; a second click adds the compound path (#2441, in part) |