The context menu entry is gone; dragging replaces it entirely. The drop target is the view under the pointer, not the active tab, so the same gesture works for scenes and for container templates. While dragging, an outline shows the footprint the object will have plus a crosshair on its origin, turning red when the drop would be refused. Preview and command share one function (placementRejection), so the outline cannot promise something the drop then rejects. A left-press used to select the list row immediately. Since the container tab follows the selection, it switched to the very container being dragged and every drop would have nested it inside itself. The list now selects on release without a drag: picking something up is not the same as choosing it.
57 lines
2.7 KiB
TypeScript
57 lines
2.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
import { CreateAssetCommand } from '../../src/core/commands/assetCommands';
|
||
import { CreateContainerCommand } from '../../src/core/commands/containerCommands';
|
||
import type { ContainerAsset } from '../../src/core/model/Asset';
|
||
import { placementRejection } from '../../src/core/model/placement';
|
||
import { findAsset, findObjectHolder, findScene } from '../../src/core/model/Project';
|
||
import { makeSpriteScene, place } from '../helpers';
|
||
|
||
describe('placementRejection', () => {
|
||
it('lets a sprite into a scene', () => {
|
||
const f = makeSpriteScene();
|
||
const scene = findScene(f.project, f.sceneId)!;
|
||
const sprite = findAsset(f.project, f.spriteId)!;
|
||
expect(placementRejection(f.project, scene, sprite)).toBe('');
|
||
});
|
||
|
||
it('refuses a scene: a scene is not something a scene can hold', () => {
|
||
const f = makeSpriteScene();
|
||
const scene = findScene(f.project, f.sceneId)!;
|
||
expect(placementRejection(f.project, scene, scene)).toMatch(/cannot be placed/);
|
||
});
|
||
|
||
it('refuses a container that would end up inside itself', () => {
|
||
const f = makeSpriteScene();
|
||
const a = place(f, f.spriteId, { x: 0, y: 0 });
|
||
const create = new CreateContainerCommand(f.sceneId, [a]);
|
||
f.stack.execute(create);
|
||
const container = findAsset(f.project, create.assetId) as ContainerAsset;
|
||
const template = findObjectHolder(f.project, create.assetId)!;
|
||
|
||
expect(placementRejection(f.project, template, container)).toMatch(/nests forever/);
|
||
// The same container in a scene is fine – only its own template nests.
|
||
expect(placementRejection(f.project, findScene(f.project, f.sceneId)!, container)).toBe('');
|
||
});
|
||
|
||
it('refuses a container whose template already reaches the target, however deeply', () => {
|
||
const f = makeSpriteScene();
|
||
const inner = new CreateContainerCommand(f.sceneId, [place(f, f.spriteId, { x: 0, y: 0 })]);
|
||
f.stack.execute(inner);
|
||
// An outer container built from the object that holds the inner one.
|
||
const outer = new CreateContainerCommand(f.sceneId, [inner.objectId]);
|
||
f.stack.execute(outer);
|
||
const outerAsset = findAsset(f.project, outer.assetId) as ContainerAsset;
|
||
const innerTemplate = findObjectHolder(f.project, inner.assetId)!;
|
||
|
||
expect(placementRejection(f.project, innerTemplate, outerAsset)).toMatch(/nests forever/);
|
||
});
|
||
|
||
it('does not care what an asset is called: a fresh sprite is placeable', () => {
|
||
const f = makeSpriteScene();
|
||
const created = new CreateAssetCommand('text', 'Label');
|
||
f.stack.execute(created);
|
||
const scene = findScene(f.project, f.sceneId)!;
|
||
expect(placementRejection(f.project, scene, created.asset)).toBe('');
|
||
});
|
||
});
|