Show the list a running game fills in the list tab (#251)
While playing, the inspector said "2 items" for a list a graph had filled, but clicking it opened an empty list tab. The inspector reads a Variables asset from the running game, the list tab only read the list stored in the project. It did not follow the run's ticks either. - resolveList takes the live values. For a Variables asset it uses the running game's value while one runs, the project's otherwise. This is the inspector's rule. - ListEditor refreshes on runtime ticks about 10 times a second, and not while a field in the tab is being typed in, like the inspector. - Editor hands the list editor the inspector's liveVariables. - __gm.list.info() also returns rows, the rows the last build drew. - Editing in the tab while playing goes into the run's copy of the project, as in the inspector. A very long, growing list rebuilds all rows up to 10 times a second. Tests: - ./check.sh: 180 files, 2634 tests, CHECK EXIT 0 - e2e GM_STEPS=1,32: ALL PASSED, 30 checks - e2e 32b-ii, 2 checks: a graph adds an item every frame, the test pauses and clicks "N items" with a real mouse. The tab draws 26 rows for 26 items, grows to 50 after resuming and is empty after Stop. - Counter-checks: without live values the tab draws 0 rows for 23 items, and without the refresh it stays at 23 - README: 1481 editor checks, counted by hand Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b90b62886b
commit
92fad6b9e3
5 files changed
+126
-9
No files matched your search
@@ -27,6 +27,21 @@ const counterGraph = {
|
||||
],
|
||||
};
|
||||
|
||||
/** `On update → Variables{set bag} ← Join lists(bag, 7)`: a shared list that grows every frame. */
|
||||
const fillerGraph = {
|
||||
nodes: [
|
||||
node('sc-f-update', 'OnUpdate', 0, 0),
|
||||
node('sc-f-read', 'Variables', 0, 200, { variablesAsset: 'sc-bag' }, [port('bag', 'list')]),
|
||||
node('sc-f-join', 'Concat', 260, 200, { b: [7] }),
|
||||
node('sc-f-write', 'Variables', 560, 0, { variablesAsset: 'sc-bag' }, [written('bag', 'list')]),
|
||||
],
|
||||
edges: [
|
||||
edge('sc-f-e1', 'sc-f-update', 'exec', 'sc-f-write', 'exec'),
|
||||
edge('sc-f-e2', 'sc-f-read', 'bag', 'sc-f-join', 'a'),
|
||||
edge('sc-f-e3', 'sc-f-join', 'result', 'sc-f-write', 'bag'),
|
||||
],
|
||||
};
|
||||
|
||||
export async function scope(t) {
|
||||
const { check, sleep, json, shot, click, locate, asset, mutate, stop, has } = t;
|
||||
|
||||
@@ -89,6 +104,72 @@ export async function scope(t) {
|
||||
);
|
||||
await stop();
|
||||
|
||||
// 32b-ii. A list the run fills: the inspector row counts the run's items, so the List tab it
|
||||
// opens has to show those too and follow them – not the empty list the document declares.
|
||||
await mutate(`
|
||||
const p = __gm.state.project;
|
||||
const scene = __gm.state.activeScene;
|
||||
const mover = p.assets.find((a) => a.id === 'mover');
|
||||
const f = structuredClone(mover);
|
||||
Object.assign(f, {
|
||||
id: 'sc-filler',
|
||||
name: 'Filler',
|
||||
props: { enabled: true, runInEditor: false },
|
||||
variables: [],
|
||||
graph: ${JSON.stringify(fillerGraph)},
|
||||
});
|
||||
p.assets.unshift(f, {
|
||||
id: 'sc-bag',
|
||||
type: 'variables',
|
||||
name: 'Bag',
|
||||
props: { bag: [] },
|
||||
variables: ${JSON.stringify([variable('bag', 'list', [])])},
|
||||
});
|
||||
${MAKE_OBJECT}
|
||||
scene.objects.push(make('sc-brain-f', 'sc-filler', 'sc-box-a', 0, 'Filler'));
|
||||
`);
|
||||
// Held, so the count the row shows stands still long enough to be found and clicked.
|
||||
await t.play();
|
||||
await sleep(300);
|
||||
await t.clickToolbar('pause');
|
||||
await sleep(150);
|
||||
const held = (await json('__gm.world()')).shared.variables['sc-bag']?.bag ?? [];
|
||||
await selectAsset('sc-bag');
|
||||
await sleep(200);
|
||||
const countRow = await t.find(`${held.length} items`);
|
||||
if (countRow) await click(countRow.x, countRow.y);
|
||||
await sleep(150);
|
||||
const first = await json('__gm.list.info()');
|
||||
await shot('01b-live-list');
|
||||
await t.clickToolbar('pause');
|
||||
await sleep(500);
|
||||
const later = await json('__gm.list.info()');
|
||||
await stop();
|
||||
await sleep(200);
|
||||
const stopped = await json('__gm.list.info()');
|
||||
check(
|
||||
'while playing, the List tab opened from a shared list shows the items the run put in',
|
||||
held.length > 0 &&
|
||||
Boolean(countRow) &&
|
||||
(await json('__gm.tab()')) === 'list' &&
|
||||
first.title === 'Bag · bag' &&
|
||||
first.rows === held.length &&
|
||||
first.items.every((item) => item === '7'),
|
||||
JSON.stringify({ run: held.length, row: countRow, title: first.title, rows: first.rows }),
|
||||
);
|
||||
check(
|
||||
'and follows the list as it grows, then shows the declared list again once the run stops',
|
||||
later.rows > first.rows && stopped.rows === 0 && stopped.items.length === 0,
|
||||
JSON.stringify({ first: first.rows, later: later.rows, stopped: stopped.rows }),
|
||||
);
|
||||
await mutate(`
|
||||
const p = __gm.state.project;
|
||||
__gm.state.activeScene.objects = __gm.state.activeScene.objects.filter((o) => o.id !== 'sc-brain-f');
|
||||
p.assets = p.assets.filter((a) => a.id !== 'sc-filler' && a.id !== 'sc-bag');
|
||||
__gm.state.openList(null);
|
||||
__gm.state.select(null);
|
||||
`);
|
||||
|
||||
// 32c. The asset is where the declaration lives, and it says what it is for.
|
||||
await selectAsset('sc-vars');
|
||||
const declared = await has('score');
|
||||
|
||||
Reference in new issue
Block a user