Skip to content

Drag a selection to move it all at once - #400

Merged
SunkenInTime merged 3 commits into
mainfrom
t3/canvas-group-move
Oct 28, 2026
Merged

SunkenInTime merged 3 commits into
mainfrom
t3/canvas-group-move

Conversation

@SunkenInTime

@SunkenInTime SunkenInTime commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Dragging a selection moves all of it; one Ctrl+Z puts it back; a lone drawing drags too

Windows release build, real input: a box selects everything, dragging one agent carries all of it (stroke included), one Ctrl+Z puts it all back, then a lone selected stroke is dragged on its own, which wasn't possible before.

This is the second PR in the selection stack. It sits on #399, and #401 sits on it; the three merge together. Now that items can be selected, the obvious next move is to drag them as a group.

What changes

  • Pressing any selected item drags the whole selection. Everything, drawings and view-cone coverage included, is drawn where the drag carries it. On release everything moves for real as one undo step.
  • The drag stops before the middle of anything would leave the map. An agent or ability dropped out of bounds deletes itself, so the group never gets there.
  • Escape drops the drag.
  • Lineups stay where they are. A lineup marks the in-game spot its screenshots were taken from, so moving it, even as a whole, makes it wrong. This matches the lock from Lock placed lineups and move them in an Edit placement mode #265; Edit placement is still how to move one.
  • A lone selected drawing can now be dragged. Drawings had no drag of their own.

How

  • Drawings: DrawingProvider.translateDrawings (separate commit) moves every point. It records a real edit history entry per drawing, not delete plus add, which could bring back a drawing a teammate deleted. ActionObjectState gives drawings a JSON form, so undo and redo replay through the same field-by-field path as other items: a teammate's delete is skipped, and a teammate's colour change survives.
  • The preview is visual only:
    • canvasGroupDragProvider holds {ids, by}.
    • Each EditorEntityLayer translates its item if the drag carries it. The Transform is always present, so an item never remounts mid-drag.
    • DrawingPainter shifts the carried strokes.
    • ConeCoverage.moved() repaints the cones. They only repaint on layout, and a translate isn't a layout change.
  • Positions are saved once, on release. Moving them every frame would send a cloud op per frame.
  • The drag of the selection competes like any drag. While an item is one of several selected, its own Draggable stands aside (maxSimultaneousDrags: itemDragLimit(context)). Handles and taps keep winning: resize and rotate handles, and a click on text to edit it.
  • Collaboration and devices:
    • The drag carries the items it began with, fixed at the press.
    • It holds them so a teammate's changes wait for the drop.
    • It keeps its recognizer until it ends, even if Space is pressed mid-way.
    • It takes mouse, touch and pen, because the items' own drags step aside for all three.

Review

Astra's first round found four bugs in the earlier, eager version of this drag, and all four are fixed in the second commit:

  • Ctrl+A mid-drag moved unclamped items off the map.
  • A click could select the wrong member.
  • Handles and text editing were stolen by the drag.
  • Space mid-drag left items drawn where they were never saved.

Each has a regression test. A second round found nothing further here; its one note, that touch and pen drags were blocked, is also fixed.

Ground truth

  • Windows release build, real input:
    • A group drag moved five items with one undo step.
    • Defense side: dragging the group down and right saved it up and left in stored coordinates, confirmed by switching back to attack.
    • A lone drawing dragged.
    • A click on one member of three selected items selected that member.
    • Dragging a text item's resize handle while multi-selected resized only the text.
    • Space mid-drag still committed the move.
    • A click on selected text opened it for editing.
  • Tests: unit and widget tests cover a mixed move undoing in one step, the clamp, Escape, the frozen membership, the handle, the click target and Space mid-drag.

Resize handle on a multi-selected text item resizes only the text

Not covered

  • Ctrl+drag of a group moves it rather than duplicating it, unlike Ctrl+drag of a single item. That can follow on top of the paste code in #401.
  • No touch-screen hardware was tried.
  • The full live-sync path for a moved drawing wasn't driven end to end in a test. The diff that produces the cloud patch was checked against the real merge code (field_merge).

ci: analyze test web

@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Caution

Review failed

The pull request is closed.

Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

A group move on the canvas needs to shift drawings, which could only be
added and removed. translateDrawings moves every point and bounding box
of the named drawings in place in the stack and records one edit per
drawing, so a move inside performTransaction undoes as one step with the
widgets moved beside it.

Drawing edits replay through the same field-by-field path as other kinds:
a drawing now has history JSON (the same fields saves and live sync
write), so undo skips a drawing a teammate deleted, keeps a colour they
changed, and counts a move they already put back as no change.
Pressing a selected item drags the whole selection: everything is drawn
where the drag carries it (drawings and view cones too) and moves for real,
as one undo step, on release. The drag stops before anything would leave the
map, Escape drops it, and lineups stay where they are. A lone selected
drawing can now be dragged too.
Review found the selection's drag took every press on a selected item, so
resize and rotate handles and a text item's click stopped working, a click
could land on the wrong item, a selection grown mid-drag moved unclamped
items off the map (agents dropped there are deleted), and Space mid-drag
left items drawn where they were never moved.

The selection's drag now competes like any drag: an item's own drag stands
aside while it is one of several selected (itemDragLimit), handles and taps
keep theirs. A drag carries the items it began with, a click goes to what
the press landed on, and a drag under way keeps its recognizer until it
ends.
@SunkenInTime
SunkenInTime changed the base branch from t3/canvas-selection to main October 28, 2026 21:02
@SunkenInTime
SunkenInTime merged commit 3425462 into main Oct 28, 2026
14 of 26 checks passed
@SunkenInTime
SunkenInTime deleted the t3/canvas-group-move branch October 28, 2026 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant