Show a placed object's struct the way its asset does
The instance inspector drew its own, flatter version of a struct: one row per field, no bundle head. It now draws the same block as the asset side, because there is only one implementation left - both call buildStructBlock. What the instance does not get is everything that touches the shape: renaming a field, '+ Field', and the per-field '×'. The shape belongs to the asset; a placed object overrides values. The revert and override marks sit on the head rather than on a field, since an override covers the whole bundle at once. Underneath this, editing a single field of an unoverridden struct used to blank its siblings: structFieldsOf filled the untouched fields from spec.read, which is object.overrides[key] and therefore undefined until something is overridden. structOf then handed back the empty shape. PreviewedValue carries 'current' now - the resolved value the row actually shows - and the fields come from that. Without it the old path stays, which is right for the asset side, whose prop bag is dense.
This commit is contained in:
1 parent
14f542365e
commit
9f482573e5
9 files changed
+606
-207
No files matched your search
+94
-2
@@ -3,7 +3,7 @@
|
||||
* same row, and writing one field keeps the others – the trap a shared snapshot would fall into.
|
||||
*/
|
||||
export async function structVariable(t) {
|
||||
const { check, sleep, json, shot, click, clickText, press, typeText, mutate, undo } = t;
|
||||
const { check, sleep, json, shot, click, clickText, press, typeText, mutate, select, undo } = t;
|
||||
|
||||
/** `locate` throws when the text is absent, which is the wrong shape for "is it there?". */
|
||||
const find = (text) => json(`__gm.locate(${JSON.stringify(text)}, false)`);
|
||||
@@ -135,13 +135,105 @@ export async function structVariable(t) {
|
||||
|
||||
await mutate('__gm.state.openList(null)');
|
||||
await sleep(200);
|
||||
|
||||
// 7. 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.
|
||||
const placed = () => json("__gm.state.activeScene.objects.find((o) => o.id === 'ostruct').overrides.piece");
|
||||
await mutate(`
|
||||
__gm.state.activeScene.objects.push({
|
||||
id: 'ostruct',
|
||||
assetId: 'structy',
|
||||
name: 'Placed structy',
|
||||
position: { x: 0, y: 0, z: 0 },
|
||||
editorOffset: { x: 0, y: 0 },
|
||||
overrides: {},
|
||||
parentId: '',
|
||||
instanceEdits: {},
|
||||
});
|
||||
`);
|
||||
await select({ kind: 'object', holderId: 'scene1', objectId: 'ostruct' });
|
||||
await sleep(200);
|
||||
check('a placed behavior shows its bundle, filled from the asset', (await find('tee')) !== null);
|
||||
|
||||
// 8. It reads exactly like the asset does: a head carrying the struct mark, then one row per
|
||||
// field with its kind mark, its name and its value. What is missing is the shape editing –
|
||||
// a placed object overrides values, the declaration stays with the asset that owns it.
|
||||
const bundleMark = await find('⊞');
|
||||
const headName = await find('piece');
|
||||
const fieldMark = await find('#');
|
||||
const fieldName = await find('column');
|
||||
const fieldValue = await json("__gm.inspectorField('column')");
|
||||
check(
|
||||
'the bundle is a head above its fields, marked as a struct',
|
||||
bundleMark !== null &&
|
||||
headName !== null &&
|
||||
fieldName !== null &&
|
||||
bundleMark.x < headName.x &&
|
||||
headName.y < fieldName.y,
|
||||
JSON.stringify({ bundleMark, headName, fieldName }),
|
||||
);
|
||||
check(
|
||||
'and a field row runs kind mark, name, value',
|
||||
fieldMark !== null &&
|
||||
fieldName !== null &&
|
||||
fieldMark.x < fieldName.x &&
|
||||
fieldName.x < fieldValue.x &&
|
||||
Math.abs(fieldMark.y - fieldName.y) < 4,
|
||||
JSON.stringify({ fieldMark, fieldName, fieldValue }),
|
||||
);
|
||||
check('the shape itself cannot be changed from here', (await find('+ Field')) === null);
|
||||
|
||||
// Typing over a name has to do nothing: a Label is not a TextField, and the declaration is
|
||||
// the asset's. Plain letters are safe here – the only shortcuts are modified ones and Delete.
|
||||
const declaration = () =>
|
||||
json(
|
||||
"(() => { const v = __gm.state.project.assets.find((a) => a.id === 'structy').variables[0];" +
|
||||
' return `${v.key}:${v.fields.map((f) => f.key).join(",")}`; })()',
|
||||
);
|
||||
const before = await declaration();
|
||||
// A missing label would throw here and kill the step; clicking nowhere keeps it a plain failure.
|
||||
for (const cell of [headName, fieldName]) {
|
||||
await click(cell?.x ?? 0, cell?.y ?? 0);
|
||||
await typeText('renamed');
|
||||
await press('Enter');
|
||||
}
|
||||
await sleep(200);
|
||||
check('typing over the names of a placed bundle renames nothing', (await declaration()) === before, before);
|
||||
|
||||
const placedField = await json("__gm.inspectorField('column')");
|
||||
await click(placedField.x, placedField.y);
|
||||
await press('a', { ctrl: true });
|
||||
await typeText('7');
|
||||
await press('Enter');
|
||||
await sleep(250);
|
||||
check(
|
||||
'the first override of one field keeps the asset values of the others',
|
||||
JSON.stringify(await placed()) === '{"column":7,"tag":"tee"}',
|
||||
JSON.stringify(await placed()),
|
||||
);
|
||||
await shot('04-object-override');
|
||||
|
||||
await undo();
|
||||
await sleep(200);
|
||||
check(
|
||||
'and undo takes the override away again, not just the one field',
|
||||
(await json("__gm.state.activeScene.objects.find((o) => o.id === 'ostruct').overrides.piece")) === null,
|
||||
JSON.stringify(await placed()),
|
||||
);
|
||||
|
||||
await mutate(`
|
||||
const p = __gm.state.project;
|
||||
p.assets = p.assets.filter((a) => a.id !== 'structy');
|
||||
const scene = __gm.state.activeScene;
|
||||
scene.objects = scene.objects.filter((o) => o.id !== 'ostruct');
|
||||
__gm.state.select(null);
|
||||
__gm.state.preview();
|
||||
`);
|
||||
await sleep(200);
|
||||
const left = await json('__gm.state.project.assets.filter((a) => a.id === "structy").length');
|
||||
const left = await json(
|
||||
'__gm.state.project.assets.filter((a) => a.id === "structy").length +' +
|
||||
' __gm.state.activeScene.objects.filter((o) => o.id === "ostruct").length',
|
||||
);
|
||||
check('the step leaves the fixture as it found it', left === 0, String(left));
|
||||
}
|
||||
Reference in new issue
Block a user