Objects: only the selected object's handles resize; a press elsewhere on an object moves it

A primary drag looked for a handle under the press on whatever object it
grabbed, so pressing near the edge or corner of an object that was not
selected started a resize, while the pointer over that spot showed the
Move cursor (handles are only shown, and only offered by the cursor, on
the selected object). The press now takes its handle from
`chartview::selected_handle`, the same lookup the cursor uses; a press on
any other object selects and moves it.

Test: dragging an unselected shape by its bottom-right corner shows the
Move cursor, then selects and moves the shape without resizing it.
This commit is contained in:
prxnvt committed 2026-10-31 12:53:46 -04:00
1 parent f3fbaa5568
commit 548e5345ee
2 files changed
+23 -3

No files matched your search

+5 -3
View File
@@ -124,16 +124,18 @@ pub(super) fn interact(app: &mut SheetApp, ui: &mut egui::Ui, resp: &egui::Respo
// A drag is recognised only after the pointer has moved, so grab the object where the press began.
let at = if primary_drag { pointer.press_origin().map(|p| geo.mirror_pos(p)).or(pos) } else { pos };
// The selected object's handles reach a little outside it, so they win over hit-testing.
let on_handle = at.and_then(|p| crate::chartview::selected_handle(app, geo, sh, p)).map(|(o, r, _)| (o, r));
// Only they resize: a press anywhere on another object selects and moves it, as the
// hover cursor shows.
let on_handle = at.and_then(|p| crate::chartview::selected_handle(app, geo, sh, p));
let handle = on_handle.map(|(.., h)| h);
if let Some(p) = at
&& let Some((obj, rect)) = on_handle.or_else(|| crate::chartview::hit(app, geo, sh, p))
&& let Some((obj, _)) = on_handle.map(|(o, r, _)| (o, r)).or_else(|| crate::chartview::hit(app, geo, sh, p))
{
app.selected_object = Some(obj);
if resp.double_clicked() && app.open_object(obj) != crate::objects::OpenAction::Select {
return;
}
if primary_drag && let Some(a) = sh.object_anchor(obj) {
let handle = crate::objects::handle_at(rect, p);
app.grid.drag = Drag::Object { obj, start: p, orig: (a.cell, a.dx, a.dy, a.width, a.height), handle, stepped: false };
// The first recognised drag frame already holds movement away from the press.
if let Some(now) = pos {
+18
View File
@@ -344,3 +344,21 @@ fn each_drag_of_an_object_is_its_own_undo_step() {
h.run_steps(2);
assert_eq!(rect(&h, o.image), start, "a second undo takes back the move");
}
#[test]
fn dragging_an_unselected_object_by_its_edge_moves_it() {
let (mut h, o) = fixture();
assert_eq!(h.state().selected_object, None);
let before = rect(&h, o.shape);
// On the bottom-right corner, where the handle would be if the shape were selected; the
// pointer shows Move there, and the press selects and moves the shape.
let grab = Handle::BottomRight.pos(before) - vec2(2.0, 2.0);
h.input_mut().events.push(Event::PointerMoved(grab));
h.run_steps(2);
assert_eq!(h.output().platform_output.cursor_icon, egui::CursorIcon::Move);
drag(&mut h, grab, grab + vec2(30.0, 20.0));
let after = rect(&h, o.shape);
assert_eq!(h.state().selected_object, Some(o.shape));
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:?}");
}