Files
figcraft/crates/engine/src/al_drag_tests.rs
T
BFlatandClaude Opus 5.5 48d5964941 M2: auto layout drag: wrap rows and grid cells (#27)
- Dragging a child of a wrapping row layout inserts by visual row and position (rows are found
  from the flow, so the insertion line spans the right row), instead of by x alone.
- Dragging a child of a grid layout places it in the cell under the pointer (nearest cell over
  gaps), with a dashed cell highlight while dragging; one undo step.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 04:32:31 +07:00

72 lines
3.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Dragging children of wrapping and grid auto layout frames.
use serde_json::json;
use crate::Session;
use crate::params::id_of;
use crate::tools::{PointerEvent, PointerKind};
use figcraft_doc::NodeId;
fn id(v: serde_json::Value) -> NodeId {
id_of(&v["id"]).unwrap()
}
fn drag(s: &mut Session, from: (f64, f64), to: (f64, f64)) {
s.pointer(&PointerEvent::at(PointerKind::Down, from.0, from.1, 1.0)).unwrap();
s.pointer(&PointerEvent::at(PointerKind::Move, (from.0 + to.0) / 2.0, (from.1 + to.1) / 2.0, 1.0)).unwrap();
s.pointer(&PointerEvent::at(PointerKind::Move, to.0, to.1, 1.0)).unwrap();
s.pointer(&PointerEvent::at(PointerKind::Up, to.0, to.1, 1.0)).unwrap();
}
/// A frame with six 50×30 children in auto layout of `mode`.
fn frame_with_kids(s: &mut Session, props: serde_json::Value) -> (NodeId, Vec<NodeId>) {
let f = id(s.execute("frame.create", &json!({"x": 0, "y": 0, "width": 200, "height": 200})).unwrap());
let kids: Vec<NodeId> =
(0..6).map(|i| id(s.execute("shape.rectangle", &json!({"x": i * 60, "y": 0, "width": 50, "height": 30, "parent": f})).unwrap())).collect();
s.execute("layout.add", &json!({"ids": [f]})).unwrap();
s.execute("node.set", &json!({"id": f, "props": props})).unwrap();
s.execute("select.none", &json!({})).unwrap();
(f, kids)
}
#[test]
fn wrap_drag_reorders_by_row_and_position() {
let mut s = Session::new();
let (f, kids) = frame_with_kids(
&mut s,
json!({"layoutMode": "horizontal", "wrap": true, "width": 200, "sizingH": "fixed", "gap": 10, "crossGap": 10, "padding": 0}),
);
let b = |s: &Session, i: NodeId| s.doc().unwrap().world_bounds(i).unwrap();
// Three per row: kid 4 sits in the second row.
assert!(b(&s, kids[3]).y0 > b(&s, kids[0]).y0, "wrapped into two rows");
// Drag the first child into the second row, just before kid 5.
let from = b(&s, kids[0]).center();
let k5 = b(&s, kids[5]);
drag(&mut s, (from.x, from.y), (k5.x0 + 5.0, k5.center().y));
let order = s.doc().unwrap().node(f).unwrap().children.clone();
assert_eq!(order, vec![kids[1], kids[2], kids[3], kids[4], kids[0], kids[5]], "{order:?}");
// Right of a row's last child: after it.
let from = b(&s, kids[5]).center();
let k1 = b(&s, kids[2]);
drag(&mut s, (from.x, from.y), (k1.x1 + 4.0, k1.center().y));
let order = s.doc().unwrap().node(f).unwrap().children.clone();
assert_eq!(order.get(..3), Some(&[kids[1], kids[2], kids[5]][..]), "{order:?}");
}
#[test]
fn grid_drag_takes_the_cell_under_the_pointer() {
let mut s = Session::new();
let (f, kids) = frame_with_kids(&mut s, json!({"layoutMode": "grid", "gridColumns": 3, "gridRows": 2}));
let geometry = figcraft_layout::grid_geometry(s.doc().unwrap(), f).unwrap();
let (c2, r1) = (geometry.columns[2], geometry.rows[1]);
let to = s.doc().unwrap().world_transform(f) * figcraft_geom::Point::new((c2.0 + c2.1) / 2.0, (r1.0 + r1.1) / 2.0);
let from = s.doc().unwrap().world_bounds(kids[0]).unwrap().center();
drag(&mut s, (from.x, from.y), (to.x, to.y));
let cl = s.doc().unwrap().node(kids[0]).unwrap().child_layout.clone();
assert_eq!((cl.grid_column, cl.grid_row), (Some(2), Some(1)));
assert!(s.tool.overlay.drop_cell.is_none(), "the cell indicator clears on release");
s.undo().unwrap();
let cl = s.doc().unwrap().node(kids[0]).unwrap().child_layout.clone();
assert_eq!((cl.grid_column, cl.grid_row), (None, None), "one undo step");
}