Merge pull request #5208 from dotCooCoo/fix/bounding-box-moves-with-rtd-off

Selection: with Real-time Drawing off, the bounding box moves with a move drag
This commit is contained in:
BFlat
2026-11-02 14:53:07 +07:00
committed by GitHub
4 changed files with 74 additions and 1 deletions
+18
View File
@@ -572,6 +572,24 @@ impl Session {
self.tool.transforming()
}
/// With Real-time Drawing off, how far a Selection-tool move drag has carried the selection
/// so far: the document moves on release, and the bounding box shows at this offset meanwhile.
pub fn deferred_move(&self) -> Option<vectorcraft_geom::Vec2> {
if self.tool.id() != "selection" || !self.tool.transforming() {
return None;
}
let it = self.active()?.interaction.as_ref()?;
if !matches!(it.label.as_str(), "Move" | "Copy") {
return None;
}
let (cmd, p) = self.unapplied_interaction_preview()?;
if cmd != "object.transform" || p.get("objects").and_then(Value::as_bool) == Some(false) {
return None;
}
let [a, b, c, d, e, f] = crate::cmd::matrix_param(&p, "matrix")?.as_coeffs();
(a == 1.0 && b == 0.0 && c == 0.0 && d == 1.0).then(|| vectorcraft_geom::Vec2::new(e, f))
}
/// Does the active tool take `key` ahead of the shortcuts bound to it (see `Tool::claims_key`)?
pub fn tool_claims_key(&mut self, key: ToolKey, view: ViewInfo) -> bool {
self.plane_key(key).is_some() || self.with_tool_cx(view, |t, cx| t.claims_key(cx, key))
+8 -1
View File
@@ -92,11 +92,18 @@ fn curve_point(p: &egui::Painter, c: Pos2, color: Color32, filled: bool, size: f
pub(super) fn selection_overlay(app: &mut VectorcraftApp, p: &egui::Painter, xf: &Xf, curve_points_from_tool: bool) {
// The Selection tool's bounding box (rotated with the objects after a rotation). It hides while
// a drag moves or resizes the selection, so only the art is seen going with the pointer (#712).
// With Real-time Drawing off the art waits for the release of a move drag, and the box moves
// with the pointer instead.
let moved = app.session.deferred_move();
let show_box = app.session.tool_id() == "selection"
&& !app.session.tool_transforming()
&& (!app.session.tool_transforming() || moved.is_some())
&& app.ui.view.bounding_box
&& app.session.active().is_some_and(|st| !st.selection.is_empty() && st.selection.anchors.is_empty());
let bbox = if show_box { app.selection_box() } else { None };
let bbox = bbox.map(|b| match moved {
Some(d) => vectorcraft_doc::OrientedBox { rect: b.rect + b.to_local(d.to_point()).to_vec2(), ..b },
None => b,
});
let st = app.session.active();
let big = st.is_some_and(|st| too_many_anchors(st.selection.objects.iter().filter_map(|id| st.doc.node(*id))));
let big_bounds = if big { app.selection_bounds() } else { None };
+47
View File
@@ -1105,6 +1105,53 @@ fn a_guide_dragged_out_of_a_ruler_shows_while_dragged() {
}
}
/// With Preferences › Performance › Real-time Drawing and Editing off, the bounding box moves
/// with a Selection-tool move drag while the object stays put until the release. With it on, the
/// art moves and the box hides during the drag (#712).
#[test]
fn real_time_drawing_off_moves_the_bounding_box_with_a_move_drag() {
for real_time in [true, false] {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
app.session.execute("prefs.set", &json!({"key": "realTimeDrawing", "value": real_time})).unwrap();
app.ui.task_bar = false;
app.ui.view.rulers = false;
app.ui.view.smart_guides = false;
app.ui.view.snap_to_point = false;
let id = app.run("shape.rectangle", json!({"x": 100, "y": 100, "width": 100, "height": 60})).unwrap()["id"].clone();
app.run("select.set", json!({"ids": [id]})).unwrap();
app.select_tool("selection");
let ctx = egui::Context::default();
frame(&mut app, &ctx, vec![]);
let xf = Xf::new(app.canvas_rect.unwrap(), app.view().unwrap());
let is_handle = |s: &Shape| matches!(s, Shape::Rect(r) if (r.rect.width() - 6.0).abs() < 1.0 && (r.rect.height() - 6.0).abs() < 1.0 && r.fill == Color32::WHITE);
let handles = |app: &mut VectorcraftApp| {
shapes(app, &ctx).iter().filter(|s| is_handle(s)).map(|s| s.visual_bounding_rect().center()).collect::<Vec<_>>()
};
let at_rest = handles(&mut app);
assert_eq!(at_rest.len(), 8, "real-time drawing {real_time}: the box's handles at rest");
let left = |app: &VectorcraftApp| app.session.active().unwrap().doc.art_bounds().unwrap().x0;
let start = left(&app);
let view = app.view_info();
app.session.pointer(&PointerEvent::new(PointerKind::Down, 150.0, 130.0), view).unwrap();
app.session.pointer(&PointerEvent::new(PointerKind::Drag, 250.0, 230.0), view).unwrap();
let during = handles(&mut app);
if real_time {
assert!(during.is_empty(), "the box hides while the art moves: {during:?}");
assert_eq!(left(&app), start + 100.0, "the art moves with the pointer");
} else {
assert_eq!(during.len(), 8, "the box shows during the drag");
let corner = xf.to_screen(Point::new(200.0, 200.0));
assert!(during.iter().any(|c| c.distance(corner) < 0.5), "the box's corner is at the dragged position: {during:?}");
assert!(!during.iter().any(|c| c.distance(xf.to_screen(Point::new(100.0, 100.0))) < 0.5), "not at the start");
assert_eq!(left(&app), start, "the object waits for the release");
}
app.session.pointer(&PointerEvent::new(PointerKind::Up, 250.0, 230.0), view).unwrap();
assert_eq!(left(&app), start + 100.0, "real-time drawing {real_time}: moved on release");
assert_eq!(handles(&mut app).len(), 8, "real-time drawing {real_time}: the box at rest again");
}
}
#[test]
fn new_ruler_guides_follow_alt_through_preview_release_and_undo() {
use egui::{Event, Modifiers};
File diff suppressed because one or more lines are too long