Describe asset categories and the + menu submenus

- Concepts: the asset panel groups by category (Basics, Visuals,
  Logics, Physics, Audio), then by type. The + menu lists the same
  categories, each opening its types in a submenu.
- Architecture: Menu entries may carry a submenu, and Popup takes a
  backdrop option for it.
- Development: locate prefers hits in an open popup, and
  pickAssetType opens the category first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adminandClaude Opus 5.5 committed 2026-10-07 18:17:36 +02:00
1 parent 7545d2ea34
commit 9ff3778d5e
3 files changed
+45 -22

No files matched your search

+8 -4
@@ -70,8 +70,12 @@
the scrolling part — and it *measures* that chrome (a search box, a header, a row of buttons)
rather than repeating it as a number, so no popup can forget one. A fixed cap would waste a tall
screen or overflow a short one, and a constant gap that frames a popup on a laptop is a hairline
on a large screen. It is what holds the whole `+` menu of seventeen asset types on screen, and what
lets the image, font and sound pickers show six rows of what the server has instead of two.
on a large screen. It is what lets the image, font and sound pickers show six rows of what the
server has instead of two.
A **submenu** (`MenuSubmenu`, an entry with `submenu` instead of `onSelect`) is a second `Menu`
built with `under` set, opened right of its menu (left of it where the canvas ends), and without
a backdrop or Escape of its own: a backdrop would cover the menu below it, and the first menu's
backdrop and Escape close the whole stack. Picking an entry closes the stack from its first menu.
A popup that does not fit below its anchor is still *moved* up rather than squeezed, and
`clampPopupY()` enforces the tenth while moving it — otherwise the gap ends up entirely above it
and the popup sits flush on the bottom edge, which is exactly what it used to do.
@@ -303,10 +307,10 @@ src/editor/inspector/ Inspector panel, host (what a section builds into, and `e
objectQueries/vectorRows/parentRow, cutSection (the Nine-slice and Sorting rows, shared by the frame and the drawing inspectors, #308),
SpritePreview (a sprite as `syncSpriteMesh` draws it in the scene) + previewFit (whole device
pixels per unit, the corner on one)
src/editor/panels/ AssetPanel, assetGroups (groups by type and their order, #176), assetIcons (one SVG per
src/editor/panels/ AssetPanel, assetGroups (the categories, the type groups in them 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 + hierarchyDrop (#241: what
assetMenu (the categories of the + menu and their types), HierarchyPanel + hierarchyDrop (#241: what
dropping a row does, group rows included) + GroupNameDialog,
Toolbar + toolbarIcons + toolbarTips (icon buttons, no captions: menu, undo, redo, play, pause, #183, what each says as a tooltip, #209, and the eye
the scene tab's header wears, see ScenePane),
+17 -8
@@ -219,18 +219,27 @@ render layers in *Project settings…*. What is about the person at the screen r
project is in *General settings…* (#309), kept in this browser and the same in every project: the
**zoom transition** (see below). A project is deleted in *Open project…*: **Delete** asks first, and Cancel, Esc
and the deletion itself all lead back to the project list. The active scene is picked where the scenes are — by double-clicking
one in the **Assets** panel. The **Assets** panel's `+` creates an asset and opens its tab; its menu lists the types in
two alphabetical groups, first what can be placed into a scene, then what only lives in the library
(#43) — the line between them answers the question that follows right after "which type?".
one in the **Assets** panel. The **Assets** panel's `+` creates an asset and opens its tab; its menu lists the categories of
the panel (*Basics*, *Visuals*, *Logics*, *Physics*, *Audio*), and each opens a **submenu** beside it
with the types of that category, sorted by name — the same filing as in the panel, so a new asset
turns up where you looked for its kind. A submenu opens as soon as the pointer rests on its
category or on a click; moving on to another category swaps it after a short wait, so a pointer
crossing the rows on its way into a submenu does not take it away. Picking a type closes both, and
so do Escape and a click beside them. (Until then the menu was one list in two groups, placeable
types and library ones, #43.)
A right-click on a group heading or on the empty space below the rows opens the same menu (#244);
a right-click on an asset keeps its own menu.
Every type has an **icon** of its own, the same wherever the type shows: in the `+` menu, on the
asset panel's groups, on a document's tab and at the end of a row in **Scene objects** (#176).
The asset panel **groups by type** (#176): one heading per type that has assets, with its icon and
the count at the right, groups and the assets inside them sorted by name. Every group starts
**folded** when a project opens; a click anywhere on a heading folds or unfolds it, and a heading
is never selected. Selecting an asset from elsewhere (a picker, a graph, a new asset) unfolds its
group; folding the group the selected asset sits in lets go of the selection.
The asset panel **groups by category and type** (#176). On top are the categories *Basics*,
*Visuals*, *Logics*, *Physics* and *Audio*, always in that order. Under each is one heading per type
that has assets, named like its `+` menu entry (*Scene*, *Sprite*, *Sound FX*), with its icon and
the count at the right. Groups and the assets inside them are sorted by name. Which category a type
belongs to is set in `ASSET_GROUPS` (`editor/panels/assetGroups.ts`). When a project opens, every
category is **open** and every group **folded**; a click anywhere on a heading folds or unfolds it,
and a heading is never selected. Selecting an asset from elsewhere (a picker, a graph, a new asset)
unfolds its category and its group; folding the category or group the selected asset sits in lets
go of the selection.
A click on a row only
selects (the inspector shows it, no tab moves); a **double-click** opens the asset as a document
in a tab of its own (see **The tab row**) — a behavior, state machine, shader, particles,
+20 -10
@@ -19,7 +19,7 @@ php tests/php/accounts.test.php # the API's accoun
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
php tests/php/files.test.php # uploaded files in a folder per account (#344), the same way
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1863 editor checks, screenshots in /tmp/gm-e2e-*.png
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1864 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
@@ -125,15 +125,25 @@ not by clicking its name: since the tenth tab the row hides some of them at the
and the helper goes through the `»` menu when it has to. Clicking the label directly took the
whole suite down the day the Particles tab was added.
The same holds for the **`+` menu**: it has one entry per asset type, and an entry below the mask
edge is still found by `locate` while a click on it lands on nothing. The fourteenth type
(Variables) pushed `Font` under that edge and took the font step down, because only the particles
step had carried a workaround of its own; sound is the fifteenth, the tile the sixteenth and the tilemap the seventeenth. Since #43 a menu may grow to the
height of the canvas, so on the test window all seventeen stand on screen at once — the step still
picks a type with `pickAssetType(t, 'Font')` from the same file, which keeps the scroll as insurance
for a shorter window. The menu's own order is checked in the assets step through `__gm.menuRows()`:
two alphabetical groups, placeable types then library ones, one separator between them. Order is
the whole answer there, and no colour check can see an order.
The same holds for the **`+` menu**. A step picks a type with `pickAssetType(t, 'Font')` from the
same file: it asks `__gm.addMenuCategory('Font')` which category holds the type, clicks that
category, and picks the type inside the submenu that opens. It keeps a scroll as insurance for a
short window, since an entry below the mask edge is still found by `locate` while a click on it
lands on nothing (that is what took the font step down when the menu was one long list). A step
that wants to look at the entries itself clicks the category first; from then on
`__gm.menuRows()`, `__gm.menuIcons()` and `__gm.overlayRect()` describe the submenu, and
`__gm.menuSubmenus()` tells which rows open one. The assets step checks the order of the
categories and the sorted types under each. Order is the whole answer there, and no colour check
can see an order.
**An open popup wins over what is beneath it.** `locate` (and with it `find`, `has` and
`clickText`) takes the first text in drawing order, and the editor is drawn before any popup.
Since the asset panel's headings are named like the `+` menu entries (*Behavior*, *Sprite*, *Text*),
a click meant for an entry would have folded a heading. So `locate` looks in the popup layer
(`ui().overlay`) first and only then in the rest; `locate(text, true)` stays "the topmost". This
matches the screen: below an open menu a click only closes the menu. `04-assets` checks it with the
word *Behavior* on screen twice. The categories (*Logics*) read like the panel's category headings
in the same way. A submenu lies on the popup layer too, above the menu it hangs off.
The node palette has the same property, and `__gm.graph.paletteRows()` is its witness: the rows all
look alike, only their sequence says whether the search ranked them (#43).