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
@@ -12,6 +12,7 @@ export async function structVariable(t) {
|
||||
shot,
|
||||
click,
|
||||
clickText,
|
||||
drag,
|
||||
press,
|
||||
typeText,
|
||||
mutate,
|
||||
@@ -214,6 +215,48 @@ export async function structVariable(t) {
|
||||
await undo();
|
||||
check('undo takes both back out', !('field1' in (await piece())), JSON.stringify(await piece()));
|
||||
|
||||
// 22f-iii. Reordering by dragging the mark (#310): a variable past a struct of several rows, and a
|
||||
// field among the fields of its struct. A drop just above a mark lands in front of its row.
|
||||
await mutate(`
|
||||
const asset = __gm.state.project.assets.find((a) => a.id === 'structy');
|
||||
asset.variables.push({ key: 'count', label: 'count', kind: 'number', default: 0 });
|
||||
asset.props.count = 5;
|
||||
`);
|
||||
const order = () =>
|
||||
json(
|
||||
"(() => { const v = __gm.state.project.assets.find((a) => a.id === 'structy').variables;" +
|
||||
" return v.map((d) => d.key).join(',') + '|' + v[v.findIndex((d) => d.key === 'piece')].fields.map((f) => f.key).join(','); })()",
|
||||
);
|
||||
const dragMark = async (from, onto) => {
|
||||
const grip = await json(`__gm.inspectorMark(${JSON.stringify(from)})`);
|
||||
const target = await json(`__gm.inspectorMark(${JSON.stringify(onto)})`);
|
||||
await drag(grip.x, grip.y, target.x, target.y - 6);
|
||||
};
|
||||
await dragMark('count', 'piece');
|
||||
check(
|
||||
'dragging a variable by its mark puts it in front of the struct, fields and all',
|
||||
(await order()) === 'count,piece|column,tag',
|
||||
await order(),
|
||||
);
|
||||
await dragMark('tag', 'column');
|
||||
check(
|
||||
'a field moves among the fields of its struct',
|
||||
(await order()) === 'count,piece|tag,column',
|
||||
await order(),
|
||||
);
|
||||
check(
|
||||
'and the values stay with their names',
|
||||
JSON.stringify(await piece()) === '{"column":30,"tag":"tee"}',
|
||||
);
|
||||
await undo();
|
||||
await undo();
|
||||
check('each move is one undo step', (await order()) === 'piece,count|column,tag', await order());
|
||||
await mutate(`
|
||||
const asset = __gm.state.project.assets.find((a) => a.id === 'structy');
|
||||
asset.variables = asset.variables.filter((v) => v.key !== 'count');
|
||||
delete asset.props.count;
|
||||
`);
|
||||
|
||||
// 22g. The same bundle on a placed object. An override is sparse – it starts out unset – while a
|
||||
// field edit rebuilds the whole bundle, so the untouched fields have to come from what the row
|
||||
// shows (the asset's values) rather than from the empty form of the declaration.
|
||||
|
||||
Reference in new issue
Block a user