Key what an empty timeline row shows on a double-click (#412)

A double-click into a timeline row without keys set the property's
default, while the field in the row header showed the value the target
actually holds. A sprite standing on x 40 got a key with 0, so the
first key moved it. The double-click now keys the value the row shows.

- trackHeld in src/core/model/animation.ts says what a row without keys
  holds. The field in the row header and SetAnimationKeyCommand both
  ask it, so they cannot drift apart again.
- A relative row still gets 0, a row with keys still takes the value of
  its curve at that frame, and a row whose target is gone still only
  gives a notice and no key.
- README and the wiki page Development name 2047 checks.

Tests:
- Unit animation: 87 green, 1 new. A sprite on x 40 with the override
  scale 2 gets keys with 40 and 2. Red with the old def.default.
- e2e 25 on melektaus-atx: ALL PASSED, 35 checks, 25i-ii new. The arm
  stands on x 40 and a double-click on frame 3 keys { frame: 3,
  value: 40 }.
- Gate on melektaus-atx: green, 304 files, 4703 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-12 18:09:02 +02:00
1 parent d4fc425233
commit c425eea004
5 files changed
+51 -6

No files matched your search

+18
View File
@@ -478,6 +478,24 @@ export async function animation(t) {
JSON.stringify({ whilePlaying, scrubbed }),
);
// 25i-ii. A double click into a row without keys keys what its header showed (#412): the Arm
// stands at x 40, not at the property's default 0.
await mutate(`
const p = __gm.state.project;
p.assets.find((a) => a.id === 'an-box').objects[0].position.x = 40;
p.assets.find((a) => a.id === 'an-walk').tracks = [{ id: 'an-r4', target: 'an-arm', property: 'x', keys: [] }];
`);
await sleep(150);
const shownX = (await json('__gm.animation.lines()')).find((l) => l.kind !== 'group')?.value;
const emptyCell = await json("__gm.animation.cellAt('an-r4', 3)");
await doubleClick(emptyCell.x, emptyCell.y);
const keyedX = (await walk()).tracks[0].keys;
check(
'a double click into an empty row keys the value its header showed – 40, not the default 0',
shownX === 40 && JSON.stringify(keyedX) === JSON.stringify([{ frame: 3, value: 40 }]),
JSON.stringify({ shownX, keyedX }),
);
await mutate(`
const p = __gm.state.project;
p.assets = p.assets.filter((a) => !['an-walk', 'an-box', 'an-sprite'].includes(a.id));