Let variables, fields, ports and list items be dragged into order (#310)
Variables of a behavior, the fields of a struct, the ports of a start or end node and the items of a list could only be added and removed. Changing their order meant removing and adding them again. Now each of them is dragged by the mark in front of its row. - sortBlocks (ui/blockDrag.ts) makes the rows of one column sortable. A block is every row it covers, so a struct moves with its fields and a list item with its nested list. The block is dimmed while it is carried, an accent line shows where it lands, and a scrolled panel follows the pointer at its edge. Less than 4 px stays a click. - The arithmetic is pure and lives in ui/sortable.ts: blockGap, gapLineY, dropSlot and edgeScroll. dropIndex of the tile strip moved there too, so the reorder maths sits in one file. - MoveVariableCommand, MoveStructFieldCommand and MovePortCommand build on the existing MoveCommand. Undo moves back. - Lists need no command. InlineList replaces the list as a whole, as it does for every other edit of its items. - The grip is the kind mark, for a list item its number. Moving only happens among siblings: a field stays in its struct. - On a Struct asset the move runs through WithStructTypes. Typed variables, Pack/Expand ports and split outputs follow the new order. - Values and edges hang on names, so only the order changes. Nodes running a behavior read their ports from the start node, the new order reaches them without a copy. - Not sortable by design: the variables of a placed object, the fields of a typed struct (moved on the Struct asset) and the picked ports of Variables/Properties nodes. - Test hook __gm.inspectorMark(label) finds the mark of a row. Tests: - Gate on melektaus-atx: 235 files, 3572 tests, CHECK EXIT 0 (+9). - tests/core/reorder.test.ts: a variable, a field two levels deep, a Struct asset reaching a typed variable and Pack, and start ports reaching the calling node with edges and result kept. - e2e 22: ALL PASSED, 39 checks (+4, sum 1739). 22f-iii drags a variable past a struct of several rows, then a field inside it. - Counter-probes: dropSlot by top edge instead of middle and a field move that ignores its path fail 3 unit tests. With sortBlocks switched off, the three order checks of 22f-iii fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cf6043a066
commit
2c19c89878
18 files changed
+671
-83
No files matched your search
@@ -54,7 +54,9 @@ export function uiHooks({ app, state, editor }: HookContext) {
|
||||
return control && { ...centreOf(control), left: control.getGlobalPosition().x };
|
||||
};
|
||||
/** The control right of the row label `label`, as `fieldOf` finds it. */
|
||||
const controlOf = (label: string, last: boolean): Widget | null => {
|
||||
const controlOf = (label: string, last: boolean): Widget | null => besideOf(label, last, 1);
|
||||
/** The widget `step` places along from the row label `label`: +1 its control, -1 its mark. */
|
||||
const besideOf = (label: string, last: boolean, step: number): Widget | null => {
|
||||
let found: Text | null = null;
|
||||
const walk = (node: Container): void => {
|
||||
if ((found !== null && !last) || !node.visible) return;
|
||||
@@ -70,7 +72,7 @@ export function uiHooks({ app, state, editor }: HookContext) {
|
||||
while (owner && !(owner instanceof Widget)) owner = owner.parent;
|
||||
const row = owner?.parent;
|
||||
if (!row || !owner) return null;
|
||||
const control = row.children[row.children.indexOf(owner) + 1];
|
||||
const control = row.children[row.children.indexOf(owner) + step];
|
||||
if (!(control instanceof Widget)) return null;
|
||||
// Taller than the window since its groups have bands (#290): the row is scrolled into view.
|
||||
inspector.reveal(control);
|
||||
@@ -164,6 +166,11 @@ export function uiHooks({ app, state, editor }: HookContext) {
|
||||
return (last ? hits.at(-1) : hits[0]) ?? null;
|
||||
},
|
||||
inspectorField: fieldOf,
|
||||
/** The mark in front of the row named `label` – what a variable, a field or a port is dragged by (#310). */
|
||||
inspectorMark: (label: string, last = false) => {
|
||||
const mark = besideOf(label, last, -1);
|
||||
return mark && centreOf(mark);
|
||||
},
|
||||
/** The eyedropper beside the object field of a row (#276): where to press it, and whether it is lit. */
|
||||
inspectorEyedropper: (label: string, last = false) => {
|
||||
const button = controlOf(label, last)?.children.at(-1);
|
||||
|
||||
Reference in new issue
Block a user