mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-08 11:52:10 +00:00
Selection: with Real-time Drawing off, the bounding box moves with a move drag
With Preferences › Performance › Real-time Drawing and Editing off, a Selection tool move drag leaves the object in place until release, and the bounding box was hidden for the whole drag. The box, with its handles, now follows the pointer during the drag (Session::deferred_move gives the offset the document has not taken yet). Scale and rotate drags, and drags with Real-time Drawing on, hide the box as before. Docs: docs/target-app-parity.md.
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user