Create a sprite asset from a texture's context menu
The sprite takes the whole image at its pixel size and the texture's name, numbered like any other clash. CreateAssetCommand now accepts starting properties; they go through the schema in execute, so an unknown key or a bad value is refused before the project is touched, and one undo removes the asset. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1e2285c545
commit
9212e61c4c
9 files changed
+137
-7
No files matched your search
@@ -41,12 +41,19 @@ export class CreateAssetCommand implements Command {
|
||||
readonly asset: Asset;
|
||||
private previousActiveScene = '';
|
||||
|
||||
constructor(type: AssetType, name: string) {
|
||||
/** `props` replace schema defaults and pass through the schema like any other edit. */
|
||||
constructor(
|
||||
type: AssetType,
|
||||
name: string,
|
||||
private readonly props: PropertyBag = {},
|
||||
) {
|
||||
this.asset = buildAsset(type, name);
|
||||
this.label = `Create ${type} "${name}"`;
|
||||
}
|
||||
|
||||
execute(project: Project): void {
|
||||
for (const [key, value] of Object.entries(this.props))
|
||||
this.asset.props[key] = coerceValue(requirePropDef(this.asset, key), value);
|
||||
project.assets.push(this.asset);
|
||||
this.previousActiveScene = project.activeSceneId;
|
||||
if (this.asset.type === 'scene' && !project.activeSceneId) project.activeSceneId = this.asset.id;
|
||||
|
||||
@@ -22,6 +22,13 @@ export function texturePixelSize(project: Project, props: PropertyBag): PixelSiz
|
||||
return w > 0 && h > 0 ? { w, h } : null;
|
||||
}
|
||||
|
||||
/** A sprite wearing the texture at its own pixel size – or at the default size while that is unknown. */
|
||||
export function spriteFromTexture(project: Project, textureId: string): PropertyBag {
|
||||
const props: PropertyBag = { texture: textureId };
|
||||
const size = texturePixelSize(project, props);
|
||||
return size ? { ...props, width: size.w, height: size.h } : props;
|
||||
}
|
||||
|
||||
/**
|
||||
* What `axis` has to become for the sprite to carry the picture's proportions, leaving the
|
||||
* other side where it is. Null when the other side is not a usable number.
|
||||
|
||||
@@ -7,7 +7,9 @@ import {
|
||||
hasColorSwatch,
|
||||
isPlaceableType,
|
||||
} from '../../core/model/AssetTypes';
|
||||
import type { PropertyBag } from '../../core/model/Properties';
|
||||
import { findAsset } from '../../core/model/Project';
|
||||
import { spriteFromTexture } from '../../core/properties/aspect';
|
||||
import { assetDeviates } from '../../core/properties/deviation';
|
||||
import type { TextureStore } from '../../core/store/TextureStore';
|
||||
import { Button, ListView, Menu, type MenuItem, Panel, ScrollContainer, startDrag } from '../../ui';
|
||||
@@ -77,8 +79,8 @@ export class AssetPanel extends Panel {
|
||||
this.list.select(s?.kind === 'asset' ? s.assetId : null, true);
|
||||
}
|
||||
|
||||
private createAsset(type: AssetType): void {
|
||||
const command = new CreateAssetCommand(type, nextAssetName(this.state.project, ASSET_TYPE_LABELS[type]));
|
||||
private createAsset(type: AssetType, name = ASSET_TYPE_LABELS[type], props: PropertyBag = {}): void {
|
||||
const command = new CreateAssetCommand(type, nextAssetName(this.state.project, name), props);
|
||||
if (this.state.execute(command)) this.state.select({ kind: 'asset', assetId: command.asset.id });
|
||||
}
|
||||
|
||||
@@ -128,6 +130,12 @@ export class AssetPanel extends Panel {
|
||||
disabled: this.state.project.activeSceneId === id,
|
||||
});
|
||||
}
|
||||
if (asset.type === 'texture') {
|
||||
items.push({
|
||||
label: 'Create sprite from texture',
|
||||
onSelect: () => this.createAsset('sprite', asset.name, spriteFromTexture(this.state.project, id)),
|
||||
});
|
||||
}
|
||||
items.push({ label: 'Delete…', danger: true, onSelect: () => confirmDeleteAsset(this.state, id) });
|
||||
const menu = new Menu(items);
|
||||
menu.open(x, y);
|
||||
|
||||
Reference in new issue
Block a user