Open a scene framed, and thin out two inspector sections (#295)

Three small things asked for in one issue. A scene came up wherever
the view happened to stand, and the inspector carried a button and a
list that are better reached somewhere else.

- A scene comes up framed as if Fit had been clicked: after a
  project is loaded and when another scene is opened. From then on
  the view belongs to the user.
- An empty scene has nothing to frame and stands at the origin, one
  pixel per unit. A scene the running game loads is not framed anew.
- The Container tab is left out. It keeps the view of each container
  as before (#171).
- The Unpack button and the two lines under it are gone from the
  inspector of a placed container. Unpack container stays in the
  right-click menu of the hierarchy and of the scene view.
- The corners of a polygon collider are no row in the inspector any
  more. They are edited on the outline in the scene view. The new
  schema option noRow keeps a property out of the inspector while it
  stays a property.

Tests:
- Gate on melektaus-atx: 219 files, 3401 tests, CHECK EXIT 0.
- One new unit test in tests/core/polygon.test.ts: the corners are
  a property of the collider and no row.
- Three new e2e checks: a loaded project is framed like Fit (module
  03), an opened scene is (04), and after a real reload a click on
  Fit moves nothing (09).
- Module 09 unpacks through the right-click menu now and checks that
  the inspector offers no button.
- Full suite on melektaus-atx: 1695 checks, all pass. Smokes 24, 19
  and 21, all pass.
- No counter-probe evaluated, and nothing looked at after the build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-02 23:52:49 +02:00
1 parent ca5b2db449
commit 963f8a152a
10 files changed
+106 -23

No files matched your search

+14
View File
@@ -402,6 +402,20 @@ export async function play(t) {
const { ok, detail } = await sameAsGame(label);
check(`Fit in the scene tab shows the world exactly as the game view does at ${label}`, ok, detail);
}
// 3i. A project that is loaded brings its scene up framed the same way, without the click (#295).
// The scale still stands at Fit, so the framed view is not the one a bare reset would give.
const gameAtFit = await json(`__gm.gameRect('o1')`);
await showTab('scene');
await evaluate('__gm.lookAt(5000, 5000)');
await evaluate('__gm.state.loadProject(__gm.state.project, __gm.state.revision)');
await showTab('scene');
const sceneOnLoad = await json(`__gm.objectRect('o1')`);
check(
'a loaded project shows its scene framed like Fit, without a click',
Boolean(sceneOnLoad) && ['x', 'y', 'w', 'h'].every((k) => Math.abs(sceneOnLoad[k] - gameAtFit[k]) < 0.01),
JSON.stringify({ gameAtFit, sceneOnLoad }),
);
await showTab('game');
g = await pickScale('100%');
check('and 100% can be picked back', g.mode === 1 && g.scale === 1, JSON.stringify(g));
await showTab('scene');
+14
View File
@@ -151,6 +151,8 @@ export async function assets(t) {
`__gm.state.project.assets.push({ id: 'scene2', type: 'scene', name: 'Level 2', props: { backgroundColor: 0x22252c }, objects: [] })`,
);
const secondScene = await json("__gm.assetRow('scene2')");
await evaluate('__gm.lookAt(5000, 5000)');
const pannedAway = await json(`__gm.objectRect('o1')`);
await showTab('graph');
await doubleClick(secondScene.x, secondScene.y);
check(
@@ -166,6 +168,18 @@ export async function assets(t) {
(await json('__gm.state.project.activeSceneId')) === 'scene1',
await json('__gm.state.project.activeSceneId'),
);
// An opened scene comes up framed, as if Fit had been clicked (#295) – not where the view stood.
const opened = await json(`__gm.objectRect('o1')`);
const fitAt = await json(`__gm.sceneHeaderAt('fit')`);
await click(fitAt.x, fitAt.y);
const fitted = await json(`__gm.objectRect('o1')`);
const sameRect = (a, b) =>
Boolean(a && b) && ['x', 'y', 'w', 'h'].every((k) => Math.abs(a[k] - b[k]) < 0.01);
check(
'a scene that is opened comes up framed like after a click on Fit',
sameRect(opened, fitted) && !sameRect(opened, pannedAway),
JSON.stringify({ pannedAway, opened, fitted }),
);
await mutate(`__gm.state.project.assets = __gm.state.project.assets.filter((a) => a.id !== 'scene2')`);
// 4d′. Opening an asset: a click on its row only selects, a double-click opens the tab it
+19 -5
View File
@@ -388,10 +388,12 @@ export async function containers(t) {
const storedBeforeUnpack = (await storedObjects()).length;
await selectObject(instanceC.id);
check(
'the inspector shows what the placed container follows',
await has('Follows a template of 5 object(s).'),
'the inspector shows what the placed container follows, without a button to unpack it (#295)',
(await has('Follows a template of 5 object(s).')) && !(await has('Unpack')),
);
await clickText('Unpack');
const instanceCRow = await json(`__gm.hierarchyRow(${JSON.stringify(instanceC.id)})`);
await click(instanceCRow.x, instanceCRow.y, 'right');
await clickText('Unpack container');
check(
'Unpack writes the content into the scene, keeping the ids and the listing',
(await storedObjects()).length === storedBeforeUnpack + 5 &&
@@ -405,8 +407,9 @@ export async function containers(t) {
);
await selectObject(instanceC.id);
check(
'and the inspector stops offering the unpack, there is nothing left to follow',
!(await has('Unpack')) && !(await has('Follows a template of 5 object(s).')),
'and the inspector says so, there is nothing left to follow',
(await has('Unpacked: its content is stored in the scene.')) &&
!(await has('Follows a template of 5 object(s).')),
);
await setTemplate(`tpl.position.x = -300`);
check(
@@ -597,6 +600,17 @@ export async function containers(t) {
const listedBeforeReload = (await objects()).length;
await navigate();
await sleep(500);
// The reloaded scene stands framed already (#295): a click on Fit moves nothing.
const afterReload = await json(`__gm.objectRect(${JSON.stringify(instanceB.id)})`);
const fitButton = await json(`__gm.sceneHeaderAt('fit')`);
await click(fitButton.x, fitButton.y);
const afterFit = await json(`__gm.objectRect(${JSON.stringify(instanceB.id)})`);
check(
'after a reload the scene is framed as Fit frames it',
Boolean(afterReload && afterFit) &&
['x', 'y', 'w', 'h'].every((k) => Math.abs(afterReload[k] - afterFit[k]) < 0.01),
JSON.stringify({ afterReload, afterFit }),
);
check(
'a reloaded project resolves the same content again, deviations included',
(await objects()).length === listedBeforeReload &&