Artboards: Artboard Options' Show Center Mark and Show Cross Hairs draw on the canvas

The Artboard Options help pages describe two display options
(https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/modify-display-settings-of-artboards.html,
.../set-video-display-options-for-artboards.html): a mark at the artboard's
center, and lines from the middle of each side through the center. The
document already had show_center_mark and show_cross_hairs on Artboard, but
nothing set or drew them.

artboard.setProps (each `artboards` entry too) and artboard.new take
showCenterMark and showCrossHairs as true/false, in the same single undo
step as the other fields; a locked artboard can still change them.
Artboard Options has both checkboxes, filled in from the artboard whether
it opens from the panel or a double-click with the Artboard tool, and its
New Artboard form passes them to artboard.new. The canvas draws them in the
new theme token artboard_mark, 1 point wide on screen at any zoom: a cross
12 points across at the center whose arms follow the artboard's sides, and
the two cross hairs. They are screen-only like the edges; exports and print
never read the fields. inspect_document reports them when on. Saved files
are unchanged: the two keys were already always written, so a document that
never shows them saves byte for byte as before, and a file without the keys
opens with both off. Video safe areas, video rulers and the pixel aspect
ratio are not part of this change.

Verification: artboard_display_marks_are_set_and_cleared_in_one_undo_step,
artboard_display_marks_round_trip_through_the_native_format,
artboard_display_marks_stay_out_of_exports (SVG and PDF identical with the
marks on and off), an_artboard_shows_its_center_mark_and_cross_hairs
(positions, screen-constant size under zoom, rotated view) and
artboard_options_show_center_mark_and_cross_hairs. cargo xtask ci: all 10
steps passed.
This commit is contained in:
Taha-Mahmoodi
2026-10-23 09:42:09 +07:00
committed by bflatastic
parent 08cfb45561
commit 64f6d2db50
39 changed files with 384 additions and 27 deletions
+103
View File
@@ -828,6 +828,109 @@ fn artboard_lock_round_trips_through_the_native_format() {
assert_eq!(save(&s), before);
}
/// Artboard Options' Show Center Mark and Show Cross Hairs: `artboard.setProps {showCenterMark,
/// showCrossHairs}` sets and clears each on that artboard only, in one undo step, also for a
/// locked artboard and in an `artboards` list. A change without them leaves them, a duplicate
/// keeps them, `artboard.new` takes them, `inspect_document` reports them, and a value that isn't
/// true or false is refused.
#[test]
fn artboard_display_marks_are_set_and_cleared_in_one_undo_step() {
let mut s = session();
s.execute("artboard.new", &json!({})).unwrap();
let marks = |s: &Session, i: usize| {
let a = &s.doc().unwrap().doc.artboards[i];
(a.show_center_mark, a.show_cross_hairs)
};
let undo = |s: &Session| s.doc().unwrap().history.undo.len();
let u0 = undo(&s);
s.execute("artboard.setProps", &json!({"index": 1, "showCenterMark": true})).unwrap();
assert_eq!((marks(&s, 0), marks(&s, 1)), ((false, false), (true, false)), "only that artboard");
s.execute("artboard.setProps", &json!({"index": 1, "showCrossHairs": true, "showCenterMark": false})).unwrap();
assert_eq!(marks(&s, 1), (false, true));
s.execute("artboard.setProps", &json!({"index": 1, "name": "Marked"})).unwrap();
assert_eq!(marks(&s, 1), (false, true), "a change without them leaves them");
assert_eq!(undo(&s), u0 + 3, "one undo step each");
for was in [(false, true), (true, false), (false, false)] {
s.execute("edit.undo", &json!({})).unwrap();
assert_eq!(marks(&s, 1), was);
}
s.execute("edit.redo", &json!({})).unwrap();
assert_eq!(marks(&s, 1), (true, false));
// A locked artboard keeps its rectangle, not its display marks.
s.execute("artboard.setProps", &json!({"index": 0, "locked": true})).unwrap();
s.execute("artboard.setProps", &json!({"index": 0, "showCrossHairs": true})).unwrap();
assert_eq!(marks(&s, 0), (false, true));
let u = undo(&s);
s.execute(
"artboard.setProps",
&json!({"artboards": [{"index": 0, "showCenterMark": true}, {"index": 1, "showCenterMark": false, "showCrossHairs": true}]}),
)
.unwrap();
assert_eq!((marks(&s, 0), marks(&s, 1)), ((true, true), (false, true)), "each listed artboard takes its own");
assert_eq!(undo(&s), u + 1, "in one undo step");
let doc = crate::inspect::document(&s);
assert_eq!((doc["artboards"][0]["showCenterMark"].clone(), doc["artboards"][1]["showCrossHairs"].clone()), (json!(true), json!(true)));
assert!(doc["artboards"][1].get("showCenterMark").is_none(), "{doc}");
let copy = s.execute("artboard.duplicate", &json!({"index": 1})).unwrap()["index"].as_u64().unwrap() as usize;
assert_eq!(marks(&s, copy), (false, true), "a duplicate keeps them");
let made = s.execute("artboard.new", &json!({"showCenterMark": true})).unwrap()["index"].as_u64().unwrap() as usize;
assert_eq!(marks(&s, made), (true, false), "a new artboard takes them too");
let plain = s.execute("artboard.new", &json!({})).unwrap()["index"].as_u64().unwrap() as usize;
assert_eq!(marks(&s, plain), (false, false));
let before = s.doc().unwrap().doc.clone();
assert!(s.execute("artboard.setProps", &json!({"index": 1, "showCenterMark": "yes"})).is_err());
assert!(
s.execute("artboard.setProps", &json!({"artboards": [{"index": 0, "showCenterMark": false}, {"index": 1, "showCrossHairs": 1}]})).is_err()
);
assert!(s.execute("artboard.new", &json!({"showCrossHairs": "yes"})).is_err());
assert_eq!(*s.doc().unwrap().doc, *before, "a bad value changes nothing");
}
/// The native format keeps Show Center Mark and Show Cross Hairs. A document that never shows them
/// is written byte for byte as before they could be set, and a file written without the two keys
/// opens with both off.
#[test]
fn artboard_display_marks_round_trip_through_the_native_format() {
let mut s = session();
s.execute("artboard.new", &json!({})).unwrap();
let save = |s: &Session| vectorcraft_format::save(&s.doc().unwrap().doc, false);
let before = save(&s);
s.execute("artboard.setProps", &json!({"artboards": [{"index": 0, "showCenterMark": true}, {"index": 1, "showCrossHairs": true}]})).unwrap();
let bytes = save(&s);
let saved: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
let ab = |i: usize| saved["document"]["artboards"][i].clone();
assert_eq!((ab(0)["show_center_mark"].clone(), ab(0)["show_cross_hairs"].clone()), (json!(true), json!(false)));
assert_eq!((ab(1)["show_center_mark"].clone(), ab(1)["show_cross_hairs"].clone()), (json!(false), json!(true)));
let back = vectorcraft_format::load(&bytes).unwrap();
assert_eq!(back.artboards, s.doc().unwrap().doc.artboards);
s.execute("artboard.setProps", &json!({"artboards": [{"index": 0, "showCenterMark": false}, {"index": 1, "showCrossHairs": false}]})).unwrap();
assert_eq!(save(&s), before, "off again, the file is as it was");
// A file whose artboards have no such keys (older writers) opens with both off.
let mut old: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
for a in old["document"]["artboards"].as_array_mut().unwrap() {
let a = a.as_object_mut().unwrap();
a.remove("show_center_mark");
a.remove("show_cross_hairs");
}
let back = vectorcraft_format::load(&serde_json::to_vec(&old).unwrap()).unwrap();
assert!(back.artboards.iter().all(|a| !a.show_center_mark && !a.show_cross_hairs));
}
/// Show Center Mark and Show Cross Hairs are screen-only: SVG and PDF exports (and print, which
/// prints the PDF) come out the same with them on as with them off.
#[test]
fn artboard_display_marks_stay_out_of_exports() {
let mut s = session();
rect(&mut s);
let doc = |s: &Session| (*s.doc().unwrap().doc).clone();
let off = doc(&s);
s.execute("artboard.setProps", &json!({"index": 0, "showCenterMark": true, "showCrossHairs": true})).unwrap();
let on = doc(&s);
assert_ne!(on.artboards, off.artboards);
assert_eq!(vectorcraft_svg::export(&on, &Default::default()), vectorcraft_svg::export(&off, &Default::default()));
assert_eq!(crate::export_pdf(&on, &Default::default()).unwrap(), crate::export_pdf(&off, &Default::default()).unwrap());
}
#[test]
fn swatch_groups_duplicate_sort() {
let mut s = session();