Drop a picture or video onto a composition track (#419)
A track item has always carried an object, and an object needs an asset. Instead of objects without one, a dropped picture or video gets a sprite the composition owns, the way a song owns its sounds (#355). To the rest of the code it is a sprite like any other. - The asset list starts a drag for textures, vector art, pixel art and videos. Dropped on a track, they make the sprite and the item in one undo step. A video item is as long as the video and follows it. - One sprite per picture and composition. A second drop of the same video shares it. Once no item wears it, it goes in the same step. - Deleting a composition takes its sprites along. A duplicate gets sprites of its own. - Pasting into a scene or container turns the sprite into a project asset. Pasting into another composition gives that one a copy. The clipboard carries the sprite, so cut and paste loses nothing. - Owned sprites stay out of the asset list and the pickers. The inspector shows them with a dot in front. - "Make project asset" in the item's menu, on the timeline and in the hierarchy, turns the sprite into an ordinary one. - The first track of the Composition tab gets its top line, as the Song tab's has. Found on the way: loading kept an owner only for the asset types a song can hold. A composition's sprite would have lost its owner and turned up in the list after a reload. Tests: - Unit composition-sprites: 11 new. Without the rule 5 of them are red, without the loading fix the loading test is. - e2e 69 on melektaus-atx: ALL PASSED, 15 checks, 69k new - Gate on melektaus-atx: 305 files, 4714 tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f171c77f91
commit
b16fe8c718
18 files changed
+573
-49
No files matched your search
@@ -5,7 +5,8 @@
|
||||
* past it. An animation dragged in becomes a clip that moves the item it overlaps in the picture. The hierarchy lists what is alive at the playhead, Delete
|
||||
* takes an item with its object, and Play runs the playhead to the end. The video of an item about
|
||||
* to begin is opened ahead, so the cut to it shows its picture at once, and Play waits for a video
|
||||
* whose player is still loading. A copied item is pasted under the pointer or at the playhead.
|
||||
* whose player is still loading. A copied item is pasted under the pointer or at the playhead. A
|
||||
* video dragged in wears a sprite the composition keeps to itself.
|
||||
*
|
||||
* Everything it adds carries a `ct69` id and goes again at the end.
|
||||
*/
|
||||
@@ -308,8 +309,47 @@ export async function compositionTab(t) {
|
||||
JSON.stringify({ copies, picked: pickedNow?.objectIds }),
|
||||
);
|
||||
|
||||
// 69k. A video dragged straight onto a track (#419) is an item as long as the video, wearing a sprite
|
||||
// the composition keeps and the Assets panel leaves out; a second drop shares it, and undoing both
|
||||
// takes the sprite along.
|
||||
const kept = (id) =>
|
||||
json(`(() => {
|
||||
const p = __gm.state.project;
|
||||
const c = p.assets.find((a) => a.id === '${CUT}');
|
||||
const object = c.objects.find((o) => o.id === ${JSON.stringify(id)});
|
||||
const sprite = object && p.assets.find((a) => a.id === object.assetId);
|
||||
return { item: c.items.find((i) => i.id === ${JSON.stringify(id)}) ?? null,
|
||||
sprite: sprite ? { id: sprite.id, type: sprite.type, owner: sprite.owner, texture: sprite.props.texture } : null,
|
||||
owned: p.assets.filter((a) => a.owner === '${CUT}').length };
|
||||
})()`);
|
||||
const videoDrop = await dragAsset(VIDEO, await json(`__gm.composition.laneAt('ct69-t2', 70)`));
|
||||
const one = await kept((await json('__gm.state.selection'))?.objectIds?.[0] ?? '');
|
||||
const row = one.sprite ? await json(`__gm.assetRow('${one.sprite.id}')`) : 'none';
|
||||
await dragAsset(VIDEO, await json(`__gm.composition.laneAt('ct69-t2', 100)`));
|
||||
const two = await kept((await json('__gm.state.selection'))?.objectIds?.[0] ?? '');
|
||||
await evaluate('__gm.state.undo()');
|
||||
await evaluate('__gm.state.undo()');
|
||||
const undone = await kept('');
|
||||
check(
|
||||
'a video dropped on a track wears a sprite the composition keeps, shared by a second drop',
|
||||
videoDrop?.what === 'item' &&
|
||||
videoDrop.rejection === '' &&
|
||||
one.item?.track === 'ct69-t2' &&
|
||||
one.item.start === 70 &&
|
||||
one.item.length === 20 &&
|
||||
one.sprite?.type === 'sprite' &&
|
||||
one.sprite.owner === CUT &&
|
||||
one.sprite.texture === VIDEO &&
|
||||
row === null &&
|
||||
two.item?.start === 100 &&
|
||||
two.sprite?.id === one.sprite.id &&
|
||||
two.owned === 1 &&
|
||||
undone.owned === 0,
|
||||
JSON.stringify({ videoDrop, one, row, two, undone }),
|
||||
);
|
||||
|
||||
await showTab('scene');
|
||||
await mutate(`
|
||||
const p = __gm.state.project;
|
||||
p.assets = p.assets.filter((a) => !a.id.startsWith('ct69-'));`);
|
||||
p.assets = p.assets.filter((a) => !a.id.startsWith('ct69-') && !a.owner?.startsWith('ct69-'));`);
|
||||
}
|
||||
Reference in new issue
Block a user