mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-07 02:51:33 +00:00
Objects: a resize handle under the headers leaves the press to them
A selected object's handles reach a few points outside it, and a press was matched against them before any check of where it landed, so a press on the column or row header beside an object at the sheet's edge resized the object instead of selecting the column or row. The cursor already ignored handles outside the cells. `chartview::selected_handle` finds the selected object's handle under a point only over the cell area, as `hit` does, and not while objects are hidden; the press and the cursor both use it. Test: presses within a handle's reach over the column header and the row header neither move nor resize the object.
This commit is contained in:
1 parent
de8f1854f1
commit
028ce2b417
4 files changed
+39
-9
No files matched your search
@@ -505,6 +505,19 @@ pub fn object_rect(geo: &Geo, sh: &Sheet, o: ObjRef) -> Option<Rect> {
|
||||
sh.object_anchor(o).map(|a| anchor_rect(geo, sh, &a))
|
||||
}
|
||||
|
||||
/// The resize handle of the selected object under `p`, with the object and its rect. Handles
|
||||
/// reach a little outside their object, so they win over [`hit`], but like it only over the cell
|
||||
/// area: a handle that lies under the headers, the formula bar or a scroll bar leaves the press
|
||||
/// to them. Nothing while objects are hidden.
|
||||
pub fn selected_handle(app: &SheetApp, geo: &Geo, sh: &Sheet, p: Pos2) -> Option<(ObjRef, Rect, crate::objects::Handle)> {
|
||||
if !geo.cells.contains(p) || app.session.active().is_some_and(|d| objects_hidden(&d.wb)) {
|
||||
return None;
|
||||
}
|
||||
let o = app.selected_object?;
|
||||
let r = object_rect(geo, sh, o)?;
|
||||
crate::objects::handle_at(r, p).map(|h| (o, r, h))
|
||||
}
|
||||
|
||||
/// The topmost object under `p` and its rect: slicers paint on top, so they are hit first, then
|
||||
/// charts, pictures and shapes, each from the top of its stack. Nothing is hit outside the cell
|
||||
/// area or while objects are hidden.
|
||||
|
||||
@@ -78,11 +78,7 @@ fn hover_cursor(app: &SheetApp, ctx: &egui::Context, resp: &egui::Response, poin
|
||||
let on_cur_border = on_border(cur_rect, p) && border_panes.iter().any(|r| r.contains(p));
|
||||
// The selected object's handles resize it; any object's body moves it.
|
||||
let idle = app.grid.drag == Drag::None && app.editor.is_none();
|
||||
let object_handle = if idle && in_cells {
|
||||
app.selected_object.and_then(|o| crate::chartview::object_rect(geo, sh, o)).and_then(|r| crate::objects::handle_at(r, p))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let object_handle = if idle { crate::chartview::selected_handle(app, geo, sh, p).map(|(.., h)| h) } else { None };
|
||||
let on_object = idle && crate::chartview::hit(app, geo, sh, p).is_some();
|
||||
if let Some(h) = object_handle {
|
||||
ctx.set_cursor_icon(h.cursor(geo.rtl));
|
||||
|
||||
@@ -124,10 +124,7 @@ 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 = app.selected_object.and_then(|o| {
|
||||
let r = crate::chartview::object_rect(geo, sh, o)?;
|
||||
at.and_then(|p| crate::objects::handle_at(r, p)).map(|_| (o, r))
|
||||
});
|
||||
let on_handle = at.and_then(|p| crate::chartview::selected_handle(app, geo, sh, p)).map(|(o, r, _)| (o, r));
|
||||
if let Some(p) = at
|
||||
&& let Some((obj, rect)) = on_handle.or_else(|| crate::chartview::hit(app, geo, sh, p))
|
||||
{
|
||||
|
||||
@@ -300,3 +300,27 @@ fn on_a_right_to_left_sheet_the_mirrored_handles_resize() {
|
||||
assert!((after.left() - (before.left() - 20.0)).abs() < 1.0 && (after.right() - before.right()).abs() < 1.0, "{before:?} {after:?}");
|
||||
assert!((after.bottom() - (before.bottom() + 16.0)).abs() < 1.0 && (after.top() - before.top()).abs() < 1.0, "{before:?} {after:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_handle_under_the_headers_does_not_take_the_press() {
|
||||
let (mut h, o) = fixture();
|
||||
h.state_mut().run("object.move", json!({"kind": "shape", "id": o.shape.id, "at": "A1", "dx": 0, "dy": 0})).unwrap();
|
||||
h.run_steps(2);
|
||||
let before = rect(&h, o.shape);
|
||||
click(&mut h, before.center());
|
||||
assert_eq!(h.state().selected_object, Some(o.shape));
|
||||
let cells = h.state().grid.cells_rect.unwrap();
|
||||
assert_eq!(before.left_top(), cells.left_top(), "the shape's top-left handle sits on the header corner");
|
||||
// Within the top-left handle's reach, but over the column header: the press is the header's.
|
||||
let grab = Handle::TopLeft.pos(before) + vec2(4.0, -4.0);
|
||||
assert!(grab.y < cells.top());
|
||||
drag(&mut h, grab, grab + vec2(30.0, 40.0));
|
||||
assert_eq!(rect(&h, o.shape), before, "the shape is neither moved nor resized");
|
||||
assert_eq!(h.state().selected_object, None, "the press went to the column header");
|
||||
// Within the left handle's reach, but over the row header.
|
||||
let grab = Handle::Left.pos(before) + vec2(-4.0, 0.0);
|
||||
assert!(grab.x < cells.left());
|
||||
h.state_mut().selected_object = Some(o.shape);
|
||||
drag(&mut h, grab, grab + vec2(40.0, 0.0));
|
||||
assert_eq!(rect(&h, o.shape), before, "the shape is neither moved nor resized");
|
||||
}
|
||||
Reference in new issue
Block a user