Merge pull request #2605 from storytold/fix/1964-shift-vertex-drag

Viewer: Shift keeps a dragged mask or shape vertex to one axis (#1964)
This commit is contained in:
BFlat
2026-10-30 03:18:22 +07:00
committed by GitHub
2 changed files with 31 additions and 2 deletions
+3 -2
View File
@@ -1352,8 +1352,9 @@ fn phase_drag(app: &mut EffectcraftApp, ui: &mut egui::Ui, ectx: &EvalCtx, resp:
}
}
Gesture::Vertices { start, src, applied, inv, layer } => {
// The grabbed vertex snaps (to other layers' vertices and features, guides, grid).
let want = [cpt[0] - start[0], cpt[1] - start[1]];
// The grabbed vertex snaps (to other layers' vertices and features, guides, grid);
// Shift keeps it to one axis, as a layer move does (#1964).
let want = one_axis([cpt[0] - start[0], cpt[1] - start[1]], mods.shift);
let c = vt::snap(app, &ctx, ectx, &map, &[layer], &[[src[0] + want[0], src[1] + want[1]]], mods);
let total = [want[0] + c[0], want[1] + c[1]];
let d = inv.apply_vec(gv2(total[0] - applied[0], total[1] - applied[1]));
+28
View File
@@ -802,6 +802,34 @@ fn hidden_shape_layer_keeps_its_controls_and_can_still_scale() {
assert_ne!(scale(&h), before, "a hidden layer's handle still scales it");
}
/// #1964: Shift-dragging a selected mask vertex keeps it to the axis it moves along most, as a
/// Shift-dragged layer does; without Shift it moves freely.
#[test]
fn shift_drag_keeps_a_mask_vertex_to_one_axis() {
let mut h = harness();
let plate = layer_id(&h, "Plate");
let corners = [[100.0, 60.0], [500.0, 60.0], [500.0, 300.0], [100.0, 300.0]];
let mask = {
let s = &mut h.state_mut().session;
s.execute("layer.select", json!({"layers": [plate]})).unwrap();
let m = s.execute("mask.new", json!({"layer": plate, "vertices": corners, "closed": true})).unwrap()["mask"].as_u64().unwrap();
s.execute("mask.selectVertices", json!({"vertices": [{"layer": plate, "mask": m, "index": 0}]})).unwrap();
m
};
h.run_steps(2);
let vertex = format!("viewer.mask.{mask}.vertex.0");
let before = mask_path(&h, "Plate").vertices[0];
let from = rect(&h, &vertex).center();
drag_path(&mut h, &[from, from + vec2(30.0, 8.0)], egui::Modifiers::SHIFT);
let shifted = mask_path(&h, "Plate").vertices[0];
assert!(shifted[0] > before[0], "it moved along x: {before:?} -> {shifted:?}");
assert_eq!(shifted[1], before[1], "and not along y");
let from = rect(&h, &vertex).center();
drag_path(&mut h, &[from, from + vec2(30.0, 8.0)], egui::Modifiers::NONE);
let free = mask_path(&h, "Plate").vertices[0];
assert!(free[0] > shifted[0] && free[1] > shifted[1], "without Shift both axes move: {shifted:?} -> {free:?}");
}
/// #1769: a hidden masked layer keeps its mask path and vertices, and they remain editable.
#[test]
fn hidden_masked_layer_keeps_its_mask_path_editable() {