From 4a71ee5d83f5a733dec7a36446bc0a93baa5ef15 Mon Sep 17 00:00:00 2001 From: moppeeer Date: Thu, 29 Oct 2026 13:22:55 +0100 Subject: [PATCH] rustfmt Co-Authored-By: Claude Opus 5.5 --- crates/tools/src/select.rs | 140 ++++++++--------- crates/ui-egui/src/panels/properties/tests.rs | 146 +++++++++--------- 2 files changed, 143 insertions(+), 143 deletions(-) diff --git a/crates/tools/src/select.rs b/crates/tools/src/select.rs index d94b72560..e1161fce9 100644 --- a/crates/tools/src/select.rs +++ b/crates/tools/src/select.rs @@ -782,51 +782,51 @@ impl SelectionTool { && let Some((id, si, ai, handle)) = anchor_at(cx, p) { let at = anchor_spread_point(cx, id, si, ai); - let mut out = vec![]; - let mut others = vec![]; - if handle.is_none() { - // InDesign's anchor point selection: a click selects the point, Shift+click - // adds it or takes it away, and dragging a selected point moves all of them. - let me = (ItemId(id), si, ai); - let mut points = cx.selection.anchors.clone(); - let was = points.contains(&me); - if ev.mods.shift && was { - // Kept selected so a Shift-drag can constrain it; a click still takes it - // away, on release, as for objects. - self.anchor_release = Some(me); - } else if ev.mods.shift { - points.push(me); - } else if !was { - points = vec![me]; - } - if points != cx.selection.anchors { - out.push(Action::Exec("selection.set".into(), anchor_selection(cx, &points, ev.mods.shift))); - } - others = points.iter().filter(|a| **a != me).map(|(i, s, a)| (i.0, *s, *a)).collect(); - } - let label = if handle.is_some() { - "Move Direction Handle" - } else if others.is_empty() { - "Move Anchor" - } else { - "Move Anchors" - }; - self.drag = Drag::Anchor { id, si, ai, handle, start: p, at, others }; - out.push(Action::Begin(label.into())); - return out; + let mut out = vec![]; + let mut others = vec![]; + if handle.is_none() { + // InDesign's anchor point selection: a click selects the point, Shift+click + // adds it or takes it away, and dragging a selected point moves all of them. + let me = (ItemId(id), si, ai); + let mut points = cx.selection.anchors.clone(); + let was = points.contains(&me); + if ev.mods.shift && was { + // Kept selected so a Shift-drag can constrain it; a click still takes it + // away, on release, as for objects. + self.anchor_release = Some(me); + } else if ev.mods.shift { + points.push(me); + } else if !was { + points = vec![me]; } - // As in InDesign, the Direct Selection tool pressed on a corner or anchor of an object - // that isn't selected selects it and moves just that point (Chris Nielsen's "The - // InDesign Toolbox", 10:10: click outside, then pull a frame's corner). - if self.direct - && !ev.mods.shift - && let Some((_, hit)) = cx.hit(p) - && !cx.selection.contains(hit) - && let Some((id, si, ai, None)) = anchor_at_in(cx, &[hit], p) - { - let at = anchor_spread_point(cx, id, si, ai); - self.drag = Drag::Anchor { id, si, ai, handle: None, start: p, at, others: vec![] }; - return vec![Action::Exec("selection.set".into(), json!({"ids": [id], "content": true})), Action::Begin("Move Anchor".into())]; + if points != cx.selection.anchors { + out.push(Action::Exec("selection.set".into(), anchor_selection(cx, &points, ev.mods.shift))); + } + others = points.iter().filter(|a| **a != me).map(|(i, s, a)| (i.0, *s, *a)).collect(); + } + let label = if handle.is_some() { + "Move Direction Handle" + } else if others.is_empty() { + "Move Anchor" + } else { + "Move Anchors" + }; + self.drag = Drag::Anchor { id, si, ai, handle, start: p, at, others }; + out.push(Action::Begin(label.into())); + return out; + } + // As in InDesign, the Direct Selection tool pressed on a corner or anchor of an object + // that isn't selected selects it and moves just that point (Chris Nielsen's "The + // InDesign Toolbox", 10:10: click outside, then pull a frame's corner). + if self.direct + && !ev.mods.shift + && let Some((_, hit)) = cx.hit(p) + && !cx.selection.contains(hit) + && let Some((id, si, ai, None)) = anchor_at_in(cx, &[hit], p) + { + let at = anchor_spread_point(cx, id, si, ai); + self.drag = Drag::Anchor { id, si, ai, handle: None, start: p, at, others: vec![] }; + return vec![Action::Exec("selection.set".into(), json!({"ids": [id], "content": true})), Action::Begin("Move Anchor".into())]; } // A selected placed graphic's own handles resize it inside its frame (the // frame's anchors, under Direct Selection, come first). @@ -1165,18 +1165,18 @@ impl SelectionTool { // Screen y points down: a positive screen angle is clockwise. vec![Action::Preview("transform.rotate".into(), json!({"angle": -a}))] } - Drag::Anchor { id, si, ai, handle, start, at, others } => { - let mut d = p - start; - if d.hypot() >= cx.tol(3.0) { - self.anchor_release = None; - } else if self.anchor_release.is_some() { - // A Shift-press on a selected point is still a click: nothing moves yet. - return vec![]; - } - // Shift: horizontal, vertical or at 45°. - if ev.mods.shift { - d = constrain_45(d); - } + Drag::Anchor { id, si, ai, handle, start, at, others } => { + let mut d = p - start; + if d.hypot() >= cx.tol(3.0) { + self.anchor_release = None; + } else if self.anchor_release.is_some() { + // A Shift-press on a selected point is still a click: nothing moves yet. + return vec![]; + } + // Shift: horizontal, vertical or at 45°. + if ev.mods.shift { + d = constrain_45(d); + } let mut dx = d.x; let mut dy = d.y; self.guides.clear(); @@ -1204,25 +1204,25 @@ impl SelectionTool { }, ); // dx and dy stay in canvas space. Snap.delta is spread space. - let mut corr = spread_delta_to_canvas(cx.layout.xf(spread), hit.delta); - corr = Vec2::new(if corr.x.is_finite() { corr.x } else { 0.0 }, if corr.y.is_finite() { corr.y } else { 0.0 }); - // Shift: the snap moves the points along the constrained direction only. - if ev.mods.shift && d.hypot() > 1e-9 { - let dir = d / d.hypot(); - corr = dir * corr.dot(dir); + let mut corr = spread_delta_to_canvas(cx.layout.xf(spread), hit.delta); + corr = Vec2::new(if corr.x.is_finite() { corr.x } else { 0.0 }, if corr.y.is_finite() { corr.y } else { 0.0 }); + // Shift: the snap moves the points along the constrained direction only. + if ev.mods.shift && d.hypot() > 1e-9 { + let dir = d / d.hypot(); + corr = dir * corr.dot(dir); } - dx += corr.x; - dy += corr.y; + dx += corr.x; + dy += corr.y; self.guides = hit.guides; } - // The command moves in spread space: each point by the drag in its own spread. - let own = at.map_or(Vec2::new(dx, dy), |(spread, _)| cx.layout.delta_to_spread(spread, Vec2::new(dx, dy))); - let mut params = json!({"id": id, "anchors": [[si, ai]], "dx": own.x, "dy": own.y}); + // The command moves in spread space: each point by the drag in its own spread. + let own = at.map_or(Vec2::new(dx, dy), |(spread, _)| cx.layout.delta_to_spread(spread, Vec2::new(dx, dy))); + let mut params = json!({"id": id, "anchors": [[si, ai]], "dx": own.x, "dy": own.y}); if let Some(h) = handle { params["handle"] = json!(h); - } else if !others.is_empty() { - let points = std::iter::once((id, si, ai)).chain(others).map(|(i, s, a)| (ItemId(i), s, a)); - params = json!({"points": moved_points(cx, points, Vec2::new(dx, dy))}); + } else if !others.is_empty() { + let points = std::iter::once((id, si, ai)).chain(others).map(|(i, s, a)| (ItemId(i), s, a)); + params = json!({"points": moved_points(cx, points, Vec2::new(dx, dy))}); } vec![Action::Preview("path.moveAnchors".into(), params)] } diff --git a/crates/ui-egui/src/panels/properties/tests.rs b/crates/ui-egui/src/panels/properties/tests.rs index 025368deb..ef4519688 100644 --- a/crates/ui-egui/src/panels/properties/tests.rs +++ b/crates/ui-egui/src/panels/properties/tests.rs @@ -786,82 +786,82 @@ fn apply_to_text_colours_a_text_frames_text_not_the_frame() { assert_eq!(doc.item(designcraft_doc::ids::ItemId(id)).unwrap().fill.swatch, "[None]", "the frame isn't"); } - /// Points selected with Direct Selection: InDesign's "Anchor Point(s)" panel, with Convert - /// Points and the Delete Point(s) quick action, as in the Zero2Graphic tutorial (1:25). - #[test] - fn selected_points_show_convert_points_and_delete_points() { - let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default()); - app.run("file.new", json!({})).unwrap(); - let a = app.run("frame.create", json!({"rect": [72, 72, 172, 172]})).unwrap()["id"].as_u64().unwrap(); - let b = app.run("frame.create", json!({"rect": [200, 72, 300, 172]})).unwrap()["id"].as_u64().unwrap(); - app.run("selection.set", json!({"anchors": [[a, 0, 0], [b, 0, 0]]})).unwrap(); - assert_eq!(selection_title(&app, &super::super::sel_info(&app)), "Anchor Point(s)"); - let mut h = panel(app); - // The four point types (Appearance has a Corner row too). - for tip in ["Plain", "Corner", "Smooth", "Symmetrical"] { - assert!(h.get_all_by_label(tip).next().is_some(), "{tip}"); - } - assert!(h.query_by_label("Align").is_none(), "only the sections InDesign shows for points"); - h.get_by_label("Delete Point(s)").click(); - h.run_steps(3); - let d = &h.state().session.active().unwrap().doc; - for id in [a, b] { - assert_eq!(d.item(designcraft_doc::ids::ItemId(id)).unwrap().path.subpaths[0].anchors.len(), 3, "one point gone from each"); - } +/// Points selected with Direct Selection: InDesign's "Anchor Point(s)" panel, with Convert +/// Points and the Delete Point(s) quick action, as in the Zero2Graphic tutorial (1:25). +#[test] +fn selected_points_show_convert_points_and_delete_points() { + let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default()); + app.run("file.new", json!({})).unwrap(); + let a = app.run("frame.create", json!({"rect": [72, 72, 172, 172]})).unwrap()["id"].as_u64().unwrap(); + let b = app.run("frame.create", json!({"rect": [200, 72, 300, 172]})).unwrap()["id"].as_u64().unwrap(); + app.run("selection.set", json!({"anchors": [[a, 0, 0], [b, 0, 0]]})).unwrap(); + assert_eq!(selection_title(&app, &super::super::sel_info(&app)), "Anchor Point(s)"); + let mut h = panel(app); + // The four point types (Appearance has a Corner row too). + for tip in ["Plain", "Corner", "Smooth", "Symmetrical"] { + assert!(h.get_all_by_label(tip).next().is_some(), "{tip}"); } + assert!(h.query_by_label("Align").is_none(), "only the sections InDesign shows for points"); + h.get_by_label("Delete Point(s)").click(); + h.run_steps(3); + let d = &h.state().session.active().unwrap().doc; + for id in [a, b] { + assert_eq!(d.item(designcraft_doc::ids::ItemId(id)).unwrap().path.subpaths[0].anchors.len(), 3, "one point gone from each"); + } +} - /// With points selected, Transform measures and moves the points: one corner of a frame shows - /// that corner, not the frame, and X moves the corner alone; W spreads two points apart. - #[test] - fn transform_measures_and_moves_the_selected_points() { - let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default()); - app.run("file.new", json!({})).unwrap(); - let a = app.run("frame.create", json!({"rect": [72, 72, 172, 172]})).unwrap()["id"].as_u64().unwrap(); - let corner = |app: &DesignApp, i: usize| { - let d = &app.session.active().unwrap().doc; - let it = d.item(designcraft_doc::ids::ItemId(a)).unwrap(); - it.xf * it.path.subpaths[0].anchors[i].p - }; - let first = corner(&app, 0); - app.run("selection.set", json!({"anchors": [[a, 0, 0]]})).unwrap(); - let i = super::super::sel_info(&app).unwrap(); - assert_eq!((i.page_rect.width(), i.page_rect.height()), (0.0, 0.0), "a point, not the frame"); - app.ui.ref_point = 0; - transform_anchors(&mut app, &json!({"x": i.page_rect.x0 + 10.0})).unwrap(); - assert_eq!(corner(&app, 0), first + designcraft_geom::Vec2::new(10.0, 0.0), "the corner moved"); - assert_eq!(corner(&app, 2), designcraft_geom::Point::new(172.0, 172.0), "the opposite corner stayed"); - // Two points 100 pt apart, W 200 from the left: the right one goes 100 further. - app.run("selection.set", json!({"anchors": [[a, 0, 2], [a, 0, 3]]})).unwrap(); - let (p2, p3) = (corner(&app, 2), corner(&app, 3)); - let w = super::super::sel_info(&app).unwrap().page_rect.width(); - transform_anchors(&mut app, &json!({"width": w * 2.0})).unwrap(); - let (left, right) = if p2.x < p3.x { (2, 3) } else { (3, 2) }; - let (pl, pr) = if left == 2 { (p2, p3) } else { (p3, p2) }; - assert_eq!(corner(&app, left), pl, "the reference side stays"); - assert!((corner(&app, right).x - (pr.x + w)).abs() < 1e-9, "spread to twice the width"); - } +/// With points selected, Transform measures and moves the points: one corner of a frame shows +/// that corner, not the frame, and X moves the corner alone; W spreads two points apart. +#[test] +fn transform_measures_and_moves_the_selected_points() { + let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default()); + app.run("file.new", json!({})).unwrap(); + let a = app.run("frame.create", json!({"rect": [72, 72, 172, 172]})).unwrap()["id"].as_u64().unwrap(); + let corner = |app: &DesignApp, i: usize| { + let d = &app.session.active().unwrap().doc; + let it = d.item(designcraft_doc::ids::ItemId(a)).unwrap(); + it.xf * it.path.subpaths[0].anchors[i].p + }; + let first = corner(&app, 0); + app.run("selection.set", json!({"anchors": [[a, 0, 0]]})).unwrap(); + let i = super::super::sel_info(&app).unwrap(); + assert_eq!((i.page_rect.width(), i.page_rect.height()), (0.0, 0.0), "a point, not the frame"); + app.ui.ref_point = 0; + transform_anchors(&mut app, &json!({"x": i.page_rect.x0 + 10.0})).unwrap(); + assert_eq!(corner(&app, 0), first + designcraft_geom::Vec2::new(10.0, 0.0), "the corner moved"); + assert_eq!(corner(&app, 2), designcraft_geom::Point::new(172.0, 172.0), "the opposite corner stayed"); + // Two points 100 pt apart, W 200 from the left: the right one goes 100 further. + app.run("selection.set", json!({"anchors": [[a, 0, 2], [a, 0, 3]]})).unwrap(); + let (p2, p3) = (corner(&app, 2), corner(&app, 3)); + let w = super::super::sel_info(&app).unwrap().page_rect.width(); + transform_anchors(&mut app, &json!({"width": w * 2.0})).unwrap(); + let (left, right) = if p2.x < p3.x { (2, 3) } else { (3, 2) }; + let (pl, pr) = if left == 2 { (p2, p3) } else { (p3, p2) }; + assert_eq!(corner(&app, left), pl, "the reference side stays"); + assert!((corner(&app, right).x - (pr.x + w)).abs() < 1e-9, "spread to twice the width"); +} - /// One point has no size: W and H show its frame's, greyed out, as InDesign; two points - /// measure (and set) their own. - #[test] - fn one_selected_point_shows_its_frames_size_greyed() { - let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default()); - app.run("file.new", json!({})).unwrap(); - let a = app.run("frame.create", json!({"rect": [72, 72, 172, 222]})).unwrap()["id"].as_u64().unwrap(); - app.run("selection.set", json!({"anchors": [[a, 0, 0]]})).unwrap(); - assert_eq!(super::super::sel_info(&app).unwrap().object_size, (100.0, 150.0)); - let mut h = panel(app); - h.run_steps(2); - let greyed = |h: &Harness<'_, DesignApp>| { - h.query_all_by(|n| n.role() == egui::accesskit::Role::TextInput && n.is_disabled()).filter_map(|n| n.value()).collect::>() - }; - let u = units(h.state()); - let f = |v| designcraft_geom::units::format_measure(v, u); - assert_eq!(greyed(&h), [f(100.0), f(150.0)], "the frame's W and H, greyed"); - h.state_mut().run("selection.set", json!({"anchors": [[a, 0, 0], [a, 0, 2]]})).unwrap(); - h.run_steps(2); - assert!(greyed(&h).is_empty(), "two points have a size of their own"); - } +/// One point has no size: W and H show its frame's, greyed out, as InDesign; two points +/// measure (and set) their own. +#[test] +fn one_selected_point_shows_its_frames_size_greyed() { + let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default()); + app.run("file.new", json!({})).unwrap(); + let a = app.run("frame.create", json!({"rect": [72, 72, 172, 222]})).unwrap()["id"].as_u64().unwrap(); + app.run("selection.set", json!({"anchors": [[a, 0, 0]]})).unwrap(); + assert_eq!(super::super::sel_info(&app).unwrap().object_size, (100.0, 150.0)); + let mut h = panel(app); + h.run_steps(2); + let greyed = |h: &Harness<'_, DesignApp>| { + h.query_all_by(|n| n.role() == egui::accesskit::Role::TextInput && n.is_disabled()).filter_map(|n| n.value()).collect::>() + }; + let u = units(h.state()); + let f = |v| designcraft_geom::units::format_measure(v, u); + assert_eq!(greyed(&h), [f(100.0), f(150.0)], "the frame's W and H, greyed"); + h.state_mut().run("selection.set", json!({"anchors": [[a, 0, 0], [a, 0, 2]]})).unwrap(); + h.run_steps(2); + assert!(greyed(&h).is_empty(), "two points have a size of their own"); +} #[test] fn transform_flips_and_rotates_from_properties() {