A sprite's texture and shader were only reachable by hunting for them
in the asset panel. Every place that shows such a reference now leads
straight to it.
- Object menu (hierarchy and scene view): "Open texture" and "Open
shader" sit right below "Open asset" when the sprite has one. The
object's own values count, so an overridden shader opens the
override.
- Asset context menu: the same entries at the top.
- Inspector: every asset field with a value shows its label as a blue,
underlined link. One click selects the asset, a double click opens
it. An override stays gold and gets the underline too.
- The first click rebuilds the inspector, so the second one lands on a
different row. The inspector catches that second click before any
row sees it.
- New pure rule `references.ts`: the asset behind a reference field. A
frame leads to its sheet. Kinds without a tab (palette, font, sound)
get no menu entry.
- `followsPress` in `clicks.ts` keeps the double-click window and
tolerance in one place.
- `activateAsset` in `actions.ts`: opening means the same everywhere
as a double click in the asset panel, so a scene also becomes the
active one. The asset panel uses it too.
Tests:
- ./check.sh: 150 files, 2226 tests, CHECK EXIT 0 (before 148/2214).
- 12 new unit tests: 6 for the reference rule, 4 for the link in
createFieldRow, 2 for followsPress.
- 5 new e2e checks in 28l-ii: both menus, the link's look, click
selects, double click opens.
- e2e 1-16,26,28,35,39,40 on the final build: ALL PASSED, 732 checks.
- Counter-probes: no "Open ..." entries, no underline and a second
click never recognised each fail exactly at their checks.
- Check total 1341 (CI counted 1336 at fd6af84), computed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
90 lines
2.9 KiB
TypeScript
90 lines
2.9 KiB
TypeScript
/** The label of a reference row as a link to the asset it names (#221). */
|
|
import type { FederatedPointerEvent } from 'pixi.js';
|
|
import { describe, expect, it } from 'vitest';
|
|
import { CreateAssetCommand } from '../../src/core/commands/assetCommands';
|
|
import type { PropertyValue } from '../../src/core/model/Properties';
|
|
import type { PropertyDef } from '../../src/core/properties/PropertySchema';
|
|
import {
|
|
createFieldRow,
|
|
type FieldContext,
|
|
type FieldRow,
|
|
READ_ONLY,
|
|
} from '../../src/editor/inspector/fieldFactory';
|
|
import type { Label } from '../../src/ui';
|
|
import { makeProject } from '../helpers';
|
|
|
|
const SHADER: PropertyDef = {
|
|
key: 'shader',
|
|
label: 'Shader',
|
|
kind: 'asset',
|
|
default: '',
|
|
assetTypes: ['shader'],
|
|
};
|
|
|
|
interface Built {
|
|
row: FieldRow;
|
|
label: Label;
|
|
press(button?: number): void;
|
|
followed: string[];
|
|
shaderId: string;
|
|
}
|
|
|
|
/**
|
|
* Builds the row, runs the checks and destroys it before returning: widgets repaint in a microtask,
|
|
* and text cannot be measured without a document. The value `'shader'` stands for the shader's id.
|
|
*/
|
|
function withRow(value: PropertyValue, follows: boolean, checks: (b: Built) => void): void {
|
|
const fixture = makeProject();
|
|
const shader = new CreateAssetCommand('shader', 'Glow');
|
|
fixture.stack.execute(shader);
|
|
const followed: string[] = [];
|
|
const ctx: FieldContext = {
|
|
project: () => fixture.project,
|
|
...(follows ? { followReference: (assetId: string) => void followed.push(assetId) } : {}),
|
|
};
|
|
const row = createFieldRow(ctx, SHADER, value === 'shader' ? shader.asset.id : value, READ_ONLY);
|
|
const label = row.row.children[0] as Label;
|
|
const press = (button = 0) => label.emit('pointerdown', { button } as FederatedPointerEvent);
|
|
try {
|
|
checks({ row, label, press, followed, shaderId: shader.asset.id });
|
|
} finally {
|
|
row.row.destroy({ children: true });
|
|
}
|
|
}
|
|
|
|
describe('a reference row label', () => {
|
|
it('is a link while the row names an asset, and a press follows it', () => {
|
|
withRow('shader', true, (b) => {
|
|
expect(b.label.underlined).toBe(true);
|
|
expect(b.label.eventMode).toBe('static');
|
|
b.press();
|
|
expect(b.followed).toEqual([b.shaderId]);
|
|
});
|
|
});
|
|
|
|
it('follows nothing on another button', () => {
|
|
withRow('shader', true, (b) => {
|
|
b.press(2);
|
|
expect(b.followed).toEqual([]);
|
|
});
|
|
});
|
|
|
|
it('is plain text while the row names nothing, and becomes a link once it does', () => {
|
|
withRow('', true, (b) => {
|
|
expect(b.label.underlined).toBe(false);
|
|
expect(b.label.eventMode).toBe('none');
|
|
b.row.setValue(b.shaderId);
|
|
expect(b.label.underlined).toBe(true);
|
|
b.row.setValue('gone');
|
|
expect(b.label.underlined).toBe(false);
|
|
});
|
|
});
|
|
|
|
it('stays plain text where nobody can follow it', () => {
|
|
withRow('shader', false, (b) => {
|
|
expect(b.label.underlined).toBe(false);
|
|
expect(b.label.eventMode).toBe('none');
|
|
});
|
|
});
|
|
});
|