mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-27 20:06:53 -06:00
- 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>
72 lines
3.4 KiB
Rust
72 lines
3.4 KiB
Rust
//! 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");
|
||
}
|