Describe groups in the scene hierarchy (#241)

- Scene editing: new section "Groups in the hierarchy", plus the click
  on an object that is already selected with others.
- Architecture: group model, treeOrder and the drop rules.
- Development: check sum 1442.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adminandClaude Opus 5.5 committed 2026-09-27 01:18:05 +02:00
1 parent 79c3b3ef53
commit 2b694379c3
3 files changed
+38 -2

No files matched your search

+4 -1
@@ -144,6 +144,8 @@ src/editor/ App → Editor (layout + ticker), PlaySession (the run),
EditorRun (run in editor), state, actions, shortcuts, clipboard (#240: copy and paste of
objects and graph pieces — where a paste lands, into which parent, what a graph may take;
the snapshot is `core/model/clipboard`, the paste `core/commands/pasteCommands`),
objectGroups (#241: the group entries of the object menu and the group row's menu;
the commands are `core/commands/groupCommands`),
debugHook (installs `window.__gm`;
the hooks are grouped by area in debug/: ui, scene, graph, documents, pixel art, vector art,
render — `tools/` sees one flat object as before),
@@ -194,7 +196,8 @@ src/editor/inspector/ Inspector panel, host (what a section builds into), field
src/editor/panels/ AssetPanel, assetGroups (groups by type and their order, #176), assetIcons (one SVG per
asset type in icons/, shared by the panel, the + menu, the tabs and Scene objects,
plus the List, Game, Plugins and User management tabs' own four, which stand for no asset),
assetMenu (the two groups of the + menu), HierarchyPanel,
assetMenu (the two groups of the + menu), HierarchyPanel + hierarchyDrop (#241: what
dropping a row does, group rows included) + GroupNameDialog,
Toolbar + toolbarIcons (icon buttons, no captions: menu, undo, redo, play, pause, #183, and the eye),
SceneVisibilityPopup (behind the eye: symbol, name and collider outline per type),
headerIcons (the pictures of the tab headers' switches: the magnet, #194),
+1 -1
@@ -15,7 +15,7 @@ npm run build # writes dist/ and regenerates index.html
php tools/make-user.php <name> <password> [admin|user] # an account; every tool signs in (see Accounts)
php tests/php/accounts.test.php # the API's account rules, no server needed (CI's gate job)
php tests/php/snapshots.test.php # the snapshot store (#231), the same way
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1432 editor checks, screenshots in /tmp/gm-e2e-*.png
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1442 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
+33
@@ -149,6 +149,39 @@ unfolds the way down to it and scrolls it into view, so a selection is never hid
play mode entities carry the same `parent` link: moving a parent entity moves its subtree,
`Destroy` removes it. `Get position` exposes both the local and the world coordinates.
**Groups in the hierarchy (#241).** A group is a folder for top-level objects — 100 crates under
one foldable row, say. It is editor looks only: parents, positions and behaviors stay exactly as
they are, and the game never sees it. Select one or more top-level objects, right-click →
**Group…**, name it; the row shows a folder, the name and the number of members, the members
indented below it and their own children one step further in. Only top-level objects go in: the
entry is greyed out as soon as a child is selected, and an object put under a parent leaves its
group. There are no groups inside groups.
- **Clicking the group row selects all members**, so they move together in the view; Ctrl-click
adds or takes them off the selection. A group whose members are all selected stays folded; one
selected member alone unfolds it, like a parent.
- **A plain click on a row that is already selected** along with others keeps only that row
selected, a member of a group included. The press alone changes nothing yet, the release does, so
a drag that starts there leaves the selection as it was.
- **Dragging** an object onto the group row puts it in behind the last member; dropping it into
the gap between two members puts it there, dropping it beside a row outside the group takes it
out. The group row itself drags as one block between top-level rows.
- **The group row's menu** has *Select members*, *Rename…* and *Ungroup* (the members stay).
The object menu adds *Remove from group*. Grouping, renaming, ungrouping and moving in or out
are one undo step each.
- **Order:** a group is listed where its first member stands, with the others moved up behind it.
That listed order is also the order equal `z` is drawn in (`groupedOrder` in
`src/core/model/hierarchy.ts`), in the editor and in the game, so the list never shows a
different order than the one drawn. Grouping objects can therefore change which of two objects
at the same `z` is in front.
- Which groups are open is view state like a folded parent: a reloaded scene starts folded.
- A copy pasted at the top level of the same scene lands in its original's group. A container made
from members of one group takes their place in it; its template has no groups of its own. A
container tab can group its template's top level the same way, and a placed container shows
its content in that order.
- Stored as `groups: [{ id, name }]` on the scene or container and `groupId` on the object; a
group left without members disappears from the list and is dropped on the next load.
**The object menu in the view.** Right-clicking an object in the scene view opens the same menu
its row in **Scene objects** has (#204) — one function builds both (`src/editor/objectMenu.ts`),
so they cannot drift apart. Only *Expand all* / *Collapse all* stay in the panel, where they mean