Objects: dragging an object's body sends its position only, not its size

`drag_object` sent `width` and `height` with every step of a body drag,
so moving a picture a file made 12,000 wide shrank it to the 10,000 that
`object.move` gives a new size. Only a handle drag sends a size now; a
body move leaves the engine's size alone.

Tests (real input): dragging_the_body_of_an_oversized_imported_object_keeps_its_size
reopens the workbook with a 12,000 by 60 picture, drags its body by
30 by 25 and checks the size is untouched, the picture moved with the
pointer and the journaled `object.move` carries no width or height; it
fails without the fix (the journaled command carries both, and without
the engine's cap the picture is 10,000 wide).
a_handle_drag_on_an_oversized_imported_object_resizes_by_the_drag_only
moves the left handle 10 px right: the picture is 10 points narrower in
the window and keeps a width near 12,000, with no jump to the limit.
This commit is contained in:
prxnvt committed 2026-11-02 01:58:23 -05:00
1 parent 381a121339
commit af28f01bfd
2 files changed
+72 -2

No files matched your search

+10 -2
View File
@@ -974,11 +974,19 @@ fn drag_object(app: &mut SheetApp, geo: &Geo, sh: &Sheet, p: Pos2) {
};
// Re-anchor at the cell under the new top-left.
let nc = CellRef::new(sh.row_at(y0 as f64), sh.col_at(x0 as f64));
let params = json!({
let mut params = json!({
"kind": obj.kind.name(), "id": obj.id, "at": nc.a1(),
"dx": x0 - sh.col_left(nc.col) as f32, "dy": y0 - sh.row_top(nc.row) as f32,
"width": x1 - x0, "height": y1 - y0,
});
// Only a handle changes the size. A body move sends none, so the engine keeps whatever the
// object has: a file can make it larger than `object.move` accepts for a new size, and
// moving it must not shrink it to that limit.
if handle.is_some()
&& let Some(p) = params.as_object_mut()
{
p.insert("width".into(), json!(x1 - x0));
p.insert("height".into(), json!(y1 - y0));
}
let revision = app.session.active().map(|d| d.revision);
let _ = app.session.run("object.move", params);
if stepped {
+62
View File
@@ -362,3 +362,65 @@ fn dragging_an_unselected_object_by_its_edge_moves_it() {
assert!((after.size() - before.size()).length() < 0.5, "not resized: {before:?} {after:?}");
assert!((after.min - before.min - vec2(30.0, 20.0)).length() < 1.0, "moved with the pointer: {before:?} {after:?}");
}
fn anchor(h: &Harness, o: ObjRef) -> gridcraft_engine::model::Anchor {
h.state().session.doc().unwrap().wb.active().unwrap().object_anchor(o).unwrap()
}
/// Makes the fixture's picture 12,000 by 60 and reopens the workbook from its XLSX bytes, the way
/// a file can carry an object wider than the 10,000 points `object.move` gives a new size.
/// Returns the picture as reopened.
fn imported_oversized_picture(h: &mut Harness, o: &Objects) -> ObjRef {
{
let wb = std::sync::Arc::make_mut(&mut h.state_mut().session.doc_mut().unwrap().wb);
let a = wb.sheet_mut(0).unwrap().object_anchor_mut(o.image).unwrap();
a.width = 12_000.0;
a.height = 60.0;
}
let saved = h.state_mut().run("file.saveBytes", json!({"format": "xlsx"})).unwrap();
h.state_mut().run("file.open", json!({"name": "wide.xlsx", "base64": saved["base64"]})).unwrap();
h.run_steps(4);
let pic = ObjRef::new(ObjKind::Image, h.state().session.doc().unwrap().wb.active().unwrap().images[0].id);
let a = anchor(h, pic);
assert_eq!((a.width.round(), a.height.round()), (12_000.0, 60.0), "the reader keeps the file's size: {a:?}");
pic
}
/// Dragging the body of such a picture moves it and sends no size, so nothing shrinks it to the
/// limit on the way.
#[test]
fn dragging_the_body_of_an_oversized_imported_object_keeps_its_size() {
let (mut h, o) = fixture();
let pic = imported_oversized_picture(&mut h, &o);
let before = rect(&h, pic);
// Well inside the body, on the part of the picture in view and clear of the handles' reach.
let grab = before.left_top() + vec2(30.0, 30.0);
click(&mut h, grab);
assert_eq!(h.state().selected_object, Some(pic));
drag(&mut h, grab, grab + vec2(30.0, 25.0));
let a = anchor(&h, pic);
assert_eq!((a.width.round(), a.height.round()), (12_000.0, 60.0), "the size is untouched: {a:?}");
let after = rect(&h, pic);
assert!((after.min - before.min - vec2(30.0, 25.0)).length() < 1.0, "moved with the pointer: {before:?} {after:?}");
// The drag sent the position only: the engine saw no size to cap.
let (_, p) = h.state().session.journal.iter().rev().find(|(id, _)| id == "object.move").unwrap();
assert!(p.get("width").is_none() && p.get("height").is_none(), "a body move sends no size: {p}");
}
/// A handle drag on such a picture resizes it by the drag only: the left handle moved 10 px to
/// the right leaves it 11,990 wide, with no jump to 10,000.
#[test]
fn a_handle_drag_on_an_oversized_imported_object_resizes_by_the_drag_only() {
let (mut h, o) = fixture();
let pic = imported_oversized_picture(&mut h, &o);
let before = rect(&h, pic);
click(&mut h, before.left_top() + vec2(30.0, 30.0));
let grab = Handle::Left.pos(before);
drag(&mut h, grab, grab + vec2(10.0, 0.0));
// In window points (the grid draws at its zoom): 10 narrower, the left edge 10 further right.
let after = rect(&h, pic);
assert!((before.width() - after.width() - 10.0).abs() < 1.0, "narrower by the drag only: {before:?} {after:?}");
assert!((after.left() - before.left() - 10.0).abs() < 1.0 && (after.top() - before.top()).abs() < 0.5, "{before:?} {after:?}");
let a = anchor(&h, pic);
assert!(a.width > 11_900.0 && a.width < 12_000.0 && a.height.round() == 60.0, "no jump to the 10,000 limit: {a:?}");
}