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
@@ -1,10 +1,17 @@
|
||||
import { MakeProjectAssetCommand } from '../../core/commands/assetCommands';
|
||||
import { removeFromComposition, SetCompositionCommand } from '../../core/commands/compositionCommands';
|
||||
import type { CompositionAsset, CompositionItem } from '../../core/model/Asset';
|
||||
import { clipLength, compositionFps, fittedLength, itemVideo } from '../../core/model/composition';
|
||||
import {
|
||||
clipLength,
|
||||
compositionFps,
|
||||
fittedLength,
|
||||
isCompositionSprite,
|
||||
itemVideo,
|
||||
} from '../../core/model/composition';
|
||||
import { resizedItem } from '../../core/model/compositionEdit';
|
||||
import { findAssetOf, type Project } from '../../core/model/Project';
|
||||
import { findAsset, findAssetOf, type Project } from '../../core/model/Project';
|
||||
import type { PropertyDef } from '../../core/properties/PropertySchema';
|
||||
import { Button, formatNumber } from '../../ui';
|
||||
import { Button, formatNumber, type MenuEntry } from '../../ui';
|
||||
import { createFieldRow } from './fieldFactory';
|
||||
import { addGap, addNameField, addNote, addTitle, type InspectorHost } from './host';
|
||||
import { assetLink } from './objectSection';
|
||||
@@ -215,6 +222,25 @@ export function fitToMedia(host: Pick<InspectorHost, 'state'>, holderId: string,
|
||||
host.state.execute(new SetCompositionCommand(holderId, { items }, 'Fit to media'));
|
||||
}
|
||||
|
||||
/**
|
||||
* For an item wearing a sprite its composition keeps (#419): that sprite turned into a project asset,
|
||||
* listed and placeable anywhere. Greyed out for any other.
|
||||
*/
|
||||
export function makeProjectAssetEntry(
|
||||
host: Pick<InspectorHost, 'state'>,
|
||||
holderId: string,
|
||||
objectId: string,
|
||||
): MenuEntry {
|
||||
const { project } = host.state;
|
||||
const object = findAssetOf(project, holderId, 'composition')?.objects.find((o) => o.id === objectId);
|
||||
const asset = object && findAsset(project, object.assetId);
|
||||
return {
|
||||
label: 'Make project asset',
|
||||
disabled: !asset || !isCompositionSprite(asset),
|
||||
onSelect: () => asset && host.state.execute(new MakeProjectAssetCommand(asset.id)),
|
||||
};
|
||||
}
|
||||
|
||||
function withOffset(item: CompositionItem, offset: number): CompositionItem {
|
||||
const { fitMedia: _, ...rest } = item;
|
||||
return { ...rest, offset: Number.isFinite(offset) ? offset : 0 };
|
||||
|
||||
Reference in new issue
Block a user