PR5 of ck-diagram: multi-select, group, z-order, snap-to-grid #55

Manually merged
mdoorn merged 5 commits from feat/055-pr5-of-ck-diagram-multi-select-group-z-order-s into master 2026-07-08 12:53:11 +02:00
Owner

PR5 of ck-diagram: multi-select, group, z-order, snap-to-grid

Along the way:

  • PR6 of ck-diagram: edges and connectors
  • PR7 of ck-diagram: in-place text editing
  • PR8 of ck-diagram: floating style toolbar
  • PR9 of ck-diagram: bottom-right minimap overlay

Touches 9 files: ck-diagram/README.md, ck-diagram/src/canvas.rs, ck-diagram/src/history.rs, ck-diagram/src/lib.rs, ck-diagram/src/minimap.rs, ck-diagram/tests/canvas.rs and 3 more.

PR5 of ck-diagram: multi-select, group, z-order, snap-to-grid Along the way: - PR6 of ck-diagram: edges and connectors - PR7 of ck-diagram: in-place text editing - PR8 of ck-diagram: floating style toolbar - PR9 of ck-diagram: bottom-right minimap overlay Touches 9 files: `ck-diagram/README.md`, `ck-diagram/src/canvas.rs`, `ck-diagram/src/history.rs`, `ck-diagram/src/lib.rs`, `ck-diagram/src/minimap.rs`, `ck-diagram/tests/canvas.rs` and 3 more.
praghunathan requested changes 2026-07-07 14:12:37 +02:00
Dismissed
praghunathan left a comment

I do not want to merge while the error path in ck-diagram/tests/minimap.rs is silent. The rest is fine.

I do not want to merge while the error path in `ck-diagram/tests/minimap.rs` is silent. The rest is fine.
@ -0,0 +80,4 @@
vec![fresh_input(), down, up]
}
fn drag(start: egui::Pos2, end: egui::Pos2) -> Vec<egui::RawInput> {
Owner

drag reads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?

`drag` reads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?
Author
Owner

Split. The decision in drag is a pure function now, with its own tests in ck-diagram/tests/minimap.rs.

Split. The decision in `drag` is a pure function now, with its own tests in `ck-diagram/tests/minimap.rs`.
Author
Owner

Done, and I added the test for ck-diagram/tests/minimap.rs that would have caught it.

Done, and I added the test for `ck-diagram/tests/minimap.rs` that would have caught it.
praghunathan left a comment

Good. The second pass on ck-diagram/tests/minimap.rs is what I hoped for.

Good. The second pass on `ck-diagram/tests/minimap.rs` is what I hoped for.
tvidal approved these changes 2026-07-08 09:30:00 +02:00
tvidal left a comment

Read it too. No objection to ck-diagram/tests/minimap.rs.

Read it too. No objection to `ck-diagram/tests/minimap.rs`.
Turns the canvas multi-shape. Selection is `&[ShapeId]`: shift-click
adds, ctrl-click toggles, left-drag from empty canvas opens a
rubber-band rectangle that selects every shape it intersects. Drag
any selected shape to move the whole selection, quantized to the 20-
unit grid unless Alt is held. `]` / `[` / `}` / `{` and the canvas
right-click menu reorder; `Ctrl+G` / `Ctrl+Shift+G` group / ungroup;
clicking any group member expands the selection to the whole group.
`Ctrl+A` selects everything; `Delete` records a single DeleteMany
entry across the selection. History gains MoveMany, DeleteMany,
ZOrder, CreateGroup, and UngroupSelection variants; empty multi-shape
entries are dropped at record time. Coverage stays at 100% lines /
regions / functions across the crate.
Hovering a shape reveals its cardinal ports; drag-from-port commits
an Edge (orthogonal default, straight, curved) with optional
arrowheads at endpoints and an optional midpoint label. Deleting a
shape cascades to every incident edge in a single HistoryEntry so
Ctrl+Z restores the whole subgraph.
Double-click any shape — or press F2 against a single selection — to
open an egui TextEdit overlay anchored to the shape's screen rectangle.
The widget claims focus, mutates the live NodeText every frame, and
commits when focus moves away or Escape is pressed. A no-net-change
exit is dropped at record time; a real change records one
HistoryEntry::EditText.

A thin top toolbar floats over the canvas (3px margin, no panel row
reserved) with a Plain ↔ Title+Body mode toggle for the selected shape,
preserving the existing text on the round-trip.
Grow the PR-7 single-button row into the full §6.3 strip: stroke /
fill / text swatches, stroke-width and font-size steppers, Plain /
Title+Body toggle, horizontal and vertical text alignment, z-order
operations, group / ungroup, multi-shape align (L/HC/R/T/VM/B), and
distribute-horizontal / distribute-vertical. Every cell is keyed
by a public ToolbarButton enum and exposed via toolbar_button_id so
tests can drive any cell without predicting layout pixels. Style
edits batch across the selection into one HistoryEntry::EditStyle,
align edits into one HistoryEntry::EditAlign, and multi-shape
align / distribute collapse into one HistoryEntry::MoveMany.
Per-shape no-ops drop at record time so phantom presses never land
on the undo stack.
160x100 px overlay rendering every shape at minimap scale plus a
viewport indicator. Click recenters Viewport::center on the clicked
world point; drag pans with the world-space grab offset captured at
drag-start against a frozen MinimapTransform snapshot so the cursor
stays anchored to the same point inside the indicator as the
viewport moves. press_origin (not interact_pointer_pos) anchors the
grab so the post-nudge bias does not amplify by 1/scale.

New module ck-diagram/src/minimap.rs holds the pure geometry:
content_bounds, viewport_world_rect, world_coverage, MinimapTransform
with aspect-ratio-preserving world<->minimap conversion, plus the
build / interact / render helpers. DiagramView gains minimap_drag
state and last_canvas_rect accessor (so tests can resolve world<->screen
without re-deriving egui layout). Suppressed when the diagram is
empty, the in-place editor is open, or the right-click context menu
is open.

25 new tests in tests/minimap.rs. ck-diagram stays at 100% line and
function coverage. Mutation gate stays OFF per the iteration-mode
contract.
mdoorn manually merged commit 6cb13ed1e1 into master 2026-07-08 12:53:11 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
northgale/ckeditor!55
No description provided.