mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-30 12:47:13 -06:00
G11: Align panel moves ruler guides to artboard (#3463)
## Summary object.align now moves selected ruler guides when aligning to the artboard, matching Illustrator and the existing Distribute behaviour. Guide-only selection can run align; the Align panel enables its buttons when only guides are selected. Guides still stay put when objects are in the selection and Align To is Selection; key-guide behaviour is unchanged. ## Tests - engine tests_keyguide: guides_selected_alone_align_to_artboard; updated selected_guides_align_to_a_clicked_key_object for artboard align - ui-egui align panel: the_align_buttons_align_guides_selected_alone_to_artboard Closes storytold/vectorcraft#3463 Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
This commit is contained in:
committed by
bflatastic
co-authored by
nrq6993-a11y
parent
430741f074
commit
c8082facb1
@@ -252,8 +252,8 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Align",
|
||||
["Window", "Align"],
|
||||
None,
|
||||
"{horizontal?: \"left\"|\"center\"|\"right\", vertical?: \"top\"|\"center\"|\"bottom\", to?: \"selection\"|\"artboard\"|\"key\" (default: the key object when the selection has one, select.key, else the selection; a key guide, select.key {guide}, stays put and the objects line up on its line: a vertical guide sets where left, center and right go and vertical alignment leaves them, a horizontal one the other way round; aligning to the key, the other selected ruler guides move too: a vertical guide to the key's left edge, center or right edge, a horizontal one to its top, center or bottom), artboard?: index (0-based; with to: \"artboard\", the artboard to align to; the app passes the active one; default: the artboard under the center of the selection, else the first), bounds?: \"preview\"|\"geometric\" (default: the Use Preview Bounds preference; preview bounds take in strokes), patterns?: bool (Transform Patterns: pattern fills and strokes transform with the art; default: prefs transformPatternTiles)} With anchor points selected (Direct Selection), the points align instead: to their own bounds, or to the artboard (#1180)",
|
||||
has_selection,
|
||||
"{horizontal?: \"left\"|\"center\"|\"right\", vertical?: \"top\"|\"center\"|\"bottom\", to?: \"selection\"|\"artboard\"|\"key\" (default: the key object when the selection has one, select.key, else the selection; a key guide, select.key {guide}, stays put and the objects line up on its line: a vertical guide sets where left, center and right go and vertical alignment leaves them, a horizontal one the other way round; aligning to the key, the other selected ruler guides move too: a vertical guide to the key's left edge, center or right edge, a horizontal one to its top, center or bottom; aligning to the artboard moves selected guides to its edges too (#3463), and with only guides selected aligning to the selection moves them to their collective bounds), artboard?: index (0-based; with to: \"artboard\", the artboard to align to; the app passes the active one; default: the artboard under the center of the selection, else the first), bounds?: \"preview\"|\"geometric\" (default: the Use Preview Bounds preference; preview bounds take in strokes), patterns?: bool (Transform Patterns: pattern fills and strokes transform with the art; default: prefs transformPatternTiles)} With anchor points selected (Direct Selection), the points align instead: to their own bounds, or to the artboard (#1180)",
|
||||
has_selection_or_guides,
|
||||
align
|
||||
),
|
||||
cmd!(
|
||||
@@ -1361,11 +1361,22 @@ fn align_reference(s: &Session, p: &Value, ids: &[NodeId], preview: bool, own: O
|
||||
Ok((r, true, true))
|
||||
}
|
||||
|
||||
/// Bounds of the selected ruler guides (each guide is a zero-thickness line), for Align to
|
||||
/// Selection when no objects are selected.
|
||||
fn selected_guides_bounds(st: &crate::DocState) -> Option<Rect> {
|
||||
st.selection.guides.iter().filter_map(|&i| st.doc.guides.get(i)).fold(None, |acc: Option<Rect>, g| {
|
||||
let r = if g.vertical { Rect::new(g.pos, 0.0, g.pos, 0.0) } else { Rect::new(0.0, g.pos, 0.0, g.pos) };
|
||||
Some(acc.map(|a| a.union(r)).unwrap_or(r))
|
||||
})
|
||||
}
|
||||
|
||||
fn reference_rect(s: &Session, p: &Value, ids: &[NodeId], preview: bool) -> Result<Rect> {
|
||||
let st = s.doc()?;
|
||||
match align_to(s, p) {
|
||||
Some("artboard") => {
|
||||
let b = effects::preview::bounds_of(&st.doc, ids, preview).unwrap_or_default();
|
||||
let b = effects::preview::bounds_of(&st.doc, ids, preview)
|
||||
.or_else(|| ids.is_empty().then(|| selected_guides_bounds(st)).flatten())
|
||||
.unwrap_or_default();
|
||||
// Align to Artboard uses artboard `artboard` (the app passes the active one). Without
|
||||
// it, or with an index the document doesn't have, it uses the artboard under the center
|
||||
// of the selection, or the first one when no artboard is there.
|
||||
@@ -1377,7 +1388,13 @@ fn reference_rect(s: &Session, p: &Value, ids: &[NodeId], preview: bool) -> Resu
|
||||
let k = st.selection.key.ok_or_else(|| EngineError::Other("no key object".into()))?;
|
||||
effects::preview::bounds_of(&st.doc, &[k], preview).ok_or(EngineError::NoNode(k))
|
||||
}
|
||||
_ => effects::preview::bounds_of(&st.doc, ids, preview).ok_or_else(|| EngineError::Other("nothing to align".into())),
|
||||
_ => {
|
||||
if ids.is_empty() {
|
||||
selected_guides_bounds(st).ok_or_else(|| EngineError::Other("nothing to align".into()))
|
||||
} else {
|
||||
effects::preview::bounds_of(&st.doc, ids, preview).ok_or_else(|| EngineError::Other("nothing to align".into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1443,15 +1460,22 @@ fn align(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
apply_moves(s, "Align", p, &ids, &moves, &guides)
|
||||
}
|
||||
|
||||
/// The selected ruler guides that Align to the key object (or key guide) moves, and where: a
|
||||
/// vertical guide goes to the left edge, centre or right edge of `r` that `h` names, a horizontal
|
||||
/// one to the top, centre or bottom that `v` names. The key guide stays put, and so do guides
|
||||
/// when aligning to the selection or the artboard.
|
||||
/// The selected ruler guides that Align moves, and where: a vertical guide goes to the left edge,
|
||||
/// centre or right edge of `r` that `h` names, a horizontal one to the top, centre or bottom that
|
||||
/// `v` names. Align to the key object (or key guide) moves them too, and the key guide stays put.
|
||||
/// Align to the artboard moves them (#3463). Align to the selection moves them when only guides are
|
||||
/// selected; with objects in the selection they stay put.
|
||||
fn guide_moves(s: &Session, p: &Value, r: Rect, h: Option<&str>, v: Option<&str>) -> Result<Vec<(usize, f64)>> {
|
||||
if align_to(s, p) != Some("key") {
|
||||
let st = s.doc()?;
|
||||
let to = align_to(s, p);
|
||||
let move_guides = match to {
|
||||
Some("key" | "artboard") => true,
|
||||
Some("selection") => st.selection.is_empty(),
|
||||
_ => false,
|
||||
};
|
||||
if !move_guides {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
let st = s.doc()?;
|
||||
let at = |vertical: bool| match (vertical, h, v) {
|
||||
(true, Some("left"), _) => Some(r.x0),
|
||||
(true, Some("center"), _) => Some(r.center().x),
|
||||
@@ -1461,11 +1485,12 @@ fn guide_moves(s: &Session, p: &Value, r: Rect, h: Option<&str>, v: Option<&str>
|
||||
(false, _, Some("bottom")) => Some(r.y1),
|
||||
_ => None,
|
||||
};
|
||||
let skip_key = to == Some("key");
|
||||
Ok(st
|
||||
.selection
|
||||
.guides
|
||||
.iter()
|
||||
.filter(|&&i| Some(i) != st.selection.key_guide)
|
||||
.filter(|&&i| !skip_key || Some(i) != st.selection.key_guide)
|
||||
.filter_map(|&i| Some((i, at(st.doc.guides.get(i)?.vertical)?)))
|
||||
.collect())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user