mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-09 12:03:46 +00:00
M12: connectors attached to a side start 4 pt outside the border (#1916)
A connector end attached to a side was routed from the middle of that side, so the stroke's centre line started on the border and half its width plus the round cap lay on the fill, and several connectors leaving one side piled up into a knot sticking into the card. FigJam starts the line a few pixels outside the border and connectors leaving one side run together along the side's normal before they fan out (the issue's screenshots). `board_route::END_GAP` (4 pt): an end attached to a side (`RouteEnd::Box`) starts that far outside it along the side's normal; an end placed by hand on the object (`RouteEnd::On`) keeps its exact point; a free end is unchanged. The pinned route coordinates in the engine, format and UI tests move with it; the overlay's attach points stay on the border. Docs: the Connectors rows, status line and history of boards-parity.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
4bb35d5266
commit
2dc7bed2a0
13 files changed
+136
-53
No files matched your search
@@ -2,7 +2,7 @@
|
||||
//! command and a drag of the selected connector's end), curved connectors, and elbows that go
|
||||
//! around the objects they join.
|
||||
|
||||
use figcraft_doc::{BoardItem, End, NodeKind, Routing, Side};
|
||||
use figcraft_doc::{BoardItem, END_GAP, End, NodeKind, Routing, Side};
|
||||
use figcraft_geom::{Point, Rect};
|
||||
use serde_json::json;
|
||||
|
||||
@@ -27,7 +27,7 @@ fn reattach_moves_one_end_and_undoes() {
|
||||
s.execute("board.reattach", &json!({"id": k, "end": "to", "target": c, "side": "top"})).unwrap();
|
||||
assert_eq!(connector(&s, k).to, End::Node { node: c, side: Side::Top, at: None });
|
||||
assert_eq!(connector(&s, k).from.node(), Some(a));
|
||||
assert!(near(*drawn(&s, k).last().unwrap(), Point::new(650.0, 0.0)), "arrives on c's top: {:?}", drawn(&s, k));
|
||||
assert!(near(*drawn(&s, k).last().unwrap(), Point::new(650.0, -END_GAP)), "arrives END_GAP above c's top: {:?}", drawn(&s, k));
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(connector(&s, k).to.node(), Some(b), "one undo step");
|
||||
// Off into the canvas: the end stays there, free. The selection is the default connector.
|
||||
@@ -107,9 +107,9 @@ fn curved_connectors_leave_and_arrive_along_the_sides() {
|
||||
let NodeKind::Vector { network } = &n.kind else { panic!("a vector") };
|
||||
assert_eq!((network.vertices.len(), network.segments.len()), (2, 1));
|
||||
assert!(!network.segments[0].is_straight(), "one curve");
|
||||
assert_eq!(drawn(&s, k), vec![Point::new(100.0, 50.0), Point::new(300.0, 50.0)]);
|
||||
assert_eq!(drawn(&s, k), vec![Point::new(104.0, 50.0), Point::new(296.0, 50.0)]);
|
||||
// Side to side in a line: the curve's control points lie on that line, so its box is flat.
|
||||
assert!(n.height.abs() < 1e-9 && (n.width - 200.0).abs() < 1e-9, "{} × {}", n.width, n.height);
|
||||
assert!(n.height.abs() < 1e-9 && (n.width - 192.0).abs() < 1e-9, "{} × {}", n.width, n.height);
|
||||
// From a's bottom to c's top: out downwards and in from above; the box holds the whole curve.
|
||||
let k2 = id(
|
||||
&s.execute("board.connector", &json!({"from": a, "to": c, "fromSide": "bottom", "toSide": "top", "style": "curved", "label": "x"})).unwrap(),
|
||||
@@ -146,18 +146,18 @@ fn elbows_go_around_the_objects_they_join() {
|
||||
let pts = drawn(&s, k2);
|
||||
let (ra, rb) = (Rect::new(0.0, 0.0, 100.0, 100.0), Rect::new(300.0, 0.0, 400.0, 100.0));
|
||||
assert!(!through(&pts, ra) && !through(&pts, rb), "{pts:?}");
|
||||
assert_eq!(pts.first().copied(), Some(Point::new(0.0, 50.0)));
|
||||
assert_eq!(pts.last().copied(), Some(Point::new(400.0, 50.0)));
|
||||
assert_eq!(pts.first().copied(), Some(Point::new(-END_GAP, 50.0)));
|
||||
assert_eq!(pts.last().copied(), Some(Point::new(400.0 + END_GAP, 50.0)));
|
||||
assert_eq!(pts.len(), 6, "out, over, down and in: {pts:?}");
|
||||
assert!(pts.iter().all(|p| p.y <= 50.0), "over the top, the nearer way round: {pts:?}");
|
||||
assert!(pts.windows(2).all(|w| w[0].x == w[1].x || w[0].y == w[1].y), "right angles only");
|
||||
// Facing each other, the short way stays.
|
||||
assert_eq!(drawn(&s, k), vec![Point::new(100.0, 50.0), Point::new(300.0, 50.0)]);
|
||||
assert_eq!(drawn(&s, k), vec![Point::new(104.0, 50.0), Point::new(296.0, 50.0)]);
|
||||
// An end at the bottom of b leaving towards a above it: around b's side and up.
|
||||
s.execute("board.reattach", &json!({"id": k2, "end": "to", "target": b, "side": "bottom"})).unwrap();
|
||||
let pts = drawn(&s, k2);
|
||||
assert!(!through(&pts, ra) && !through(&pts, rb), "{pts:?}");
|
||||
assert_eq!(pts.last().copied(), Some(Point::new(350.0, 100.0)));
|
||||
assert_eq!(pts.last().copied(), Some(Point::new(350.0, 100.0 + END_GAP)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -202,8 +202,12 @@ fn the_connector_being_drawn_is_the_one_made() {
|
||||
// Over b: its four attach points, the one the end is at lit, and b outlined.
|
||||
assert_eq!(ov.drop_target, Some(b));
|
||||
let lit: Vec<[f64; 2]> = ov.attach.iter().filter(|a| a.1).map(|a| a.0).collect();
|
||||
// The lit attach point is the side's middle, on the border; the route's end sits
|
||||
// END_GAP outside it along the side's normal (#1916).
|
||||
let end = *drawn(&s, drawing.id).last().unwrap();
|
||||
assert_eq!((ov.attach.len(), lit), (4, vec![[end.x, end.y]]), "{style}");
|
||||
assert_eq!(ov.attach.len(), 4, "{style}");
|
||||
let [lit] = lit.as_slice() else { panic!("{style}: one lit attach point, got {lit:?}") };
|
||||
assert!(((lit[0] - end.x).abs() + (lit[1] - end.y).abs() - END_GAP).abs() < 1e-9, "{style}: {lit:?} is END_GAP inside {end:?}");
|
||||
} else {
|
||||
assert!(ov.attach.is_empty() && ov.drop_target.is_none(), "a free end");
|
||||
}
|
||||
@@ -259,7 +263,7 @@ fn an_end_dropped_along_a_side_attaches_to_that_side() {
|
||||
s.pointer(&ev(PointerKind::Up, drop)).unwrap();
|
||||
assert_eq!(connector(&s, k).from, End::Node { node: a, side, at: None }, "{drop:?}");
|
||||
if side != Side::Auto {
|
||||
assert_eq!(drawn(&s, k)[0], side.point(bounds), "the route leaves from that side");
|
||||
assert_eq!(drawn(&s, k)[0], side.point(bounds) + side.normal() * END_GAP, "the route leaves from that side, END_GAP out");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,5 +311,5 @@ fn a_connector_end_snaps_to_the_attach_point_under_the_pointer() {
|
||||
(connector(&s, k).from, connector(&s, k).to),
|
||||
(End::Node { node: a, side: Side::Right, at: None }, End::Node { node: b, side: Side::Top, at: None })
|
||||
);
|
||||
assert_eq!(drawn(&s, k), vec![Point::new(100.0, 50.0), Point::new(350.0, 200.0)]);
|
||||
assert_eq!(drawn(&s, k), vec![Point::new(104.0, 50.0), Point::new(350.0, 196.0)]);
|
||||
}
|
||||
@@ -11,8 +11,8 @@ use crate::board::{route, selected_connector_handles};
|
||||
use crate::board_tests::{board, connector, drawn, id, near};
|
||||
use crate::tools::{PointerEvent, PointerKind};
|
||||
|
||||
const A: Point = Point::new(100.0, 50.0);
|
||||
const B: Point = Point::new(300.0, 250.0);
|
||||
const A: Point = Point::new(104.0, 50.0);
|
||||
const B: Point = Point::new(296.0, 250.0);
|
||||
|
||||
/// Shapes a (at the origin) and b (down and to the right), 100 × 100, joined side to side.
|
||||
fn joined(style: &str) -> (Session, NodeId, NodeId, NodeId) {
|
||||
@@ -80,11 +80,11 @@ fn a_reshaped_connector_keeps_the_sides_it_left_by() {
|
||||
fn the_first_and_last_runs_gain_a_run_and_stay_on_their_ends() {
|
||||
let (mut s, _, _, k) = joined("elbow");
|
||||
reshape(&mut s, k, 0.0, 150.0, 120.0).unwrap();
|
||||
assert_eq!(drawn(&s, k), [A, pt(124.0, 50.0), pt(124.0, 120.0), pt(200.0, 120.0), pt(200.0, 250.0), B]);
|
||||
assert_eq!(drawn(&s, k), [A, pt(128.0, 50.0), pt(128.0, 120.0), pt(200.0, 120.0), pt(200.0, 250.0), B]);
|
||||
let last = drawn(&s, k).len() - 2;
|
||||
reshape(&mut s, k, last as f64, 250.0, 200.0).unwrap();
|
||||
let pts = drawn(&s, k);
|
||||
assert_eq!(pts, [A, pt(124.0, 50.0), pt(124.0, 120.0), pt(200.0, 120.0), pt(200.0, 200.0), pt(276.0, 200.0), pt(276.0, 250.0), B]);
|
||||
assert_eq!(pts, [A, pt(128.0, 50.0), pt(128.0, 120.0), pt(200.0, 120.0), pt(200.0, 200.0), pt(272.0, 200.0), pt(272.0, 250.0), B]);
|
||||
assert!(right_angled(&pts));
|
||||
}
|
||||
|
||||
@@ -97,8 +97,8 @@ fn a_lone_run_gains_a_run_at_each_end() {
|
||||
let k = id(&s.execute("board.connector", &json!({"from": a, "to": b, "style": "elbow"})).unwrap(), "id");
|
||||
assert_eq!(drawn(&s, k).len(), 2);
|
||||
reshape(&mut s, k, 0.0, 200.0, -40.0).unwrap();
|
||||
let (l, r) = (pt(100.0, 50.0), pt(300.0, 50.0));
|
||||
assert_eq!(drawn(&s, k), [l, pt(124.0, 50.0), pt(124.0, -40.0), pt(276.0, -40.0), pt(276.0, 50.0), r]);
|
||||
let (l, r) = (pt(104.0, 50.0), pt(296.0, 50.0));
|
||||
assert_eq!(drawn(&s, k), [l, pt(128.0, 50.0), pt(128.0, -40.0), pt(272.0, -40.0), pt(272.0, 50.0), r]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -168,7 +168,7 @@ fn connectors_follow_what_they_join_and_undo_with_it() {
|
||||
let b = id(&s.execute("board.sticky", &json!({"x": 400, "y": 0})).unwrap(), "id");
|
||||
let c = id(&s.execute("board.connector", &json!({"from": a, "to": b, "style": "straight", "label": "then"})).unwrap(), "id");
|
||||
let pts = drawn(&s, c);
|
||||
assert!(near(pts[0], Point::new(200.0, 100.0)) && near(pts[1], Point::new(400.0, 100.0)), "{pts:?}");
|
||||
assert!(near(pts[0], Point::new(204.0, 100.0)) && near(pts[1], Point::new(396.0, 100.0)), "{pts:?}");
|
||||
assert_eq!(node(&s, c).stroke.cap_end, StrokeCap::TriangleArrow);
|
||||
// Its label sits at the middle.
|
||||
let label = crate::board::label_of(s.doc().unwrap(), c).unwrap();
|
||||
@@ -178,19 +178,19 @@ fn connectors_follow_what_they_join_and_undo_with_it() {
|
||||
s.execute("board.setStyle", &json!({"ids": [c], "style": "elbow"})).unwrap();
|
||||
s.execute("transform.move", &json!({"ids": [b], "dx": 0, "dy": 300})).unwrap();
|
||||
let pts = drawn(&s, c);
|
||||
assert!(near(pts[0], Point::new(200.0, 100.0)) && near(*pts.last().unwrap(), Point::new(400.0, 400.0)), "{pts:?}");
|
||||
assert!(near(pts[0], Point::new(204.0, 100.0)) && near(*pts.last().unwrap(), Point::new(396.0, 400.0)), "{pts:?}");
|
||||
assert!(pts.windows(2).all(|w| w[0].x == w[1].x || w[0].y == w[1].y), "right angles: {pts:?}");
|
||||
// Undo takes the move back and the route with it.
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert!(near(*drawn(&s, c).last().unwrap(), Point::new(400.0, 100.0)));
|
||||
assert!(near(*drawn(&s, c).last().unwrap(), Point::new(396.0, 100.0)));
|
||||
// A deleted end stays where it was, as a point.
|
||||
s.execute("edit.delete", &json!({"ids": [b]})).unwrap();
|
||||
assert!(matches!(connector(&s, c).to, End::Point { .. }));
|
||||
assert!(near(*drawn(&s, c).last().unwrap(), Point::new(400.0, 100.0)));
|
||||
assert!(near(*drawn(&s, c).last().unwrap(), Point::new(396.0, 100.0)));
|
||||
// A connector moved on its own takes its free end along; the attached one stays.
|
||||
s.execute("transform.move", &json!({"ids": [c], "dx": 0, "dy": 50})).unwrap();
|
||||
assert!(matches!(connector(&s, c).to, End::Point { x, y } if x == 400.0 && y == 150.0), "{:?}", connector(&s, c));
|
||||
assert!(near(drawn(&s, c)[0], Point::new(200.0, 100.0)));
|
||||
assert!(matches!(connector(&s, c).to, End::Point { x, y } if x == 396.0 && y == 150.0), "{:?}", connector(&s, c));
|
||||
assert!(near(drawn(&s, c)[0], Point::new(204.0, 100.0)));
|
||||
// Deleting the connector takes its label.
|
||||
s.execute("edit.delete", &json!({"ids": [c]})).unwrap();
|
||||
assert!(!s.doc().unwrap().contains(label));
|
||||
@@ -210,8 +210,8 @@ fn routes_are_the_same_for_the_same_document() {
|
||||
let doc = s.doc().unwrap();
|
||||
let r = route(doc, &c).unwrap();
|
||||
assert_eq!(r, route(&doc.clone(), &c).unwrap());
|
||||
// Down out of the bottom, across halfway, down into the point.
|
||||
assert_eq!(r, vec![Point::new(80.0, 160.0), Point::new(80.0, 330.0), Point::new(300.0, 330.0), Point::new(300.0, 500.0)]);
|
||||
// Down out of the bottom (END_GAP below it), across halfway, down into the point.
|
||||
assert_eq!(r, vec![Point::new(80.0, 164.0), Point::new(80.0, 332.0), Point::new(300.0, 332.0), Point::new(300.0, 500.0)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -338,7 +338,7 @@ fn boards_sync_in_live_sessions() {
|
||||
let doc = room.server.document();
|
||||
let NodeKind::Vector { network } = &doc.node(c).unwrap().kind else { panic!("not a vector") };
|
||||
let end = doc.world_transform(c) * network.vertices.last().unwrap().point();
|
||||
assert_eq!(end, Point::new(400.0, 350.0), "to the moved sticky's facing side");
|
||||
assert_eq!(end, Point::new(396.0, 350.0), "to END_GAP before the moved sticky's facing side");
|
||||
assert_eq!(doc.node(a).unwrap().fills[0].solid_color(), board_color("violet"));
|
||||
}
|
||||
|
||||
@@ -410,7 +410,9 @@ fn a_press_on_a_stickys_background_picks_and_drags_it() {
|
||||
s.pointer(&PointerEvent::at(PointerKind::Up, 100.0, 450.0, 1.0)).unwrap();
|
||||
assert_eq!(s.selection(), vec![a]);
|
||||
assert_eq!(node(&s, a).transform.translation().y, 300.0);
|
||||
assert!(near(drawn(&s, c)[0], Point::new(200.0, 400.0)), "the connector followed the drag: {:?}", drawn(&s, c));
|
||||
// Its own connector's start, END_GAP outside its edge, is no snap target for it (#1916).
|
||||
assert_eq!(node(&s, a).transform.translation().x, 0.0, "not nudged sideways by its own connector");
|
||||
assert!(near(drawn(&s, c)[0], Point::new(204.0, 400.0)), "the connector followed the drag: {:?}", drawn(&s, c));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -100,18 +100,18 @@ fn routes_take_named_sides_and_free_points() {
|
||||
let side = |n, side| End::Node { node: n, side, at: None };
|
||||
let p = |x, y| End::Point { x, y };
|
||||
let pt = Point::new;
|
||||
// Side to side: out, across halfway, in.
|
||||
assert_eq!(elbow(side(a, Side::Right), side(b, Side::Left)), vec![pt(100.0, 50.0), pt(200.0, 50.0), pt(200.0, 350.0), pt(300.0, 350.0)]);
|
||||
// Side to side: out (from END_GAP outside each side), across halfway, in.
|
||||
assert_eq!(elbow(side(a, Side::Right), side(b, Side::Left)), vec![pt(104.0, 50.0), pt(200.0, 50.0), pt(200.0, 350.0), pt(296.0, 350.0)]);
|
||||
// A side and a top or bottom: one turn.
|
||||
assert_eq!(elbow(side(a, Side::Right), side(b, Side::Top)), vec![pt(100.0, 50.0), pt(350.0, 50.0), pt(350.0, 300.0)]);
|
||||
assert_eq!(elbow(side(a, Side::Bottom), side(b, Side::Left)), vec![pt(50.0, 100.0), pt(50.0, 350.0), pt(300.0, 350.0)]);
|
||||
assert_eq!(elbow(side(a, Side::Right), side(b, Side::Top)), vec![pt(104.0, 50.0), pt(350.0, 50.0), pt(350.0, 296.0)]);
|
||||
assert_eq!(elbow(side(a, Side::Bottom), side(b, Side::Left)), vec![pt(50.0, 104.0), pt(50.0, 350.0), pt(296.0, 350.0)]);
|
||||
// Two free points: the longer way first; in a line, no turns.
|
||||
assert_eq!(elbow(p(0.0, 0.0), p(100.0, 40.0)), vec![pt(0.0, 0.0), pt(50.0, 0.0), pt(50.0, 40.0), pt(100.0, 40.0)]);
|
||||
assert_eq!(elbow(p(0.0, 0.0), p(100.0, 0.0)), vec![pt(0.0, 0.0), pt(100.0, 0.0)]);
|
||||
// An automatic side faces the other end.
|
||||
let straight =
|
||||
route(doc, &figcraft_doc::Connector { from: side(a, Side::Auto), to: p(50.0, 500.0), routing: figcraft_doc::Routing::Straight, path: None });
|
||||
assert_eq!(straight.unwrap(), vec![pt(50.0, 100.0), pt(50.0, 500.0)]);
|
||||
assert_eq!(straight.unwrap(), vec![pt(50.0, 104.0), pt(50.0, 500.0)]);
|
||||
assert_eq!(Side::from_id("TOP"), Some(Side::Top));
|
||||
assert_eq!(Side::from_id("inside"), None);
|
||||
// The same place twice is refused, as are a side that doesn't exist and a point without numbers.
|
||||
@@ -128,10 +128,10 @@ fn a_connector_follows_a_layer_whose_section_moves_and_ignores_unrelated_edits()
|
||||
let a = id(&s.execute("board.sticky", &json!({"x": 50, "y": 50, "parent": sec})).unwrap(), "id");
|
||||
let b = id(&s.execute("board.stamp", &json!({"x": 800, "y": 150})).unwrap(), "id");
|
||||
let c = id(&s.execute("board.connector", &json!({"from": a, "to": b, "style": "straight"})).unwrap(), "id");
|
||||
assert!(near(drawn(&s, c)[0], Point::new(250.0, 150.0)));
|
||||
assert!(near(drawn(&s, c)[0], Point::new(254.0, 150.0)));
|
||||
// Only the section is edited; the sticky inside it moves with it, and so does the connector.
|
||||
s.execute("transform.move", &json!({"ids": [sec], "dx": 0, "dy": 100})).unwrap();
|
||||
assert!(near(drawn(&s, c)[0], Point::new(250.0, 250.0)), "{:?}", drawn(&s, c));
|
||||
assert!(near(drawn(&s, c)[0], Point::new(254.0, 250.0)), "{:?}", drawn(&s, c));
|
||||
// An edit that touches neither end leaves the connector as it was.
|
||||
let before = s.doc().unwrap().node_arc(c).cloned().unwrap();
|
||||
s.execute("board.sticky", &json!({"x": 0, "y": 900})).unwrap();
|
||||
|
||||
@@ -133,13 +133,19 @@ impl Session {
|
||||
(to.node().is_none() || to.node() != from.node()).then_some(to)
|
||||
}
|
||||
|
||||
/// The object `end` is on (if any) outlined, with its attach points; the one the end is at
|
||||
/// (`at`, else its named side's) lit.
|
||||
/// The object `end` is on (if any) outlined, with its attach points (the side middles, on the
|
||||
/// border); the one the end is at lit: the nearest to `at` (a route's end sits `END_GAP` outside
|
||||
/// its side's middle, #1916), else its named side's.
|
||||
fn show_attach(&mut self, end: Option<End>, at: Option<Point>) {
|
||||
let attach = match end {
|
||||
Some(End::Node { node, side, .. }) => self.doc().ok().and_then(|d| d.world_bounds(node)).map(|b| {
|
||||
let on = at.or_else(|| (side != Side::Auto).then(|| side.point(b)));
|
||||
SIDES.map(|s| s.point(b)).map(|p| ([p.x, p.y], on.is_some_and(|o| (o - p).hypot() < 1e-6))).to_vec()
|
||||
let pts = SIDES.map(|s| s.point(b));
|
||||
let lit = on.and_then(|o| {
|
||||
let near = pts.iter().enumerate().map(|(i, p)| (i, (o - *p).hypot())).filter(|(_, d)| *d <= figcraft_doc::END_GAP + 1e-6);
|
||||
near.min_by(|x, y| x.1.total_cmp(&y.1)).map(|(i, _)| i)
|
||||
});
|
||||
pts.iter().enumerate().map(|(i, p)| ([p.x, p.y], lit == Some(i))).collect()
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! guide lines (with spacing labels) for the canvas to draw in red. Vector points also snap to
|
||||
//! other points (see `vectoredit::snap`); landing on one shows a marker (a zero-length guide).
|
||||
|
||||
use figcraft_doc::{Document, NodeId};
|
||||
use figcraft_doc::{BoardItem, Document, NodeId};
|
||||
use figcraft_geom::{Affine, Point, Rect, Vec2, transform_rect_bbox};
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -94,10 +94,22 @@ impl Targets {
|
||||
t.ys.push((y, b.x0, b.x1));
|
||||
}
|
||||
}
|
||||
// Siblings and, at the top level, all top-level layers.
|
||||
// Siblings and, at the top level, all top-level layers. A board connector joined to a
|
||||
// moving layer re-routes with it, as does its label: no snapping to where its own route
|
||||
// starts (#1916).
|
||||
let follows = |id: NodeId| match doc.node(id).and_then(|n| n.board.as_ref()) {
|
||||
Some(BoardItem::Connector(k)) => [k.from.node(), k.to.node()].into_iter().flatten().any(|e| moving.contains(&e)),
|
||||
_ => false,
|
||||
};
|
||||
if let Some(p) = doc.node(parent) {
|
||||
for c in &p.children {
|
||||
if moving.contains(c) || !doc.node(*c).is_some_and(|n| n.visible) {
|
||||
let Some(n) = doc.node(*c).filter(|n| n.visible) else { continue };
|
||||
let joined = match n.board.as_ref() {
|
||||
Some(BoardItem::Connector(_)) => follows(*c),
|
||||
Some(BoardItem::Label { connector }) => follows(*connector),
|
||||
_ => false,
|
||||
};
|
||||
if moving.contains(c) || joined {
|
||||
continue;
|
||||
}
|
||||
if let Some(b) = bbox(*c) {
|
||||
|
||||
@@ -46,7 +46,7 @@ fn without_cmd_an_end_keeps_snapping_to_a_side() {
|
||||
let end = *drawn(&s, k).last().unwrap();
|
||||
drag_end(&mut s, end, Point::new(650.0, 25.0), false);
|
||||
assert_eq!(connector(&s, k).to, End::Node { node: b, side: Side::Right, at: None });
|
||||
assert!(near(*drawn(&s, k).last().unwrap(), Point::new(700.0, 50.0)));
|
||||
assert!(near(*drawn(&s, k).last().unwrap(), Point::new(700.0 + figcraft_doc::END_GAP, 50.0)), "END_GAP outside the side");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user