mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-08 19:42:05 +00:00
M12: a connector joined to a layer inside a moving section is no snap target; the docs say which connectors share a run (#1916)
Review: a section moving carries its stickies and their connectors, so those connectors and their labels are no snap targets for the section either (an end counts as carried when its node or any ancestor is moving). The parity rows, the END_GAP doc and the test's doc no longer say connectors from one side run together: elbows share their first run along the normal, curves leave tangent to it, straight ones fan out from the same point. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
2dc7bed2a0
commit
7be7c054eb
5 files changed
+38
-11
No files matched your search
@@ -29,8 +29,8 @@ impl RouteEnd {
|
||||
const ROOM: f64 = 24.0;
|
||||
|
||||
/// How far outside the border a connector attached to a side starts and ends (#1916): the stroke's
|
||||
/// round cap never lies on the fill, and several connectors leaving one side run together along the
|
||||
/// side's normal for this much before they part, as FigJam's do.
|
||||
/// round cap never lies on the fill, and elbows leaving one side share their first run along the
|
||||
/// side's normal (curves leave tangent to it), as FigJam's connectors do.
|
||||
pub const END_GAP: f64 = 4.0;
|
||||
|
||||
/// The points a connector runs through from `from` to `to`: from [`END_GAP`] outside the middle of
|
||||
|
||||
@@ -24,8 +24,8 @@ fn an_attached_end_starts_outside_the_border() {
|
||||
assert_eq!(pts.first().copied(), Some(Point::new(50.0, 60.0 + END_GAP)));
|
||||
}
|
||||
|
||||
/// Several connectors leaving one side run together along its normal before they part: an elbow's
|
||||
/// and a curve's first run leave straight out of the side.
|
||||
/// Elbows leaving one side share their first run along its normal, and curves leave tangent to it:
|
||||
/// the second point of each lies straight out of the side.
|
||||
#[test]
|
||||
fn connectors_from_one_side_leave_together() {
|
||||
let a = shape(0.0, 0.0);
|
||||
|
||||
@@ -276,3 +276,29 @@ fn a_board_tool_left_on_presses_as_move_in_a_design_file() {
|
||||
assert_eq!(s.tool.tool, ToolId::Move);
|
||||
assert_eq!(s.selection(), vec![r], "the press selected as Move does");
|
||||
}
|
||||
|
||||
/// #1916: a connector's start, END_GAP outside the layer it leaves, is a snap line for other layers
|
||||
/// but not for that layer, nor for a section holding it (both carry the connector along), and nor
|
||||
/// is the connector's label.
|
||||
#[test]
|
||||
fn a_connector_joined_to_a_moving_layer_or_its_section_is_no_snap_target() {
|
||||
let mut s = board();
|
||||
let sec = id(&s.execute("node.create", &json!({"type": "section", "x": 0, "y": 0, "width": 300, "height": 300})).unwrap(), "id");
|
||||
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", "label": "to"})).unwrap(), "id");
|
||||
// A layer the connector has nothing to do with.
|
||||
let d = id(&s.execute("board.sticky", &json!({"x": 0, "y": 900})).unwrap(), "id");
|
||||
let page = s.page().unwrap();
|
||||
let start = drawn(&s, c)[0].x;
|
||||
let label = crate::board::label_of(s.doc().unwrap(), c).unwrap();
|
||||
let lx = s.doc().unwrap().world_bounds(label).unwrap().x0;
|
||||
// A box whose left edge is 1 past a line snaps back by 1 when the line is a target.
|
||||
let pull = |s: &Session, moving: &[NodeId], x: f64| {
|
||||
let t = crate::snap::Targets::gather(s.doc().unwrap(), page, moving);
|
||||
t.snap_rect(figcraft_geom::Rect::new(x + 1.0, 600.0, x + 9.0, 610.0), 4.0).0
|
||||
};
|
||||
assert_eq!((pull(&s, &[d], start), pull(&s, &[d], lx)), (-1.0, -1.0), "for an unrelated layer, the connector and its label snap");
|
||||
assert_eq!(pull(&s, &[b], start), 0.0, "not for the layer it arrives at");
|
||||
assert_eq!((pull(&s, &[sec], start), pull(&s, &[sec], lx)), (0.0, 0.0), "not for the section the connector leaves from");
|
||||
}
|
||||
@@ -95,10 +95,11 @@ impl Targets {
|
||||
}
|
||||
}
|
||||
// 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).
|
||||
// moving layer, or to one inside it (a section carries its stickies), re-routes with it, as
|
||||
// does its label: no snapping to where its own route starts (#1916).
|
||||
let carried = |e: NodeId| moving.contains(&e) || doc.ancestors(e).iter().any(|a| moving.contains(a));
|
||||
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)),
|
||||
Some(BoardItem::Connector(k)) => [k.from.node(), k.to.node()].into_iter().flatten().any(carried),
|
||||
_ => false,
|
||||
};
|
||||
if let Some(p) = doc.node(parent) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Boards parity
|
||||
|
||||
> **Last reviewed:** 2026-10-24 · **Last updated:** 2026-11-04 · **Change:** minor (a connector attached to a side starts 4 pt outside the border, so its stroke never lies on the fill and connectors leaving one side run together before they part (#1916); a connector end moved with Cmd/Ctrl held goes on any part of an object, and stays there as the object moves or resizes; a connector end dropped along a side of a shape attaches to that side (#1708); a board connector's path can be reshaped by dragging its path handles (#1710); boards have 16 more shapes (arrows, polygons, star, plus and flowchart symbols), in pickers that lay them out in a grid; a board keeps earlier votes' results, listed newest first in Results and deleted one at a time; a connector's bar sets its start and end points, dashes, weight and any colour (#1596); cells copied from a spreadsheet paste onto a board as a table; Shift+Cmd/Ctrl+Enter on a selected board shape adds the next shape already joined to it, and goes on typing (#1597); "+" handles beside a selected sticky or shape make the next one, joined by a connector (#1597); a FigJam `.jam` file opens as a board (#1342); board grid settings: dots, lines, graph paper or none, spacing, size and contrast; the connector tool shows the connector it will make while dragging, ends snapping to attach points (#1387); templates and tables; facilitation: a shared timer and voting sessions; the dot grid, dragging a connector's end to re-attach it, curved connectors, elbows that go around what they join) · **Target:** the incumbent's whiteboard product (behaviour from its public help centre)
|
||||
> **Last reviewed:** 2026-10-24 · **Last updated:** 2026-11-04 · **Change:** minor (a connector attached to a side starts 4 pt outside the border, so its stroke never lies on the fill (#1916); a connector end moved with Cmd/Ctrl held goes on any part of an object, and stays there as the object moves or resizes; a connector end dropped along a side of a shape attaches to that side (#1708); a board connector's path can be reshaped by dragging its path handles (#1710); boards have 16 more shapes (arrows, polygons, star, plus and flowchart symbols), in pickers that lay them out in a grid; a board keeps earlier votes' results, listed newest first in Results and deleted one at a time; a connector's bar sets its start and end points, dashes, weight and any colour (#1596); cells copied from a spreadsheet paste onto a board as a table; Shift+Cmd/Ctrl+Enter on a selected board shape adds the next shape already joined to it, and goes on typing (#1597); "+" handles beside a selected sticky or shape make the next one, joined by a connector (#1597); a FigJam `.jam` file opens as a board (#1342); board grid settings: dots, lines, graph paper or none, spacing, size and contrast; the connector tool shows the connector it will make while dragging, ends snapping to attach points (#1387); templates and tables; facilitation: a shared timer and voting sessions; the dot grid, dragging a connector's end to re-attach it, curved connectors, elbows that go around what they join) · **Target:** the incumbent's whiteboard product (behaviour from its public help centre)
|
||||
|
||||
**Breadth ~47%, ready ~29% (estimated, erring low).** A board is a FigCraft file of its own kind
|
||||
(`file.newBoard`) with a dot grid on the page, sticky notes, shapes with text, connectors that
|
||||
@@ -34,10 +34,10 @@ all end with the same geometry.
|
||||
| Sticky options: hide the author, change size presets, sticky stacks | partial | `board.sticky {author: false}`; no size presets or stacks |
|
||||
| Shapes with text | done | rectangle, rounded rectangle, ellipse, diamond, triangle, speech bubble, chevron; text centred and wrapping inside the outline; shape type and text size from the contextual bar |
|
||||
| Shape auto-fit to its text, more shapes (cylinder, cross, star…), shape libraries | missing | |
|
||||
| Connectors | done | from a layer's side (the facing one, or one named) to another layer or a free point, straight, elbow or curved, arrowheads (the stroke's caps), a label in the middle of the route by length (double-click to add); while one is dragged out it shows as it will be made (route, line and arrowhead), the object under the pointer outlined with its four attach points (side middles), the one the end goes to ringed; an end dropped on the outer half of an object takes the side it is towards (a drop along a whole edge chooses that edge), one in the middle takes the facing side; re-routed when either end moves or resizes; a deleted end stays where it was; an end attached to a side starts 4 pt outside the border (`board_route::END_GAP`), so the stroke never lies on the fill and connectors leaving one side run together along the side's normal before they part (#1916) |
|
||||
| Connectors | done | from a layer's side (the facing one, or one named) to another layer or a free point, straight, elbow or curved, arrowheads (the stroke's caps), a label in the middle of the route by length (double-click to add); while one is dragged out it shows as it will be made (route, line and arrowhead), the object under the pointer outlined with its four attach points (side middles), the one the end goes to ringed; an end dropped on the outer half of an object takes the side it is towards (a drop along a whole edge chooses that edge), one in the middle takes the facing side; re-routed when either end moves or resizes; a deleted end stays where it was; an end attached to a side starts 4 pt outside the border (`board_route::END_GAP`), so the stroke never lies on the fill; elbows leaving one side share their first run along its normal and curves leave tangent to it (#1916) |
|
||||
| Shapes with text | done | 23 outlines: rectangle, rounded rectangle, ellipse, diamond, triangle up and down, parallelogram right and left, trapezoid, pentagon, hexagon, octagon, star, plus, arrow left and right, chevron, speech bubble, and the flowchart symbols database, document, predefined process, manual input and internal storage; text centred and wrapping inside the outline; shape type and text size from the contextual bar |
|
||||
| Shape auto-fit to its text, the advanced shape libraries (engineering and other icon shapes) | missing | |
|
||||
| Connectors | done | from a layer's side (the facing one, or one named) to another layer or a free point, straight, elbow or curved, arrowheads (the stroke's caps), a label in the middle of the route by length (double-click to add); while one is dragged out it shows as it will be made (route, line and arrowhead), the object under the pointer outlined with its four attach points (side middles), the one the end goes to ringed, and within 10 px of one the end keeps that side; re-routed when either end moves or resizes; a deleted end stays where it was; an end attached to a side starts 4 pt outside the border (`board_route::END_GAP`), so the stroke never lies on the fill and connectors leaving one side run together along the side's normal before they part (#1916) |
|
||||
| Connectors | done | from a layer's side (the facing one, or one named) to another layer or a free point, straight, elbow or curved, arrowheads (the stroke's caps), a label in the middle of the route by length (double-click to add); while one is dragged out it shows as it will be made (route, line and arrowhead), the object under the pointer outlined with its four attach points (side middles), the one the end goes to ringed, and within 10 px of one the end keeps that side; re-routed when either end moves or resizes; a deleted end stays where it was; an end attached to a side starts 4 pt outside the border (`board_route::END_GAP`), so the stroke never lies on the fill; elbows leaving one side share their first run along its normal and curves leave tangent to it (#1916) |
|
||||
| Create the next shape already connected | done | a selected sticky or shape shows a "+" beside each side; hovering one shows where the next goes, clicking makes one of the same kind, size and colour there, joined by a connector as the Connector tool makes one, and starts typing in it; one undo step (`board.addConnected {id?, side?, edit?}`, #1597). Shift+Cmd/Ctrl+Enter on a selected (or edited) shape adds the next shape on its right, joined the same way, and goes on typing; on a sticky it adds a sticky beside it without a connector, as before |
|
||||
| Curved connectors, elbows around objects, dragging an end to re-attach | done | `board.setStyle {style: curved}` (and the contextual bar): one curve leaving and arriving along the sides; an elbow whose short way would cross what it joins goes around their boxes, 24 units clear; a selected connector shows its two ends as handles, and dragging one onto an object attaches it there (the object is outlined while dragging), onto empty canvas leaves it free (`board.reattach {id, end, target, side?}`, one undo step) |
|
||||
| A connector end on any part of an object | done | holding Cmd/Ctrl while moving an end places it where the pointer is on the object, as a share of its box, so it follows moves and resizes; the line leaves by the side facing the other end. Without the key an end snaps to a side |
|
||||
@@ -70,7 +70,7 @@ all end with the same geometry.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-04 | minor | M12, #1916: a connector end attached to a side starts 4 pt outside the border (`board_route::END_GAP`), as FigJam's do (the issue's screenshots: the line starts a few pixels outside, with a round cap, and connectors leaving one side run together along the side's normal before they fan out), so the stroke never lies on the fill; the `.jam` importer's routes, re-attached ends, path handles and labels follow the route's points; an end placed by hand with Cmd/Ctrl held keeps its exact point. Tests: `board_route_tests` (three). Numbers unchanged. |
|
||||
| 2026-11-04 | minor | M12, #1916: a connector end attached to a side starts 4 pt outside the border (`board_route::END_GAP`), as FigJam's do (the issue's screenshots: the line starts a few pixels outside, with a round cap), so the stroke never lies on the fill; elbows leaving one side share their first run along its normal, curves leave tangent to it and straight ones fan out from the same point; the `.jam` importer's routes, re-attached ends, path handles and labels follow the route's points; an end placed by hand with Cmd/Ctrl held keeps its exact point. Tests: `board_route_tests` (three). Numbers unchanged. |
|
||||
| 2026-10-31 | minor | M12: as the help describes, holding Cmd/Ctrl while moving a connector's end places it on any part of an object instead of snapping to a side. It keeps that spot as a fraction of the object's box, so it follows the object when it moves or resizes, and without the key an end snaps as before. `board.connector {fromAt, toAt}` and `board.reattach {at}` take it; saved only when set. Numbers unchanged |
|
||||
| 2026-10-31 | patch | M12, #1708: a connector end dropped on the outer half of a shape takes the side it is towards, instead of only within 10 px of a side's middle, so a drop anywhere along an edge chooses that edge (dragging a selected connector's end and with the connector tool, ring included); a drop in the middle of a shape still takes the facing side. |
|
||||
| 2026-10-31 | minor | M12, #1710: a selected elbow or curved connector shows path handles. Dragging a run moves it and a first or last run gains an extra run to stay on its end; a curve's middle goes where it is dragged (`board.reshape`, one undo step). The shape travels with what the connector joins, is saved with the file, and goes back to routing itself when the style changes or an end moves to something else. |
|
||||
|
||||
Reference in new issue
Block a user