mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
Cover mask vertex multi-select: Shift-click toggle and marquee (#1004)
Shift-click adds a vertex to the selection and removes it again, a plain click collapses to one, and a marquee from empty viewer area selects the enclosed vertices (additive with Shift) without moving the layer. These behaviors regressed or were never covered; the tests drive them through the Composition viewer like a user.
This commit is contained in:
1 parent
9efe75330c
commit
d33e82a571
1 file changed
+90
@@ -2088,3 +2088,93 @@ fn fit_up_to_100_follows_the_maximized_panel() {
|
||||
h.run_steps(3);
|
||||
assert!((rect(&h, "viewer.comp").width() - 640.0 / ppp).abs() < 0.01);
|
||||
}
|
||||
|
||||
/// Mask vertices multi-select with the Selection tool: Shift-click adds a vertex to the
|
||||
/// selection and Shift-clicking it again removes it; a plain click collapses to one (#1004).
|
||||
#[test]
|
||||
fn mask_shift_click_toggles_vertices() {
|
||||
use egui::Modifiers;
|
||||
let mut h = harness();
|
||||
let plate = layer_id(&h, "Plate");
|
||||
h.state_mut().session.execute("layer.select", json!({"layers": [plate]})).unwrap();
|
||||
h.state_mut()
|
||||
.session
|
||||
.execute("mask.new", json!({"layer": plate, "vertices": [[100.0, 60.0], [500.0, 60.0], [500.0, 300.0], [100.0, 300.0]], "closed": true}))
|
||||
.unwrap();
|
||||
h.state_mut().ui.tool = Tool::Selection;
|
||||
h.run_steps(2);
|
||||
let (p0, p1) = (screen(&h, [100.0, 60.0]), screen(&h, [500.0, 60.0]));
|
||||
let shift_click = |h: &mut Harness<'_, EffectcraftApp>, p: Pos2| {
|
||||
h.input_mut().events.push(Event::ModifiersChanged(Modifiers::SHIFT));
|
||||
h.step();
|
||||
h.input_mut().events.push(Event::PointerMoved(p));
|
||||
h.input_mut().events.push(Event::PointerButton { pos: p, button: egui::PointerButton::Primary, pressed: true, modifiers: Modifiers::SHIFT });
|
||||
h.step();
|
||||
h.input_mut().events.push(Event::PointerButton { pos: p, button: egui::PointerButton::Primary, pressed: false, modifiers: Modifiers::SHIFT });
|
||||
h.step();
|
||||
h.input_mut().events.push(Event::ModifiersChanged(Modifiers::NONE));
|
||||
h.run_steps(2);
|
||||
};
|
||||
click(&mut h, p0);
|
||||
assert_eq!(h.state().session.state.selected_vertices.len(), 1, "plain click selects one vertex");
|
||||
shift_click(&mut h, p1);
|
||||
assert_eq!(h.state().session.state.selected_vertices.len(), 2, "shift-click adds a second vertex");
|
||||
shift_click(&mut h, p1);
|
||||
assert_eq!(h.state().session.state.selected_vertices.len(), 1, "shift-click again removes it");
|
||||
}
|
||||
|
||||
/// Dragging a marquee from empty viewer area selects the mask vertices inside it, additive
|
||||
/// with Shift, without moving the layer (#1004).
|
||||
#[test]
|
||||
fn mask_marquee_selects_vertices() {
|
||||
let mut h = harness();
|
||||
let plate = layer_id(&h, "Plate");
|
||||
let bx = layer_id(&h, "Box");
|
||||
// A full-frame layer behind leaves no empty area to start a marquee from: remove it.
|
||||
h.state_mut().session.execute("layer.select", json!({"layers": [plate]})).unwrap();
|
||||
h.state_mut().session.execute("edit.clear", json!({"layers": [plate]})).unwrap();
|
||||
assert_eq!(h.state().session.active_comp().unwrap().layers.len(), 1);
|
||||
h.state_mut().session.execute("layer.select", json!({"layers": [bx]})).unwrap();
|
||||
h.state_mut()
|
||||
.session
|
||||
.execute("mask.new", json!({"layer": bx, "vertices": [[10.0, 10.0], [70.0, 10.0], [70.0, 70.0], [10.0, 70.0]], "closed": true}))
|
||||
.unwrap();
|
||||
h.state_mut().ui.tool = Tool::Selection;
|
||||
h.state_mut().session.execute("mask.selectVertices", json!({"vertices": []})).unwrap();
|
||||
h.run_steps(2);
|
||||
// Mask vertices are layer-local; map vertex 0 to comp space (no rotation, uniform scale).
|
||||
let (pp, aa) = {
|
||||
let l = h.state().session.active_comp().unwrap().layer(LayerId(bx)).unwrap().clone();
|
||||
(l.props.prop("transform/position").unwrap().value.as_vec3(), l.props.prop("transform/anchor").unwrap().value.as_vec3())
|
||||
};
|
||||
let v0 = [10.0 - aa[0] + pp[0], 10.0 - aa[1] + pp[1]];
|
||||
let pos = |h: &Harness<'_, EffectcraftApp>| {
|
||||
h.state().session.active_comp().unwrap().layer(LayerId(bx)).unwrap().props.prop("transform/position").unwrap().value.as_vec3()
|
||||
};
|
||||
let before = pos(&h);
|
||||
// Drag a box around the top-left vertex only, starting/ending on empty area.
|
||||
let (from, to) = (screen(&h, [(v0[0] - 90.0) as f32, (v0[1] - 50.0) as f32]), screen(&h, [(v0[0] + 10.0) as f32, (v0[1] + 10.0) as f32]));
|
||||
drag(&mut h, from, to);
|
||||
let sel = &h.state().session.state.selected_vertices;
|
||||
assert_eq!(sel.len(), 1, "marquee selects the enclosed vertex: {sel:?}");
|
||||
assert_eq!(sel[0].index, 0);
|
||||
assert_eq!(pos(&h), before, "the layer does not move under a vertex marquee");
|
||||
// Shift-marquee adds the vertices it encloses.
|
||||
let v1 = [70.0 - aa[0] + pp[0], 10.0 - aa[1] + pp[1]];
|
||||
let (from, to) = (screen(&h, [(v1[0] - 10.0) as f32, (v1[1] - 50.0) as f32]), screen(&h, [(v1[0] + 60.0) as f32, (v1[1] + 50.0) as f32]));
|
||||
h.input_mut().events.push(Event::ModifiersChanged(egui::Modifiers::SHIFT));
|
||||
h.step();
|
||||
h.input_mut().events.push(Event::PointerMoved(from));
|
||||
h.input_mut().events.push(Event::PointerButton { pos: from, button: egui::PointerButton::Primary, pressed: true, modifiers: egui::Modifiers::SHIFT });
|
||||
h.step();
|
||||
for i in 1..=8 {
|
||||
h.input_mut().events.push(Event::PointerMoved(from + (to - from) * (i as f32 / 8.0)));
|
||||
h.step();
|
||||
}
|
||||
h.input_mut().events.push(Event::PointerButton { pos: to, button: egui::PointerButton::Primary, pressed: false, modifiers: egui::Modifiers::SHIFT });
|
||||
h.step();
|
||||
h.input_mut().events.push(Event::ModifiersChanged(egui::Modifiers::NONE));
|
||||
h.run_steps(2);
|
||||
let sel = &h.state().session.state.selected_vertices;
|
||||
assert_eq!(sel.len(), 2, "shift-marquee adds: {sel:?}");
|
||||
}
|
||||
Reference in new issue
Block a user