Drag variables, fields, ports and list items into order (#310)

Behaviors gets an entry Reordering. The mark in front of a row is the
grip, a drag starts after 4 px, rows move among siblings only, and
every move is one undo step.

- Structs: fields of a Struct asset are dragged there, typed copies
  and Pack/Expand follow.
- Architecture: sortBlocks and the pure rules in ui/sortable.ts.
- Development: e2e sum 1739.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adminandClaude Opus 5.5 committed 2026-10-03 21:45:43 +02:00
1 parent 50dd1c18a4
commit b4b9464bdd
4 files changed
+18 -3

No files matched your search

+2 -2
@@ -169,7 +169,7 @@ src/runtime/physics/ engine (loads Rapier once, reports its state, converts be
src/ui/ widget toolkit (generic, knows nothing about assets); fonts (the faces the editor ships:
Source Sans 3 in 400, 500 and 600, Source Code Pro 400, Latin only, #202/#214/#273), icons +
Icon (the menu's tick boxes and current-entry dot as SVG files, and a widget that shows one file
on its own: a symbol the Latin subset lacks is never written as a character, #214); SortableList + sortable (the layer list of every editor that has one: rows dragged into order, eye, duplicate, cross, rename in place; the index arithmetic of a list shown upside down, kept apart so it is measured without a canvas)
on its own: a symbol the Latin subset lacks is never written as a character, #214); SortableList + sortable (the layer list of every editor that has one: rows dragged into order, eye, duplicate, cross, rename in place; the index arithmetic of a list shown upside down, of a drop in the tile strip and of blocks of rows of any height, kept apart so it is measured without a canvas); blockDrag (`sortBlocks`: the inspector's own rows dragged into order by a grip, a block being every row it covers, #310)
src/viewport/ WorldViewport (the drawing of a world or a holder, the base of every view; it draws from
a `ViewSource` — the project and word of a change — so a released game draws without the
editor, #266; a view through a camera keeps only what that camera `Sees` and lays one
@@ -326,7 +326,7 @@ src/editor/curve/ CurveEditor (header: Preset, the picked point's type, Pre
preview dot and its rail) + curveView (the curve laid onto a picture for those gestures and
read back, a point held between its neighbours, a point put down on free space)
src/editor/tile/ TileEditor (header + kind), TileCanvas (every tile of the block and a sample map, animated),
PictureStrip (the pictures as animation steps; dropIndex is the reorder maths),
PictureStrip (the pictures as animation steps; its reorder maths is `dropIndex` in ui/sortable),
TilePalette (the tiles as a grid of pictures in the tilemap panel, #139)
src/editor/statemachine/ StateMachineEditor (header, on the graph canvas)
src/editor/particles/ ParticlesEditor (graph canvas left of a preview panel, splitter between them),
+12
@@ -181,6 +181,18 @@ the inspector).
single field. All of it sits on **one column grid** with the rest of the inspector: mark (28 px),
gap and name (90 px) are exactly as wide as a plain row's label (124 px), a nested field's name gives up what
its indent takes, and names are cut with `…` rather than pushing their value out of line.
- **Reordering** (#310): variables, the fields of a struct at any depth, the items of a list and the
declared ports of a Start, End, `Pack`/`Expand` or shader function are **dragged by the mark in
front of their row** — the kind mark, for a list item its number. A press that moves less than
4 px stays a click, so the kind picker of a list item still opens. A block moves with all of its
rows (a struct with its fields, an item with its nested list) and is dimmed while it is carried;
an accent line shows where it lands, and at the top or bottom edge the panel scrolls along. It
moves among its siblings only: a field stays in its struct, an item in its list. Every move is
one undo step. Values and edges hang on names, so only the order changes: of the rows, of the
fields of `Pack`/`Expand`, of the ports on every node running the behavior. A list's order is its
value, so `Item at` 0 reads the new first item. A list cut off with `… N more` is reordered among
the rows drawn, the whole list in its tab. A placed object draws its variables in the asset's
order and cannot change it, and the fields of a typed struct are moved on the Struct asset.
- **Owner** = parent: a placed behavior is usually parented to the object it controls
(inspector → Parent, or drag it onto that object in the hierarchy; dashed line in the
viewport). The `Owner` node returns it; `Self` returns the behavior itself; `Parent` walks up
+1 -1
@@ -18,7 +18,7 @@ php tests/php/accounts.test.php # the API's accoun
# has and the web server's container has not (SKIP there)
php tests/php/snapshots.test.php # the snapshot store (#231), the same way
php tests/php/releases.test.php # the release store (#266), the same way
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1735 editor checks, screenshots in /tmp/gm-e2e-*.png
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1739 editor checks, screenshots in /tmp/gm-e2e-*.png
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step
./e2e.sh start [GM_STEPS] # the same run detached, see below
./e2e.sh status # live progress, or the last run's result
+3
@@ -15,6 +15,9 @@ a **FIELDS** section that works like the VARIABLES section of a behavior or a Va
an object, a list or a struct.
- The name field renames it, the remove button at the end of the row removes it.
- The value next to it is the field's **default**: what a struct of this type starts with.
- Dragging the mark in front of a field moves it to another place (#310). Every place of the type
follows the new order: its variables and struct fields, the ports of `Pack`/`Expand` and the
fields of a split output.
- **Open** shows the fields in a tab of their own, as it does for variables.
A field may itself be a struct with a type. What it may not be is its own type, directly or through