Object: a Distribute submenu runs the Align panel's distribute buttons

Object › Distribute follows Object › Align and lists Horizontal
Distribute Left, Center and Right, Vertical Distribute Top, Center and
Bottom, Horizontal Distribute Space and Vertical Distribute Space. Each
item runs the button's `align.distribute*` command, so it uses Align To,
the key object and the Distribute Spacing value, and with the Artboard
tool chosen it spaces the selected artboards.
This commit is contained in:
dotCooCoo
2026-11-14 16:28:19 -08:00
parent dafd993395
commit eff1500bd2
4 changed files with 71 additions and 1 deletions
+51
View File
@@ -1008,6 +1008,57 @@ fn expand_appearance_is_an_object_menu_item_only() {
assert_eq!(paths, [vec!["Object".to_string()]]);
}
/// Object › Distribute follows Object › Align and lists the Align panel's Distribute Objects and
/// Distribute Spacing buttons, each item running the button's command: dimmed with nothing
/// selected, spacing three objects evenly, and with a key object sending the Distribute Spacing value.
#[test]
fn object_distribute_runs_the_distribute_buttons() {
let object = menu_tree().into_iter().find(|(t, _)| *t == "Object").map(|(_, items)| items).unwrap_or_default();
let subs: Vec<&str> = object.iter().filter_map(|it| if let Item::Sub(l, _) = it { Some(*l) } else { None }).collect();
let align = subs.iter().position(|l| *l == "Align").unwrap_or_else(|| panic!("Object › Align: {subs:?}"));
assert_eq!(subs.get(align + 1), Some(&"Distribute"), "{subs:?}");
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({"width": 200, "height": 200})).unwrap();
let items = |app: &VectorcraftApp| -> Vec<MenuEntry> { menu_entries(app).into_iter().filter(|e| e.path == ["Object", "Distribute"]).collect() };
let listed: Vec<(String, Option<String>)> = items(&app).into_iter().map(|e| (e.label, e.command)).collect();
let expected: Vec<(String, Option<String>)> = [
("Horizontal Distribute Left", "align.distributeHorizontalLeft"),
("Horizontal Distribute Center", "align.distributeHorizontalCenter"),
("Horizontal Distribute Right", "align.distributeHorizontalRight"),
("Vertical Distribute Top", "align.distributeVerticalTop"),
("Vertical Distribute Center", "align.distributeVerticalCenter"),
("Vertical Distribute Bottom", "align.distributeVerticalBottom"),
("Horizontal Distribute Space", "align.distributeHorizontalSpace"),
("Vertical Distribute Space", "align.distributeVerticalSpace"),
]
.into_iter()
.map(|(l, id)| (l.to_string(), Some(id.to_string())))
.collect();
assert_eq!(listed, expected);
assert!(items(&app).iter().all(|e| !e.enabled), "nothing selected");
let ids: Vec<Value> = [0, 30, 100]
.into_iter()
.map(|x| app.run("shape.rectangle", json!({"x": x, "y": 10, "width": 10, "height": 10})).unwrap()["id"].clone())
.collect();
app.run("select.set", json!({"ids": ids})).unwrap();
let lefts = |app: &VectorcraftApp| -> Vec<f64> {
let doc = &app.session.active().unwrap().doc;
ids.iter().map(|id| doc.node(vectorcraft_doc::NodeId(id.as_u64().unwrap())).unwrap().geometric_bounds().unwrap().x0).collect()
};
let click = |app: &mut VectorcraftApp, label: &str| {
let e = items(app).into_iter().find(|e| e.label == label).unwrap_or_else(|| panic!("{label}"));
assert!(e.enabled, "{label}");
let (id, p) = click_target(&e.label, e.command.as_deref().unwrap_or_default(), &e.params);
invoke(app, &id, p);
};
click(&mut app, "Horizontal Distribute Left");
assert_eq!(lefts(&app), [0.0, 50.0, 100.0], "{}", app.ui.status);
app.ui.align_spacing = 5.0;
app.run("select.key", json!({"id": ids[0]})).unwrap();
click(&mut app, "Horizontal Distribute Space");
assert_eq!(lefts(&app), [0.0, 15.0, 30.0], "{}", app.ui.status);
}
/// Isolation keeps the Lock and Hide actions available for selected members.
#[test]
fn lock_and_hide_menus_are_enabled_in_isolation_mode() {
+17
View File
@@ -165,6 +165,23 @@ pub fn menu_tree_named(english_names: bool) -> Vec<(&'static str, Vec<Item>)> {
cp("Vertical Align Bottom", "object.align", json!({"vertical": "bottom"})),
],
),
// The Align panel's Distribute Objects and Distribute Spacing buttons: each item runs
// what a click on its button runs (`crate::panels::align::run_button`).
sub(
"Distribute",
vec![
c("Horizontal Distribute Left", "align.distributeHorizontalLeft"),
c("Horizontal Distribute Center", "align.distributeHorizontalCenter"),
c("Horizontal Distribute Right", "align.distributeHorizontalRight"),
Sep,
c("Vertical Distribute Top", "align.distributeVerticalTop"),
c("Vertical Distribute Center", "align.distributeVerticalCenter"),
c("Vertical Distribute Bottom", "align.distributeVerticalBottom"),
Sep,
c("Horizontal Distribute Space", "align.distributeHorizontalSpace"),
c("Vertical Distribute Space", "align.distributeVerticalSpace"),
],
),
Sep,
c("Group", "object.group"),
c("Ungroup", "object.ungroup"),
+2 -1
View File
@@ -5855,7 +5855,8 @@ commands send `to: "key"` while the selection has a key, else `"artboard"` for o
anchor points, else the Align To choice. The Distribute commands send `to: "key"` while the selection has a key, else
the Align To choice, and while there is a key object or a key guide they send the panel's Distribute Spacing value as
`spacing`. Edit › Keyboard Shortcuts lists them under Align, with no default shortcut, and
`shortcuts.set {id, shortcut}` gives one a shortcut.
`shortcuts.set {id, shortcut}` gives one a shortcut. The Object › Distribute menu lists the eight Distribute commands,
and each item runs its command.
With the Artboard tool chosen, the same buttons and UI commands, and the Control bar's Align and Distribute buttons,
run `artboard.align`, `artboard.distribute` and `artboard.distributeSpacing` on the artboards the tool has selected.
+1
View File
@@ -207,6 +207,7 @@ tools, never checked side by side) isn't captured row by row.
| Ruler guides align to a key object: a click on an object selected with guides makes it the key (one object is enough); Align moves the selected guides to its edge or centre (vertical guides to left, centre or right, horizontal ones to top, centre or bottom) and the key stays put | done | #2143; aligning to the selection or the artboard leaves guides alone |
| Align panel menu › Align to Glyph Bounds › Point Text and Area Text (checked while on): Align and Distribute measure point type or area type by the ink of its glyphs instead of its em box or frame | done | #4231; `alignGlyphBoundsPoint`, `alignGlyphBoundsArea` ([Align text](https://helpx.adobe.com/illustrator/desktop/design-with-text/edit-format-text/align-text.html)); `tests::glyphbounds`, `panels::align::tests::align_to_glyph_bounds_toggles_point_and_area_text`; the Character and Properties panels' Align to Glyph Bounds controls are not done |
| Align and distribute artboards: with the Artboard tool chosen, the Align panel's Align Objects, Distribute Objects and Distribute Spacing buttons, and the Control bar's, move the selected artboards with the art inside them (Move Artwork with Artboard) | done | `artboard.align`, `artboard.distribute`, `artboard.distributeSpacing` ([Align and distribute artboards](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/align-and-distribute-artboards.html)); VectorCraft's own rules: Align to Artboard keeps the active artboard in place and sends the Distribute Spacing value, Align to Key Object is off, a locked artboard stays where it is; `cmd::artboardalign::tests`, `panels::align::tests::the_align_buttons_align_the_selected_artboards` |
| Object › Distribute, below Object › Align: Horizontal Distribute Left, Center and Right, Vertical Distribute Top, Center and Bottom, Horizontal Distribute Space and Vertical Distribute Space, each running what a click on its Align panel button runs (Align To, the key object, the Distribute Spacing value and, with the Artboard tool chosen, the selected artboards) | done | The `align.distribute*` commands; the public page names Object › Distribute beside Object › Align for the Align and Distribute commands ([Align and distribute objects](https://helpx.adobe.com/illustrator/desktop/manage-objects/arrange-objects/align-and-distribute-objects.html)); the items and their order are VectorCraft's own, taken from the Align panel; `menus::tests::object_distribute_runs_the_distribute_buttons` |
| A marquee selects the objects whose paths it touches (anchors or segments): drawn inside a filled shape, over a background, it leaves that shape (Preview and Outline) | done | #1103 |
| Click a segment independently of its endpoint anchors; only its direction handles show | done | #1278: hollow endpoint squares, selected edge emphasized; headless egui checked |
| Drag a curved segment to bend it, a straight one to move its anchors | done | |