/** * Editor actions that several places trigger the same way (inspector, hierarchy context * menu, keyboard): they run the command and put the selection where the user expects it. */ import { DeleteAssetCommand } from '../core/commands/assetCommands'; import { AddColliderCommand } from '../core/commands/colliderCommands'; import { CreateContainerCommand } from '../core/commands/containerCommands'; import { RemoveSceneObjectsCommand } from '../core/commands/sceneObjectCommands'; import { findAsset, findPropertyUsages, findUsages } from '../core/model/Project'; import { Dialog } from '../ui'; import { type EditorState, objectSelection } from './state'; /** Groups the objects into a new container asset placed in their stead; selects the container. */ export function createContainer(state: EditorState, sceneId: string, objectIds: readonly string[]): boolean { const command = new CreateContainerCommand(sceneId, objectIds); if (!state.execute(command)) return false; state.select(objectSelection(sceneId, [command.objectId])); return true; } /** Gives the object a collider child sized like it; selects the collider. */ export function addCollider(state: EditorState, sceneId: string, objectId: string): boolean { const command = new AddColliderCommand(sceneId, objectId); if (!state.execute(command)) return false; state.select(objectSelection(sceneId, [command.objectId])); return true; } /** Removes the objects (with their subtrees) as one undo step. */ export function removeObjects(state: EditorState, sceneId: string, objectIds: readonly string[]): boolean { return state.execute(new RemoveSceneObjectsCommand(sceneId, objectIds)); } /** * Asks before deleting an asset, spelling out what goes with it. Both ways of deleting (the * asset list's context menu and the Delete key) go through here, so the warning cannot differ * depending on how the user triggered it. */ export function confirmDeleteAsset(state: EditorState, assetId: string): void { const asset = findAsset(state.project, assetId); if (!asset) return; const usages = findUsages(state.project, assetId).length; // A texture is never placed – it is worn through a property – so counting placed instances // would report "unused" for a picture a dozen sprites are showing. const references = findPropertyUsages(state.project, assetId).length; const detail = asset.type === 'scene' ? `The scene and its ${asset.objects.length} object(s) are removed.` : references > 0 ? `${references} sprite(s) reference it and fall back to their plain colour.` : usages === 0 ? 'This cannot be undone with Ctrl+Z after reload.' : asset.type === 'container' ? `${usages} placed container(s) are removed together with their content.` : `${usages} placed instance(s) are removed as well (scenes and container templates).`; new Dialog(`Delete "${asset.name}"?`, detail, [ { label: 'Cancel' }, { label: 'Delete', danger: true, onClick: () => state.execute(new DeleteAssetCommand(assetId)) }, ]).openCentered(); }