G12: ruler guides nudged with art keep to the page's axes at a turned Constrain Angle

With objects and ruler guides selected together, object.nudge moved the
guides by the art's delta turned through General > Constrain Angle, so at
90 degrees a Right nudge moved a horizontal guide vertically. The guides
in that edit now move by the page-axis delta, as guides selected alone
already did (CodeRabbit finding on #3745).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gCpjL3yUhaH3jRp7ksufh
This commit is contained in:
Angelo Bernardini
2026-10-24 04:59:57 +07:00
committed by bflatastic
co-authored by Claude Opus 5.5
parent e0c1548049
commit 52a43eefdb
2 changed files with 42 additions and 2 deletions
+17 -2
View File
@@ -380,6 +380,21 @@ pub(crate) fn transform_node(n: &mut Node, xf: Affine, sc: Scaling, objects: boo
/// `anchors` (`ids` being the selection): with anchors selected with Direct Selection and no
/// copy, only those anchors transform, their handles with them, and the objects selected whole.
pub(crate) fn apply_transform(s: &mut Session, label: &str, ids: Vec<NodeId>, xf: Affine, p: &Value, anchors: bool) -> Result<Value> {
apply_transform_moving_guides(s, label, ids, xf, p, anchors, None)
}
/// [`apply_transform`], with the selected ruler guides moved by `guides` instead of by the
/// translation `xf` when given (a nudge along a turned Constrain Angle moves the art along it and
/// the guides along the page's axes).
fn apply_transform_moving_guides(
s: &mut Session,
label: &str,
ids: Vec<NodeId>,
xf: Affine,
p: &Value,
anchors: bool,
guides: Option<Vec2>,
) -> Result<Value> {
let copy = bool_or(p, "copy", false);
let areas = bool_or(p, "typeAreas", false);
let objects = transforms_objects(p);
@@ -387,7 +402,7 @@ pub(crate) fn apply_transform(s: &mut Session, label: &str, ids: Vec<NodeId>, xf
sc.patterns = transform_patterns(s, p, &ids)?;
let c = xf.as_coeffs();
let is_translation = (c[0] - 1.0).abs() < 1e-9 && c[1].abs() < 1e-9 && c[2].abs() < 1e-9 && (c[3] - 1.0).abs() < 1e-9;
let delta = Vec2::new(c[4], c[5]);
let delta = guides.unwrap_or(Vec2::new(c[4], c[5]));
let with_guides = objects && ids_param(p, "ids").is_none() && is_translation;
let has_guides = with_guides && !s.doc()?.selection.guides.is_empty();
// A transform of ids that name no object, or an identity transform without a copy, moves
@@ -564,7 +579,7 @@ fn nudge(s: &mut Session, p: &Value) -> Result<Value> {
return super::docmenu::guide_move(s, &json!({ "dx": gx, "dy": gy, "copy": copy }));
}
let ids = selected_roots_to_transform(s)?;
apply_transform(s, "Move", ids, Affine::translate((dx, dy)), p, true)
apply_transform_moving_guides(s, "Move", ids, Affine::translate((dx, dy)), p, true, Some(Vec2::new(gx, gy)))
}
fn about(o: Point, a: Affine) -> Affine {
@@ -152,3 +152,28 @@ fn constrain_angle_whole_turns_nudge_as_zero() {
set_pref(&mut s, "constrainAngle", json!(90));
assert_eq!(moved(&mut s, 1, 0), Vec2::new(0.0, -10.0));
}
/// General › Constrain Angle turns only the art's arrow-key axes: ruler guides selected with the
/// art keep to the page's axes. At 90°, → moves the art straight up, a vertical guide 10 right and
/// a horizontal guide not at all; ↓ moves the horizontal guide 10 down. Copies alike.
#[test]
fn constrain_angle_keeps_guides_selected_with_art_on_the_page_axes() {
let mut s = new_doc();
let id = rect(&mut s, 100.0, 100.0);
set_pref(&mut s, "keyboardIncrement", json!(10));
set_pref(&mut s, "constrainAngle", json!(90));
let v = s.execute("guide.add", &json!({"vertical": true, "pos": 50})).unwrap()["index"].as_u64().unwrap() as usize;
let h = s.execute("guide.add", &json!({"vertical": false, "pos": 50})).unwrap()["index"].as_u64().unwrap() as usize;
s.execute("select.set", &json!({"ids": [id.0]})).unwrap();
s.execute("guide.select", &json!({"indexes": [v, h], "toggle": true})).unwrap();
let pos = |s: &Session| s.doc().unwrap().doc.guides.iter().map(|g| g.pos).collect::<Vec<_>>();
let at = centre(&s, id);
s.execute("object.nudge", &json!({"dx": 1, "dy": 0})).unwrap();
assert_eq!(centre(&s, id) - at, Vec2::new(0.0, -10.0), "the art moves along the turned axis");
assert_eq!(pos(&s), [60.0, 50.0], "→: the vertical guide moves right, the horizontal one stays");
s.execute("object.nudge", &json!({"dx": 0, "dy": 1})).unwrap();
assert_eq!(pos(&s), [60.0, 60.0], "↓: the horizontal guide moves down");
// With a copy, the guides' copies keep to the page too.
s.execute("object.nudge", &json!({"dx": 1, "dy": 0, "copy": true})).unwrap();
assert_eq!(pos(&s), [60.0, 60.0, 70.0, 60.0]);
}