@pixi-ui-editor/runtime-pixi 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/particles.d.ts +5 -2
- package/dist/particles.d.ts.map +1 -1
- package/dist/particles.js +57 -12
- package/dist/particles.js.map +1 -1
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +20 -2
- package/dist/scene.js.map +1 -1
- package/dist/views/NodeView.d.ts +2 -2
- package/dist/views/NodeView.d.ts.map +1 -1
- package/dist/views/NodeView.js.map +1 -1
- package/dist/views/ParticleEmitterNodeView.d.ts +13 -0
- package/dist/views/ParticleEmitterNodeView.d.ts.map +1 -1
- package/dist/views/ParticleEmitterNodeView.js +164 -32
- package/dist/views/ParticleEmitterNodeView.js.map +1 -1
- package/dist/views/StagedButtonNodeView.d.ts +7 -0
- package/dist/views/StagedButtonNodeView.d.ts.map +1 -1
- package/dist/views/StagedButtonNodeView.js +13 -2
- package/dist/views/StagedButtonNodeView.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeView.js","sourceRoot":"","sources":["../../src/views/NodeView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iCAAiC,CAAC;AAExD,OAAO,EAAE,SAAS,EAAE,SAAS,EAA2B,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAmB,MAAM,cAAc,CAAC;AASlG;;;;GAIG;AACH,MAAM,OAAgB,QAAS,SAAQ,SAAS;IACpC,OAAO,CAAa;IAC9B,4FAA4F;IACzE,QAAQ,CAA2C;IACtE,6FAA6F;IAC5E,QAAQ,GAAG,IAAI,SAAS,EAAE,CAAC;IAC5C,yGAAyG;
|
|
1
|
+
{"version":3,"file":"NodeView.js","sourceRoot":"","sources":["../../src/views/NodeView.ts"],"names":[],"mappings":"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","sourcesContent":["import { Spine } from \"@esotericsoftware/spine-pixi-v8\";\r\nimport { type LayoutProfileId, type UINode } from \"@pixi-ui-editor/schema\";\r\nimport { Container, Rectangle, Texture, type PointData } from \"pixi.js\";\r\nimport { resolveAnchoredTransform, resolveProfileTransform, type LayoutSize } from \"../layout.js\";\r\n\r\n/**\r\n * Whether a built scene is an inert authoring surface or a live one.\r\n * The editor canvas builds `authoring` scenes so controls never swallow selection and drag\r\n * gestures; Preview and the consuming app build `runtime` scenes with real pointer handling.\r\n */\r\nexport type SceneInteractionMode = \"authoring\" | \"runtime\";\r\n\r\n/**\r\n * Base display object for every schema node type. The whole layout contract — profile overrides,\r\n * anchors, pivot, position, scale, visibility — lives here once; subclasses only synchronize their\r\n * type-specific content against the resolved layout rectangle.\r\n */\r\nexport abstract class NodeView extends Container {\r\n protected content?: Container;\r\n /** A mutable scene texture map: assets may be loaded after this stable view was created. */\r\n protected readonly textures: ReadonlyMap<string, Texture> | undefined;\r\n /** Node's own grab rectangle in local space, kept equal to its resolved layout rectangle. */\r\n private readonly grabRect = new Rectangle();\r\n /** Authored/computed RectTransform-like rectangle; unlike Pixi bounds it also exists without content. */\r\n protected layoutWidth = 0;\n protected layoutHeight = 0;\n\r\n constructor(textures?: ReadonlyMap<string, Texture>) {\r\n super();\r\n this.textures = textures;\r\n }\r\n\r\n protected textureFor(assetId: string): Texture | undefined {\r\n return this.textures?.get(assetId);\r\n }\r\n\r\n /**\r\n * Makes every node selectable and draggable by its layout rectangle, whatever it renders.\r\n *\r\n * Lives here rather than in a subclass because a bare `Container` has no `containsPoint` of its\r\n * own: without this, Pixi could only find a node through whatever content the subclass happens to\r\n * draw, so nodes that draw nothing (container) or keep their content out of hit testing (button,\r\n * whose `@pixi/ui` view is inert while authoring) would silently stop being selectable.\r\n *\r\n * Deliberately `containsPoint` and NOT `hitArea`: Pixi's `hitPruneFn` drops a container together\r\n * with its whole subtree once the point falls outside its `hitArea`, which would make any child\r\n * reaching past its parent's rectangle visible but unclickable. `containsPoint` never clips\r\n * children — they are hit-tested first and independently, so a child always stays grabbable.\r\n */\r\n containsPoint(point: PointData): boolean {\r\n return this.grabRect.contains(point.x, point.y);\r\n }\r\n\r\n /** Заменяет содержимое ноды: прежнее снимается со сцены и уничтожается, чтобы не копить сироты. */\r\n protected setContent(content: Container): void {\r\n if (this.content !== undefined) { this.removeChild(this.content); this.content.destroy(); }\r\n this.content = content;\r\n this.addChild(content);\r\n }\r\n\r\n /** Syncs type-specific content to the resolved layout rectangle; layout math itself is shared. */\r\n protected abstract syncContent(node: UINode, transform: UINode[\"transform\"], profile: LayoutProfileId): void;\r\n\r\n private applyResolvedTransform(node: UINode, transform: UINode[\"transform\"], visible: boolean, managedByLayout: boolean, profile: LayoutProfileId): void {\r\n this.syncContent(node, transform, profile);\r\n\r\n const pivotX = (transform.pivotX ?? 0) * transform.width;\r\n const pivotY = (transform.pivotY ?? 0) * transform.height;\r\n this.pivot.set(pivotX, pivotY);\r\n // x/y are the pivot offset from the resolved anchor. Thus a centred anchor and pivot at\r\n // zero offsets place the visual centre exactly at the parent's centre.\r\n // A managed child is positioned by its technical Yoga item. Its own NodeView keeps only the\r\n // pivot offset inside that computed rectangle; authored x/y and anchors do not participate.\r\n this.position.set(managedByLayout ? pivotX : transform.x, managedByLayout ? pivotY : transform.y);\r\n this.rotation = transform.rotation;\r\n this.scale.set(transform.scaleX, transform.scaleY);\r\n this.visible = visible;\r\n this.layoutWidth = transform.width;\r\n this.layoutHeight = transform.height;\r\n // Тот же прямоугольник, что видит пользователь: grab-зона не зависит от содержимого ноды.\r\n this.grabRect.x = 0;\r\n this.grabRect.y = 0;\r\n this.grabRect.width = transform.width;\r\n this.grabRect.height = transform.height;\r\n }\r\n\r\n update(node: UINode, profile: LayoutProfileId, parentSize?: LayoutSize): void {\r\n const resolved = resolveProfileTransform(node, profile);\r\n this.applyResolvedTransform(node, resolveAnchoredTransform(resolved.transform, parentSize), resolved.visible, false, profile);\r\n }\r\n\r\n /** Applies a transient editor rectangle without deriving it from content-dependent Pixi bounds. */\r\n preview(node: UINode, transform: UINode[\"transform\"], visible = true, profile: LayoutProfileId = \"desktop\"): void {\r\n this.applyResolvedTransform(node, transform, visible, false, profile);\r\n }\r\n\r\n /** Applies the rectangle owned by a direct parent's layout solver. */\r\n updateManaged(node: UINode, profile: LayoutProfileId, size: LayoutSize): void {\r\n const resolved = resolveProfileTransform(node, profile);\r\n this.applyResolvedTransform(node, { ...resolved.transform, x: 0, y: 0, width: size.width, height: size.height }, resolved.visible, true, profile);\r\n }\r\n\r\n get layoutRectangle(): LayoutSize {\r\n return { width: this.layoutWidth, height: this.layoutHeight };\r\n }\r\n\r\n getSpine(): Spine | undefined {\r\n return this.content instanceof Spine ? this.content : undefined;\r\n }\r\n\r\n /**\r\n * Rasterizes canvas-based content (`TextNodeView`) at a higher pixel density than the renderer's\r\n * own backing resolution, so an authoring camera zoomed in doesn't magnify an already-rasterized\r\n * bitmap past its source resolution and read as blurred. A no-op base implementation: sprite/\r\n * graphics/bitmap-text content is either GPU-native (always crisp under zoom) or, for bitmap-text,\r\n * has its resolution fixed by the installed `BitmapFont` and can't be changed per instance.\r\n */\r\n setContentResolution(_resolution: number): void {}\r\n}\r\n"]}
|
|
@@ -5,8 +5,18 @@ import { NodeView } from "./NodeView.js";
|
|
|
5
5
|
export declare class ParticleEmitterNodeView extends NodeView {
|
|
6
6
|
private readonly contentRoot;
|
|
7
7
|
private readonly simulator;
|
|
8
|
+
private readonly renderGroups;
|
|
8
9
|
private simulationSpace;
|
|
9
10
|
private autoplayHandled;
|
|
11
|
+
private renderDirty;
|
|
12
|
+
private tintStart;
|
|
13
|
+
private tintEnd;
|
|
14
|
+
private staticColor;
|
|
15
|
+
private dynamicProperties;
|
|
16
|
+
private readonly spawnContext;
|
|
17
|
+
private renderSignature;
|
|
18
|
+
private sourceSignature;
|
|
19
|
+
private sourceTextures;
|
|
10
20
|
constructor(effect: ParticleEffectDefinition, textures?: ReadonlyMap<string, Texture>);
|
|
11
21
|
updateParticles(deltaSeconds: number): void;
|
|
12
22
|
play(): void;
|
|
@@ -22,5 +32,8 @@ export declare class ParticleEmitterNodeView extends NodeView {
|
|
|
22
32
|
destroy(options?: DestroyOptions): void;
|
|
23
33
|
protected syncContent(node: UINode): void;
|
|
24
34
|
private syncRender;
|
|
35
|
+
private cacheEffect;
|
|
36
|
+
private groupFor;
|
|
37
|
+
private destroyRenderGroups;
|
|
25
38
|
}
|
|
26
39
|
//# sourceMappingURL=ParticleEmitterNodeView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ParticleEmitterNodeView.d.ts","sourceRoot":"","sources":["../../src/views/ParticleEmitterNodeView.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,
|
|
1
|
+
{"version":3,"file":"ParticleEmitterNodeView.d.ts","sourceRoot":"","sources":["../../src/views/ParticleEmitterNodeView.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EAA0C,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAE/F,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAIzC,uFAAuF;AACvF,qBAAa,uBAAwB,SAAQ,QAAQ;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAmB;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8B;IACxD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqD;IAClF,OAAO,CAAC,eAAe,CAA8B;IACrD,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,OAAO,CAAY;IAC3B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,iBAAiB,CAA2G;IACpI,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiE;IAC9F,OAAO,CAAC,eAAe,CAAM;IAC7B,OAAO,CAAC,eAAe,CAAM;IAC7B,OAAO,CAAC,cAAc,CAAwC;gBAElD,MAAM,EAAE,wBAAwB,EAAE,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC;IAcrF,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAU3C,IAAI,IAAI,IAAI;IACZ,KAAK,IAAI,IAAI;IACb,OAAO,IAAI,IAAI;IACf,IAAI,IAAI,IAAI;IACZ,sGAAsG;IACtG,IAAI,IAAI,IAAI;IAOZ,OAAO,IAAI,IAAI;IAIf,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAC1C,cAAc;IAEd,UAAU,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI;IAKzC,OAAO,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI;IAKhD,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAazC,OAAO,CAAC,UAAU;IAyDlB,OAAO,CAAC,WAAW;IA8BnB,OAAO,CAAC,QAAQ;IAchB,OAAO,CAAC,mBAAmB;CAO5B"}
|
|
@@ -1,33 +1,45 @@
|
|
|
1
|
-
import { Container,
|
|
2
|
-
import { lerp,
|
|
1
|
+
import { Container, Particle, ParticleContainer, Texture } from "pixi.js";
|
|
2
|
+
import { lerp, ParticleSimulator } from "../particles.js";
|
|
3
3
|
import { NodeView } from "./NodeView.js";
|
|
4
4
|
const DEG_TO_RAD = Math.PI / 180;
|
|
5
5
|
/** Thin Pixi adapter: NodeView continues to own transforms, layout and hit testing. */
|
|
6
6
|
export class ParticleEmitterNodeView extends NodeView {
|
|
7
7
|
contentRoot = new Container();
|
|
8
8
|
simulator;
|
|
9
|
+
renderGroups = new Map();
|
|
9
10
|
simulationSpace = "local";
|
|
10
11
|
autoplayHandled = false;
|
|
12
|
+
renderDirty = true;
|
|
13
|
+
tintStart = 0xffffff;
|
|
14
|
+
tintEnd = 0xffffff;
|
|
15
|
+
staticColor = 0xffffffff;
|
|
16
|
+
dynamicProperties = { position: true, rotation: false, vertex: false, uvs: false, color: false };
|
|
17
|
+
spawnContext = { width: 0, height: 0, space: "local" };
|
|
18
|
+
renderSignature = "";
|
|
19
|
+
sourceSignature = "";
|
|
20
|
+
sourceTextures = [];
|
|
11
21
|
constructor(effect, textures) {
|
|
12
22
|
super(textures);
|
|
13
23
|
this.setContent(this.contentRoot);
|
|
24
|
+
this.cacheEffect(effect);
|
|
14
25
|
this.simulator = new ParticleSimulator(effect, {
|
|
15
26
|
createRender: () => {
|
|
16
|
-
|
|
17
|
-
sprite.anchor.set(0.5);
|
|
18
|
-
sprite.visible = false;
|
|
19
|
-
this.contentRoot.addChild(sprite);
|
|
20
|
-
return sprite;
|
|
27
|
+
return new Particle({ texture: Texture.EMPTY, anchorX: 0.5, anchorY: 0.5 });
|
|
21
28
|
},
|
|
22
|
-
hideRender: (
|
|
23
|
-
destroyRender: (
|
|
29
|
+
hideRender: () => undefined,
|
|
30
|
+
destroyRender: () => undefined,
|
|
24
31
|
getWorldTransform: () => this.worldTransform,
|
|
25
32
|
});
|
|
26
33
|
}
|
|
27
34
|
updateParticles(deltaSeconds) {
|
|
28
|
-
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
35
|
+
this.spawnContext.width = this.layoutWidth;
|
|
36
|
+
this.spawnContext.height = this.layoutHeight;
|
|
37
|
+
this.spawnContext.space = this.simulationSpace;
|
|
38
|
+
const advanced = this.simulator.update(deltaSeconds, this.spawnContext);
|
|
39
|
+
// World-space particles must also be reprojected when an ancestor moves between simulation
|
|
40
|
+
// steps. Local particles need no work: Pixi applies their container transform itself.
|
|
41
|
+
if (advanced || this.renderDirty || (this.simulationSpace === "world" && this.simulator.activeSlots.size > 0))
|
|
42
|
+
this.syncRender();
|
|
31
43
|
}
|
|
32
44
|
play() { this.simulator.play(); }
|
|
33
45
|
pause() { this.simulator.pause(); }
|
|
@@ -35,14 +47,22 @@ export class ParticleEmitterNodeView extends NodeView {
|
|
|
35
47
|
stop() { this.simulator.stop(); }
|
|
36
48
|
/** Advances exactly one fixed step even while paused, preserving the current playing/paused state. */
|
|
37
49
|
step() {
|
|
38
|
-
|
|
39
|
-
this.
|
|
50
|
+
this.spawnContext.width = this.layoutWidth;
|
|
51
|
+
this.spawnContext.height = this.layoutHeight;
|
|
52
|
+
this.spawnContext.space = this.simulationSpace;
|
|
53
|
+
this.simulator.step(this.spawnContext);
|
|
40
54
|
this.syncRender();
|
|
41
55
|
}
|
|
42
|
-
dispose() {
|
|
56
|
+
dispose() {
|
|
57
|
+
this.simulator.dispose();
|
|
58
|
+
this.destroyRenderGroups();
|
|
59
|
+
}
|
|
43
60
|
setEmissionMultiplier(value) { this.simulator.setEmissionMultiplier(value); }
|
|
44
61
|
getDiagnostics() { return this.simulator.getDiagnostics(); }
|
|
45
|
-
syncEffect(effect) {
|
|
62
|
+
syncEffect(effect) {
|
|
63
|
+
this.simulator.syncEffect(effect);
|
|
64
|
+
this.renderDirty ||= this.cacheEffect(effect);
|
|
65
|
+
}
|
|
46
66
|
destroy(options) {
|
|
47
67
|
this.dispose();
|
|
48
68
|
super.destroy(options);
|
|
@@ -50,6 +70,8 @@ export class ParticleEmitterNodeView extends NodeView {
|
|
|
50
70
|
syncContent(node) {
|
|
51
71
|
if (node.type !== "particle-emitter")
|
|
52
72
|
return;
|
|
73
|
+
if (this.simulationSpace !== node.simulationSpace)
|
|
74
|
+
this.renderDirty = true;
|
|
53
75
|
this.simulationSpace = node.simulationSpace;
|
|
54
76
|
// Autoplay only fires once, right after construction: syncContent also runs on every unrelated
|
|
55
77
|
// document edit (via the shared node-sync pass), and re-arming play() there would silently
|
|
@@ -65,32 +87,115 @@ export class ParticleEmitterNodeView extends NodeView {
|
|
|
65
87
|
const source = p.visual.source;
|
|
66
88
|
const space = this.simulationSpace;
|
|
67
89
|
const inverse = space === "world" ? this.worldTransform : undefined;
|
|
90
|
+
const determinant = inverse === undefined ? 0 : inverse.a * inverse.d - inverse.b * inverse.c;
|
|
91
|
+
const inverseDeterminant = determinant === 0 ? 0 : 1 / determinant;
|
|
92
|
+
for (const group of this.renderGroups.values()) {
|
|
93
|
+
group.nextCount = 0;
|
|
94
|
+
group.dirty ||= this.renderDirty;
|
|
95
|
+
}
|
|
68
96
|
for (const slot of this.simulator.activeSlots) {
|
|
69
|
-
const
|
|
97
|
+
const particle = slot.render;
|
|
70
98
|
const t = slot.lifetime > 0 ? Math.min(1, slot.age / slot.lifetime) : 1;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
99
|
+
const sourceIndex = source.type === "single" ? 0 : source.type === "random" ? slot.sourceIndex : sequenceFrame(source, slot);
|
|
100
|
+
const texture = this.sourceTextures[sourceIndex] ?? Texture.EMPTY;
|
|
101
|
+
const textureChanged = particle.texture !== texture;
|
|
102
|
+
if (textureChanged)
|
|
103
|
+
particle.texture = texture;
|
|
104
|
+
const initializeStatic = slot.renderDirty || this.renderDirty;
|
|
105
|
+
if (this.dynamicProperties.vertex || initializeStatic) {
|
|
106
|
+
const scale = lerp(slot.scaleStart, slot.scaleEnd, t);
|
|
107
|
+
particle.scaleX = scale;
|
|
108
|
+
particle.scaleY = scale;
|
|
109
|
+
}
|
|
110
|
+
if (this.dynamicProperties.rotation || initializeStatic)
|
|
111
|
+
particle.rotation = slot.rotationDegrees * DEG_TO_RAD;
|
|
112
|
+
if (this.dynamicProperties.color)
|
|
113
|
+
particle.color = packParticleColor(lerpRgb(this.tintStart, this.tintEnd, t), lerp(p.visual.alpha.start, p.visual.alpha.end, t));
|
|
114
|
+
else if (initializeStatic)
|
|
115
|
+
particle.color = this.staticColor;
|
|
82
116
|
if (space === "world" && inverse !== undefined) {
|
|
83
117
|
const dx = slot.x - slot.spawnX, dy = slot.y - slot.spawnY;
|
|
84
118
|
const worldX = slot.worldSpawnX + slot.spawnMatrixA * dx + slot.spawnMatrixC * dy;
|
|
85
119
|
const worldY = slot.worldSpawnY + slot.spawnMatrixB * dx + slot.spawnMatrixD * dy;
|
|
86
|
-
|
|
87
|
-
|
|
120
|
+
// Inline Matrix.applyInverse to avoid allocating one point per particle per rendered frame.
|
|
121
|
+
const translatedX = worldX - inverse.tx;
|
|
122
|
+
const translatedY = worldY - inverse.ty;
|
|
123
|
+
particle.x = (inverse.d * translatedX - inverse.c * translatedY) * inverseDeterminant;
|
|
124
|
+
particle.y = (-inverse.b * translatedX + inverse.a * translatedY) * inverseDeterminant;
|
|
88
125
|
}
|
|
89
126
|
else {
|
|
90
|
-
|
|
127
|
+
particle.x = slot.x;
|
|
128
|
+
particle.y = slot.y;
|
|
91
129
|
}
|
|
92
|
-
|
|
130
|
+
const group = this.groupFor(texture, p.visual.blendMode);
|
|
131
|
+
group.dirty ||= slot.renderDirty || textureChanged || group.container.particleChildren[group.nextCount] !== particle;
|
|
132
|
+
group.container.particleChildren[group.nextCount++] = particle;
|
|
133
|
+
slot.renderDirty = false;
|
|
134
|
+
}
|
|
135
|
+
for (const group of this.renderGroups.values()) {
|
|
136
|
+
const children = group.container.particleChildren;
|
|
137
|
+
if (children.length !== group.nextCount) {
|
|
138
|
+
children.length = group.nextCount;
|
|
139
|
+
group.dirty = true;
|
|
140
|
+
}
|
|
141
|
+
if (group.dirty)
|
|
142
|
+
group.container.update();
|
|
143
|
+
group.dirty = false;
|
|
144
|
+
}
|
|
145
|
+
this.renderDirty = false;
|
|
146
|
+
}
|
|
147
|
+
cacheEffect(effect) {
|
|
148
|
+
const visual = effect.particle.visual;
|
|
149
|
+
const sourceIds = visual.source.type === "single" ? [visual.source.assetId] : visual.source.assetIds;
|
|
150
|
+
const sourceTextures = sourceIds.map((id) => this.textureFor(id));
|
|
151
|
+
const signature = JSON.stringify(visual);
|
|
152
|
+
const sourceSignature = JSON.stringify(visual.source);
|
|
153
|
+
const sourceChanged = sourceSignature !== this.sourceSignature
|
|
154
|
+
|| sourceTextures.length !== this.sourceTextures.length
|
|
155
|
+
|| sourceTextures.some((texture, index) => texture !== this.sourceTextures[index]);
|
|
156
|
+
const changed = signature !== this.renderSignature
|
|
157
|
+
|| sourceChanged;
|
|
158
|
+
if (!changed)
|
|
159
|
+
return false;
|
|
160
|
+
this.renderSignature = signature;
|
|
161
|
+
this.sourceSignature = sourceSignature;
|
|
162
|
+
this.sourceTextures = sourceTextures;
|
|
163
|
+
// Source edits and late texture loads invalidate the source-keyed groups. Rebuild containers,
|
|
164
|
+
// but keep every pooled Particle and live simulation slot intact.
|
|
165
|
+
if (sourceChanged)
|
|
166
|
+
this.destroyRenderGroups();
|
|
167
|
+
const dynamicProperties = particleDynamicProperties(effect);
|
|
168
|
+
if (!sameDynamicProperties(this.dynamicProperties, dynamicProperties)) {
|
|
169
|
+
this.destroyRenderGroups();
|
|
170
|
+
this.dynamicProperties = dynamicProperties;
|
|
93
171
|
}
|
|
172
|
+
this.tintStart = parseRgb(effect.particle.visual.tint.start);
|
|
173
|
+
this.tintEnd = parseRgb(effect.particle.visual.tint.end);
|
|
174
|
+
this.staticColor = packParticleColor(this.tintStart, effect.particle.visual.alpha.start);
|
|
175
|
+
for (const group of this.renderGroups.values())
|
|
176
|
+
group.container.blendMode = effect.particle.visual.blendMode;
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
groupFor(texture, blendMode) {
|
|
180
|
+
let group = this.renderGroups.get(texture.source);
|
|
181
|
+
if (group !== undefined)
|
|
182
|
+
return group;
|
|
183
|
+
const container = new ParticleContainer({
|
|
184
|
+
texture,
|
|
185
|
+
dynamicProperties: this.dynamicProperties,
|
|
186
|
+
});
|
|
187
|
+
container.blendMode = blendMode;
|
|
188
|
+
this.contentRoot.addChild(container);
|
|
189
|
+
group = { container, nextCount: 0, dirty: true };
|
|
190
|
+
this.renderGroups.set(texture.source, group);
|
|
191
|
+
return group;
|
|
192
|
+
}
|
|
193
|
+
destroyRenderGroups() {
|
|
194
|
+
for (const group of this.renderGroups.values()) {
|
|
195
|
+
this.contentRoot.removeChild(group.container);
|
|
196
|
+
group.container.destroy();
|
|
197
|
+
}
|
|
198
|
+
this.renderGroups.clear();
|
|
94
199
|
}
|
|
95
200
|
}
|
|
96
201
|
function sequenceFrame(source, slot) {
|
|
@@ -98,4 +203,31 @@ function sequenceFrame(source, slot) {
|
|
|
98
203
|
const raw = slot.sequenceStart + Math.floor(slot.age * source.fps);
|
|
99
204
|
return source.loop ? ((raw % length) + length) % length : Math.min(raw, length - 1);
|
|
100
205
|
}
|
|
206
|
+
function parseRgb(hex) { return Number.parseInt(hex.slice(1, 7), 16); }
|
|
207
|
+
function lerpRgb(start, end, t) {
|
|
208
|
+
const r = Math.round(lerp((start >>> 16) & 0xff, (end >>> 16) & 0xff, t));
|
|
209
|
+
const g = Math.round(lerp((start >>> 8) & 0xff, (end >>> 8) & 0xff, t));
|
|
210
|
+
const b = Math.round(lerp(start & 0xff, end & 0xff, t));
|
|
211
|
+
return (r << 16) | (g << 8) | b;
|
|
212
|
+
}
|
|
213
|
+
/** Pixi Particle buffers store packed ABGR, unlike the public RGB tint number. */
|
|
214
|
+
function packParticleColor(rgb, alpha) {
|
|
215
|
+
const bgr = ((rgb & 0xff) << 16) | (rgb & 0xff00) | ((rgb >>> 16) & 0xff);
|
|
216
|
+
return (bgr | (Math.round(Math.max(0, Math.min(1, alpha)) * 255) << 24)) >>> 0;
|
|
217
|
+
}
|
|
218
|
+
function particleDynamicProperties(effect) {
|
|
219
|
+
const visual = effect.particle.visual;
|
|
220
|
+
const angular = visual.rotation.angularVelocityDegrees;
|
|
221
|
+
const scale = visual.scale;
|
|
222
|
+
return {
|
|
223
|
+
position: true,
|
|
224
|
+
rotation: angular.min !== 0 || angular.max !== 0,
|
|
225
|
+
vertex: !(scale.start.min === scale.start.max && scale.start.min === scale.end.min && scale.start.min === scale.end.max),
|
|
226
|
+
uvs: visual.source.type === "sequence",
|
|
227
|
+
color: visual.tint.start !== visual.tint.end || visual.alpha.start !== visual.alpha.end,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function sameDynamicProperties(a, b) {
|
|
231
|
+
return a.rotation === b.rotation && a.vertex === b.vertex && a.uvs === b.uvs && a.color === b.color;
|
|
232
|
+
}
|
|
101
233
|
//# sourceMappingURL=ParticleEmitterNodeView.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ParticleEmitterNodeView.js","sourceRoot":"","sources":["../../src/views/ParticleEmitterNodeView.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ParticleEffectDefinition, UINode } from \"@pixi-ui-editor/schema\";\r\nimport { Container, Sprite, Texture, type DestroyOptions } from \"pixi.js\";\r\nimport { lerp, lerpColorHex, ParticleSimulator, type ParticleSlot } from \"../particles.js\";\r\nimport { NodeView } from \"./NodeView.js\";\r\n\r\nconst DEG_TO_RAD = Math.PI / 180;\r\n\r\n/** Thin Pixi adapter: NodeView continues to own transforms, layout and hit testing. */\r\nexport class ParticleEmitterNodeView extends NodeView {\r\n private readonly contentRoot = new Container();\r\n private readonly simulator: ParticleSimulator<Sprite>;\r\n private simulationSpace: \"local\" | \"world\" = \"local\";\r\n private autoplayHandled = false;\r\n\r\n constructor(effect: ParticleEffectDefinition, textures?: ReadonlyMap<string, Texture>) {\r\n super(textures);\r\n this.setContent(this.contentRoot);\r\n this.simulator = new ParticleSimulator<Sprite>(effect, {\r\n createRender: () => {\r\n const sprite = new Sprite(Texture.EMPTY);\r\n sprite.anchor.set(0.5);\r\n sprite.visible = false;\r\n this.contentRoot.addChild(sprite);\r\n return sprite;\r\n },\r\n hideRender: (sprite) => { sprite.visible = false; },\r\n destroyRender: (sprite) => { this.contentRoot.removeChild(sprite); sprite.destroy(); },\r\n getWorldTransform: () => this.worldTransform,\r\n });\r\n }\r\n\r\n updateParticles(deltaSeconds: number): void {\r\n const rect = this.layoutRectangle;\r\n this.simulator.update(deltaSeconds, { width: rect.width, height: rect.height, space: this.simulationSpace });\r\n this.syncRender();\r\n }\r\n\r\n play(): void { this.simulator.play(); }\r\n pause(): void { this.simulator.pause(); }\r\n restart(): void { this.simulator.restart(); }\r\n stop(): void { this.simulator.stop(); }\r\n /** Advances exactly one fixed step even while paused, preserving the current playing/paused state. */\r\n step(): void {\r\n const rect = this.layoutRectangle;\r\n this.simulator.step({ width: rect.width, height: rect.height, space: this.simulationSpace });\r\n this.syncRender();\r\n }\r\n dispose(): void { this.simulator.dispose(); }\r\n setEmissionMultiplier(value: number): void { this.simulator.setEmissionMultiplier(value); }\r\n getDiagnostics() { return this.simulator.getDiagnostics(); }\r\n\r\n syncEffect(effect: ParticleEffectDefinition): void { this.simulator.syncEffect(effect); }\r\n\r\n override destroy(options?: DestroyOptions): void {\r\n this.dispose();\r\n super.destroy(options);\r\n }\r\n\r\n protected syncContent(node: UINode): void {\r\n if (node.type !== \"particle-emitter\") return;\r\n this.simulationSpace = node.simulationSpace;\r\n // Autoplay only fires once, right after construction: syncContent also runs on every unrelated\r\n // document edit (via the shared node-sync pass), and re-arming play() there would silently\r\n // override an explicit pause()/stop() the moment the author touches anything else in the project.\r\n if (!this.autoplayHandled) {\r\n this.autoplayHandled = true;\r\n if (node.autoplay) this.simulator.play();\r\n }\r\n }\r\n\r\n private syncRender(): void {\r\n const p = this.simulator.definition.particle;\r\n const source = p.visual.source;\r\n const space = this.simulationSpace;\r\n const inverse = space === \"world\" ? this.worldTransform : undefined;\r\n for (const slot of this.simulator.activeSlots) {\r\n const sprite = slot.render;\r\n const t = slot.lifetime > 0 ? Math.min(1, slot.age / slot.lifetime) : 1;\r\n sprite.alpha = lerp(p.visual.alpha.start, p.visual.alpha.end, t);\r\n sprite.tint = lerpColorHex(p.visual.tint.start, p.visual.tint.end, t);\r\n sprite.scale.set(lerp(slot.scaleStart, slot.scaleEnd, t));\r\n sprite.rotation = slot.rotationDegrees * DEG_TO_RAD;\r\n sprite.blendMode = p.visual.blendMode;\r\n const assetId = source.type === \"single\" ? source.assetId\r\n : source.type === \"random\" ? source.assetIds[slot.sourceIndex]!\r\n : source.assetIds[sequenceFrame(source, slot)]!;\r\n const texture = this.textureFor(assetId);\r\n if (texture) sprite.texture = texture;\r\n if (space === \"world\" && inverse !== undefined) {\r\n const dx = slot.x - slot.spawnX, dy = slot.y - slot.spawnY;\r\n const worldX = slot.worldSpawnX + slot.spawnMatrixA * dx + slot.spawnMatrixC * dy;\r\n const worldY = slot.worldSpawnY + slot.spawnMatrixB * dx + slot.spawnMatrixD * dy;\r\n const local = inverse.applyInverse({ x: worldX, y: worldY });\r\n sprite.position.set(local.x, local.y);\r\n } else {\r\n sprite.position.set(slot.x, slot.y);\r\n }\r\n sprite.visible = true;\r\n }\r\n }\r\n}\r\n\r\ntype SequenceSource = Extract<ParticleEffectDefinition[\"particle\"][\"visual\"][\"source\"], { type: \"sequence\" }>;\r\nfunction sequenceFrame(source: SequenceSource, slot: ParticleSlot<Sprite>): number {\r\n const length = source.assetIds.length;\r\n const raw = slot.sequenceStart + Math.floor(slot.age * source.fps);\r\n return source.loop ? ((raw % length) + length) % length : Math.min(raw, length - 1);\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"ParticleEmitterNodeView.js","sourceRoot":"","sources":["../../src/views/ParticleEmitterNodeView.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ParticleEffectDefinition, UINode } from \"@pixi-ui-editor/schema\";\nimport { Container, Particle, ParticleContainer, Texture, type DestroyOptions } from \"pixi.js\";\nimport { lerp, ParticleSimulator, type ParticleSlot, type ParticleSpawnContext } from \"../particles.js\";\nimport { NodeView } from \"./NodeView.js\";\n\r\nconst DEG_TO_RAD = Math.PI / 180;\r\n\r\n/** Thin Pixi adapter: NodeView continues to own transforms, layout and hit testing. */\r\nexport class ParticleEmitterNodeView extends NodeView {\n private readonly contentRoot = new Container();\n private readonly simulator: ParticleSimulator<Particle>;\n private readonly renderGroups = new Map<Texture[\"source\"], ParticleRenderGroup>();\n private simulationSpace: \"local\" | \"world\" = \"local\";\n private autoplayHandled = false;\n private renderDirty = true;\n private tintStart = 0xffffff;\n private tintEnd = 0xffffff;\n private staticColor = 0xffffffff;\n private dynamicProperties: DynamicParticleProperties = { position: true, rotation: false, vertex: false, uvs: false, color: false };\n private readonly spawnContext: ParticleSpawnContext = { width: 0, height: 0, space: \"local\" };\n private renderSignature = \"\";\n private sourceSignature = \"\";\n private sourceTextures: readonly (Texture | undefined)[] = [];\n\r\n constructor(effect: ParticleEffectDefinition, textures?: ReadonlyMap<string, Texture>) {\r\n super(textures);\n this.setContent(this.contentRoot);\n this.cacheEffect(effect);\n this.simulator = new ParticleSimulator<Particle>(effect, {\n createRender: () => {\n return new Particle({ texture: Texture.EMPTY, anchorX: 0.5, anchorY: 0.5 });\n },\n hideRender: () => undefined,\n destroyRender: () => undefined,\n getWorldTransform: () => this.worldTransform,\n });\n }\n\n updateParticles(deltaSeconds: number): void {\n this.spawnContext.width = this.layoutWidth;\n this.spawnContext.height = this.layoutHeight;\n this.spawnContext.space = this.simulationSpace;\n const advanced = this.simulator.update(deltaSeconds, this.spawnContext);\n // World-space particles must also be reprojected when an ancestor moves between simulation\n // steps. Local particles need no work: Pixi applies their container transform itself.\n if (advanced || this.renderDirty || (this.simulationSpace === \"world\" && this.simulator.activeSlots.size > 0)) this.syncRender();\n }\r\n\r\n play(): void { this.simulator.play(); }\r\n pause(): void { this.simulator.pause(); }\r\n restart(): void { this.simulator.restart(); }\r\n stop(): void { this.simulator.stop(); }\r\n /** Advances exactly one fixed step even while paused, preserving the current playing/paused state. */\r\n step(): void {\n this.spawnContext.width = this.layoutWidth;\n this.spawnContext.height = this.layoutHeight;\n this.spawnContext.space = this.simulationSpace;\n this.simulator.step(this.spawnContext);\n this.syncRender();\r\n }\r\n dispose(): void {\n this.simulator.dispose();\n this.destroyRenderGroups();\n }\n setEmissionMultiplier(value: number): void { this.simulator.setEmissionMultiplier(value); }\r\n getDiagnostics() { return this.simulator.getDiagnostics(); }\r\n\r\n syncEffect(effect: ParticleEffectDefinition): void {\n this.simulator.syncEffect(effect);\n this.renderDirty ||= this.cacheEffect(effect);\n }\n\r\n override destroy(options?: DestroyOptions): void {\r\n this.dispose();\r\n super.destroy(options);\r\n }\r\n\r\n protected syncContent(node: UINode): void {\n if (node.type !== \"particle-emitter\") return;\n if (this.simulationSpace !== node.simulationSpace) this.renderDirty = true;\n this.simulationSpace = node.simulationSpace;\n // Autoplay only fires once, right after construction: syncContent also runs on every unrelated\r\n // document edit (via the shared node-sync pass), and re-arming play() there would silently\r\n // override an explicit pause()/stop() the moment the author touches anything else in the project.\r\n if (!this.autoplayHandled) {\r\n this.autoplayHandled = true;\r\n if (node.autoplay) this.simulator.play();\r\n }\r\n }\r\n\r\n private syncRender(): void {\n const p = this.simulator.definition.particle;\r\n const source = p.visual.source;\r\n const space = this.simulationSpace;\r\n const inverse = space === \"world\" ? this.worldTransform : undefined;\n const determinant = inverse === undefined ? 0 : inverse.a * inverse.d - inverse.b * inverse.c;\n const inverseDeterminant = determinant === 0 ? 0 : 1 / determinant;\n for (const group of this.renderGroups.values()) {\n group.nextCount = 0;\n group.dirty ||= this.renderDirty;\n }\n for (const slot of this.simulator.activeSlots) {\n const particle = slot.render;\n const t = slot.lifetime > 0 ? Math.min(1, slot.age / slot.lifetime) : 1;\n const sourceIndex = source.type === \"single\" ? 0 : source.type === \"random\" ? slot.sourceIndex : sequenceFrame(source, slot);\n const texture = this.sourceTextures[sourceIndex] ?? Texture.EMPTY;\n const textureChanged = particle.texture !== texture;\n if (textureChanged) particle.texture = texture;\n const initializeStatic = slot.renderDirty || this.renderDirty;\n if (this.dynamicProperties.vertex || initializeStatic) {\n const scale = lerp(slot.scaleStart, slot.scaleEnd, t);\n particle.scaleX = scale;\n particle.scaleY = scale;\n }\n if (this.dynamicProperties.rotation || initializeStatic) particle.rotation = slot.rotationDegrees * DEG_TO_RAD;\n if (this.dynamicProperties.color) particle.color = packParticleColor(lerpRgb(this.tintStart, this.tintEnd, t), lerp(p.visual.alpha.start, p.visual.alpha.end, t));\n else if (initializeStatic) particle.color = this.staticColor;\n if (space === \"world\" && inverse !== undefined) {\n const dx = slot.x - slot.spawnX, dy = slot.y - slot.spawnY;\n const worldX = slot.worldSpawnX + slot.spawnMatrixA * dx + slot.spawnMatrixC * dy;\n const worldY = slot.worldSpawnY + slot.spawnMatrixB * dx + slot.spawnMatrixD * dy;\n // Inline Matrix.applyInverse to avoid allocating one point per particle per rendered frame.\n const translatedX = worldX - inverse.tx;\n const translatedY = worldY - inverse.ty;\n particle.x = (inverse.d * translatedX - inverse.c * translatedY) * inverseDeterminant;\n particle.y = (-inverse.b * translatedX + inverse.a * translatedY) * inverseDeterminant;\n } else {\n particle.x = slot.x;\n particle.y = slot.y;\n }\n const group = this.groupFor(texture, p.visual.blendMode);\n group.dirty ||= slot.renderDirty || textureChanged || group.container.particleChildren[group.nextCount] !== particle;\n group.container.particleChildren[group.nextCount++] = particle;\n slot.renderDirty = false;\n }\n for (const group of this.renderGroups.values()) {\n const children = group.container.particleChildren;\n if (children.length !== group.nextCount) {\n children.length = group.nextCount;\n group.dirty = true;\n }\n if (group.dirty) group.container.update();\n group.dirty = false;\n }\n this.renderDirty = false;\n }\n\n private cacheEffect(effect: ParticleEffectDefinition): boolean {\n const visual = effect.particle.visual;\n const sourceIds = visual.source.type === \"single\" ? [visual.source.assetId] : visual.source.assetIds;\n const sourceTextures = sourceIds.map((id) => this.textureFor(id));\n const signature = JSON.stringify(visual);\n const sourceSignature = JSON.stringify(visual.source);\n const sourceChanged = sourceSignature !== this.sourceSignature\n || sourceTextures.length !== this.sourceTextures.length\n || sourceTextures.some((texture, index) => texture !== this.sourceTextures[index]);\n const changed = signature !== this.renderSignature\n || sourceChanged;\n if (!changed) return false;\n this.renderSignature = signature;\n this.sourceSignature = sourceSignature;\n this.sourceTextures = sourceTextures;\n // Source edits and late texture loads invalidate the source-keyed groups. Rebuild containers,\n // but keep every pooled Particle and live simulation slot intact.\n if (sourceChanged) this.destroyRenderGroups();\n const dynamicProperties = particleDynamicProperties(effect);\n if (!sameDynamicProperties(this.dynamicProperties, dynamicProperties)) {\n this.destroyRenderGroups();\n this.dynamicProperties = dynamicProperties;\n }\n this.tintStart = parseRgb(effect.particle.visual.tint.start);\n this.tintEnd = parseRgb(effect.particle.visual.tint.end);\n this.staticColor = packParticleColor(this.tintStart, effect.particle.visual.alpha.start);\n for (const group of this.renderGroups.values()) group.container.blendMode = effect.particle.visual.blendMode;\n return true;\n }\n\n private groupFor(texture: Texture, blendMode: ParticleEffectDefinition[\"particle\"][\"visual\"][\"blendMode\"]): ParticleRenderGroup {\n let group = this.renderGroups.get(texture.source);\n if (group !== undefined) return group;\n const container = new ParticleContainer<Particle>({\n texture,\n dynamicProperties: this.dynamicProperties,\n });\n container.blendMode = blendMode;\n this.contentRoot.addChild(container);\n group = { container, nextCount: 0, dirty: true };\n this.renderGroups.set(texture.source, group);\n return group;\n }\n\n private destroyRenderGroups(): void {\n for (const group of this.renderGroups.values()) {\n this.contentRoot.removeChild(group.container);\n group.container.destroy();\n }\n this.renderGroups.clear();\n }\n}\n\ntype SequenceSource = Extract<ParticleEffectDefinition[\"particle\"][\"visual\"][\"source\"], { type: \"sequence\" }>;\ntype ParticleRenderGroup = { container: ParticleContainer<Particle>; nextCount: number; dirty: boolean };\ntype DynamicParticleProperties = { position: true; rotation: boolean; vertex: boolean; uvs: boolean; color: boolean };\n\nfunction sequenceFrame(source: SequenceSource, slot: ParticleSlot<Particle>): number {\n const length = source.assetIds.length;\r\n const raw = slot.sequenceStart + Math.floor(slot.age * source.fps);\r\n return source.loop ? ((raw % length) + length) % length : Math.min(raw, length - 1);\r\n}\n\nfunction parseRgb(hex: string): number { return Number.parseInt(hex.slice(1, 7), 16); }\nfunction lerpRgb(start: number, end: number, t: number): number {\n const r = Math.round(lerp((start >>> 16) & 0xff, (end >>> 16) & 0xff, t));\n const g = Math.round(lerp((start >>> 8) & 0xff, (end >>> 8) & 0xff, t));\n const b = Math.round(lerp(start & 0xff, end & 0xff, t));\n return (r << 16) | (g << 8) | b;\n}\n/** Pixi Particle buffers store packed ABGR, unlike the public RGB tint number. */\nfunction packParticleColor(rgb: number, alpha: number): number {\n const bgr = ((rgb & 0xff) << 16) | (rgb & 0xff00) | ((rgb >>> 16) & 0xff);\n return (bgr | (Math.round(Math.max(0, Math.min(1, alpha)) * 255) << 24)) >>> 0;\n}\n\nfunction particleDynamicProperties(effect: ParticleEffectDefinition): DynamicParticleProperties {\n const visual = effect.particle.visual;\n const angular = visual.rotation.angularVelocityDegrees;\n const scale = visual.scale;\n return {\n position: true,\n rotation: angular.min !== 0 || angular.max !== 0,\n vertex: !(scale.start.min === scale.start.max && scale.start.min === scale.end.min && scale.start.min === scale.end.max),\n uvs: visual.source.type === \"sequence\",\n color: visual.tint.start !== visual.tint.end || visual.alpha.start !== visual.alpha.end,\n };\n}\n\nfunction sameDynamicProperties(a: DynamicParticleProperties, b: DynamicParticleProperties): boolean {\n return a.rotation === b.rotation && a.vertex === b.vertex && a.uvs === b.uvs && a.color === b.color;\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ButtonStateKey, type LayoutPadding, type StagedButtonNode, type UINode } from "@pixi-ui-editor/schema";
|
|
2
|
+
import { FancyButton } from "@pixi/ui";
|
|
2
3
|
import type { Sound } from "@pixi/sound";
|
|
3
4
|
import { Texture } from "pixi.js";
|
|
4
5
|
import { Signal } from "typed-signals";
|
|
@@ -29,6 +30,12 @@ export declare class StagedButtonNodeView extends NodeView {
|
|
|
29
30
|
private enabledState;
|
|
30
31
|
/** Последнее editor-only визуальное состояние из `setState`: переживает пересборку `FancyButton`. */
|
|
31
32
|
private forcedState;
|
|
33
|
+
/** Stable public signals: subscriptions survive an internal `FancyButton` rebuild. */
|
|
34
|
+
readonly onPress: FancyButton["onPress"];
|
|
35
|
+
readonly onDown: FancyButton["onDown"];
|
|
36
|
+
readonly onUp: FancyButton["onUp"];
|
|
37
|
+
readonly onHover: FancyButton["onHover"];
|
|
38
|
+
readonly onOut: FancyButton["onOut"];
|
|
32
39
|
readonly onStageChange: Signal<(stage: {
|
|
33
40
|
index: number;
|
|
34
41
|
value: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StagedButtonNodeView.d.ts","sourceRoot":"","sources":["../../src/views/StagedButtonNodeView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,KAAK,gBAAgB,EAAE,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"StagedButtonNodeView.d.ts","sourceRoot":"","sources":["../../src/views/StagedButtonNodeView.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,6 +29,12 @@ export class StagedButtonNodeView extends NodeView {
|
|
|
29
29
|
enabledState;
|
|
30
30
|
/** Последнее editor-only визуальное состояние из `setState`: переживает пересборку `FancyButton`. */
|
|
31
31
|
forcedState;
|
|
32
|
+
/** Stable public signals: subscriptions survive an internal `FancyButton` rebuild. */
|
|
33
|
+
onPress = new Signal();
|
|
34
|
+
onDown = new Signal();
|
|
35
|
+
onUp = new Signal();
|
|
36
|
+
onHover = new Signal();
|
|
37
|
+
onOut = new Signal();
|
|
32
38
|
onStageChange = new Signal();
|
|
33
39
|
constructor(node, textures, interaction, sounds) {
|
|
34
40
|
super(textures);
|
|
@@ -79,17 +85,22 @@ export class StagedButtonNodeView extends NodeView {
|
|
|
79
85
|
if (this.interaction === "authoring")
|
|
80
86
|
button.eventMode = "none";
|
|
81
87
|
else {
|
|
82
|
-
button.onPress.connect(() => {
|
|
88
|
+
button.onPress.connect((source, event) => {
|
|
83
89
|
this.next();
|
|
84
90
|
const result = this.soundAssetIds?.pressAssetId === undefined ? undefined : this.sounds?.get(this.soundAssetIds.pressAssetId)?.play();
|
|
85
91
|
if (result instanceof Promise)
|
|
86
92
|
void result.catch(() => undefined);
|
|
93
|
+
this.onPress.emit(source, event);
|
|
87
94
|
});
|
|
88
|
-
button.
|
|
95
|
+
button.onDown.connect((source, event) => this.onDown.emit(source, event));
|
|
96
|
+
button.onUp.connect((source, event) => this.onUp.emit(source, event));
|
|
97
|
+
button.onHover.connect((source, event) => {
|
|
89
98
|
const result = this.soundAssetIds?.hoverAssetId === undefined ? undefined : this.sounds?.get(this.soundAssetIds.hoverAssetId)?.play();
|
|
90
99
|
if (result instanceof Promise)
|
|
91
100
|
void result.catch(() => undefined);
|
|
101
|
+
this.onHover.emit(source, event);
|
|
92
102
|
});
|
|
103
|
+
button.onOut.connect((source, event) => this.onOut.emit(source, event));
|
|
93
104
|
}
|
|
94
105
|
this.button = button;
|
|
95
106
|
this.stateViews = stateViews;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StagedButtonNodeView.js","sourceRoot":"","sources":["../../src/views/StagedButtonNodeView.ts"],"names":[],"mappings":"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","sourcesContent":["import { BUTTON_STATE_KEYS, type ButtonStateKey, type LayoutPadding, type StagedButtonNode, type UINode } from \"@pixi-ui-editor/schema\";\nimport { FancyButton } from \"@pixi/ui\";\nimport type { Sound } from \"@pixi/sound\";\nimport { Container, Graphics, NineSliceSprite, Sprite, Texture } from \"pixi.js\";\nimport { Signal } from \"typed-signals\";\nimport { NodeView, type SceneInteractionMode } from \"./NodeView.js\";\n\nexport type { StagedButtonNode };\ntype StagedButtonStage = StagedButtonNode[\"stages\"][number];\n\nconst FANCY_STATE_BY_KEY = { normal: \"default\", hover: \"hover\", pressed: \"pressed\", disabled: \"disabled\" } as const;\n\n/**\n * Adapts a staged-button node to `@pixi/ui`'s `FancyButton`, exactly like `ButtonNodeView` does for a\n * plain button — this class never reimplements its pointer state machine either. It is a deliberately\n * separate component (see TASK-057): it does not extend or share behavior with `ButtonNodeView`.\n *\n * A click at runtime advances the current stage (`next()`), which respects `wrap`. Four state views\n * are created once and reused across stages: changing stage only reassigns their textures/size, never\n * replacing the view type — a full `FancyButton` rebuild only happens when a view's required *type*\n * changes (nine-slice toggled, or a texture just finished loading), because `FancyButton` gives no way\n * to swap a view after construction.\n */\nexport class StagedButtonNodeView extends NodeView {\n private button!: FancyButton;\n private stateViews!: Record<ButtonStateKey, Container>;\n private readonly interaction: SceneInteractionMode;\n private readonly sounds: ReadonlyMap<string, Sound> | undefined;\n private soundAssetIds: StagedButtonNode[\"sounds\"];\n private nineSlice: LayoutPadding | undefined;\n private stagesData: StagedButtonStage[];\n private currentStageId: string;\n private defaultStageId: string;\n private wrapState: boolean;\n private enabledState: boolean;\n /** Последнее editor-only визуальное состояние из `setState`: переживает пересборку `FancyButton`. */\n private forcedState: ButtonStateKey | undefined;\n\n readonly onStageChange = new Signal<(stage: { index: number; value: string }) => void>();\n\n constructor(node: StagedButtonNode, textures: ReadonlyMap<string, Texture> | undefined, interaction: SceneInteractionMode, sounds?: ReadonlyMap<string, Sound>) {\n super(textures);\n this.interaction = interaction;\n this.sounds = sounds;\n this.soundAssetIds = node.sounds;\n this.nineSlice = node.nineSlice;\n this.stagesData = node.stages;\n this.wrapState = node.wrap;\n this.defaultStageId = node.defaultStageId;\n this.currentStageId = node.stages.some((stage) => stage.id === node.defaultStageId) ? node.defaultStageId : node.stages[0]!.id;\n this.enabledState = node.enabled;\n this.buildButton();\n }\n\n private currentStage(): StagedButtonStage {\n return this.stagesData.find((stage) => stage.id === this.currentStageId) ?? this.stagesData[0]!;\n }\n\n private static createStateView(texture: Texture | undefined, nineSlice: LayoutPadding | undefined): Container {\n if (texture === undefined) return new Graphics();\n return nineSlice === undefined\n ? new Sprite(texture)\n : new NineSliceSprite({ texture, leftWidth: nineSlice.left, topHeight: nineSlice.top, rightWidth: nineSlice.right, bottomHeight: nineSlice.bottom });\n }\n\n /**\n * (Re)builds `FancyButton` and its four state views for the current stage. `size`, when provided,\n * sizes the freshly built views immediately — needed because a stage change may happen well outside\n * a `syncContent` pass (e.g. a runtime click), where nothing else would resize them afterwards.\n */\n private buildButton(size?: { width: number; height: number }): void {\n const stage = this.currentStage();\n const normalTexture = this.textureFor(stage.states.normalAssetId);\n const stateViews = {} as Record<ButtonStateKey, Container>;\n const viewFor = (state: ButtonStateKey, assetId: string | undefined): Container => {\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\n const view = StagedButtonNodeView.createStateView(texture, this.nineSlice);\n stateViews[state] = view;\n return view;\n };\n\n const button = new FancyButton({\n defaultView: viewFor(\"normal\", stage.states.normalAssetId),\n hoverView: viewFor(\"hover\", stage.states.hoverAssetId),\n pressedView: viewFor(\"pressed\", stage.states.pressedAssetId),\n disabledView: viewFor(\"disabled\", stage.states.disabledAssetId),\n });\n // Authoring-сцена инертна, как и у обычной кнопки: клики не должны крутить стадию, а попадание по\n // ноде остаётся ответственностью NodeView.containsPoint.\n if (this.interaction === \"authoring\") button.eventMode = \"none\";\n else {\n button.onPress.connect(() => {\n this.next();\n const result = this.soundAssetIds?.pressAssetId === undefined ? undefined : this.sounds?.get(this.soundAssetIds.pressAssetId)?.play();\n if (result instanceof Promise) void result.catch(() => undefined);\n });\n button.onHover.connect(() => {\n const result = this.soundAssetIds?.hoverAssetId === undefined ? undefined : this.sounds?.get(this.soundAssetIds.hoverAssetId)?.play();\n if (result instanceof Promise) void result.catch(() => undefined);\n });\n }\n this.button = button;\n this.stateViews = stateViews;\n this.setContent(button);\n\n const resolved = size ?? this.layoutRectangle;\n this.applySizes(resolved.width, resolved.height);\n\n // Пересборка — деталь реализации `FancyButton`, а не действие пользователя: transient enabled и\n // визуальное состояние восстанавливаются на новом инстансе, иначе переключение nine-slice или\n // догрузка текстуры сбрасывали бы их.\n this.applyEnabled(this.enabledState);\n if (this.forcedState !== undefined) this.button.setState(FANCY_STATE_BY_KEY[this.forcedState]);\n }\n\n /**\n * Sizes the four state views to the resolved layout rectangle without touching their textures.\n *\n * `NineSliceSprite` проверяется отдельно: в PixiJS 8 он наследуется от `ViewContainer`, а не от\n * `Sprite`, поэтому одна ветка `instanceof Sprite` его молча пропускает.\n */\n private applySizes(width: number, height: number): void {\n for (const state of BUTTON_STATE_KEYS) {\n const view = this.stateViews[state];\n if (view instanceof NineSliceSprite) view.setSize(width, height);\n else if (view instanceof Sprite) view.setSize(width, height);\n else if (view instanceof Graphics) view.clear().rect(0, 0, width, height).fill(0x4a5568).stroke({ width: 1, color: 0x94a3b8 });\n }\n }\n\n /**\n * Applies the current stage's textures/border to the existing state views (or rebuilds them if a\n * view's required type no longer matches, the same rule `ButtonNodeView.syncContent` follows).\n */\n private refreshStateViews(width: number, height: number): void {\n const stage = this.currentStage();\n const normalTexture = this.textureFor(stage.states.normalAssetId);\n\n let needsRebuild = false;\n for (const state of BUTTON_STATE_KEYS) {\n const assetId = stage.states[`${state}AssetId`];\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\n const view = this.stateViews[state];\n const wantsNineSlice = texture !== undefined && this.nineSlice !== undefined;\n // `NineSliceSprite` не наследуется от `Sprite` в PixiJS 8, поэтому ветки не пересекаются.\n const matches = texture === undefined ? view instanceof Graphics : wantsNineSlice ? view instanceof NineSliceSprite : view instanceof Sprite;\n if (!matches) { needsRebuild = true; break; }\n }\n\n if (needsRebuild) {\n this.buildButton({ width, height });\n return;\n }\n\n for (const state of BUTTON_STATE_KEYS) {\n const view = this.stateViews[state];\n const assetId = stage.states[`${state}AssetId`];\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\n // Set the texture before the layout size: Sprite/NineSliceSprite preserve scale when their\n // texture changes, so sizing first would shift visual bounds for a differently-sized source.\n if (view instanceof NineSliceSprite) {\n if (texture !== undefined && view.texture !== texture) view.texture = texture;\n if (this.nineSlice !== undefined) {\n view.leftWidth = this.nineSlice.left;\n view.topHeight = this.nineSlice.top;\n view.rightWidth = this.nineSlice.right;\n view.bottomHeight = this.nineSlice.bottom;\n }\n } else if (view instanceof Sprite) {\n if (texture !== undefined && view.texture !== texture) view.texture = texture;\n }\n }\n this.applySizes(width, height);\n }\n\n private applyStage(stageId: string): void {\n this.currentStageId = stageId;\n const { width, height } = this.layoutRectangle;\n this.refreshStateViews(width, height);\n }\n\n /** In authoring mode `enabled` may only drive the visuals: `FancyButton` couples it to `eventMode`. */\n private applyEnabled(enabled: boolean): void {\n this.enabledState = enabled;\n if (this.interaction === \"runtime\") this.button.enabled = enabled;\n else this.button.setState(enabled ? \"default\" : \"disabled\");\n }\n\n protected syncContent(node: UINode, transform: UINode[\"transform\"]): void {\n if (node.type !== \"staged-button\") return;\n this.soundAssetIds = node.sounds;\n this.nineSlice = node.nineSlice;\n this.wrapState = node.wrap;\n this.defaultStageId = node.defaultStageId;\n\n // Сохраняет текущую стадию, если она ещё существует после изменения документа; если стадию\n // удалили — переходит на ближайшую валидную по прежнему индексу.\n const previousIndex = this.stagesData.findIndex((stage) => stage.id === this.currentStageId);\n this.stagesData = node.stages;\n if (!node.stages.some((stage) => stage.id === this.currentStageId)) {\n const clampedIndex = Math.min(Math.max(previousIndex, 0), node.stages.length - 1);\n this.currentStageId = node.stages[clampedIndex]!.id;\n }\n\n this.refreshStateViews(transform.width, transform.height);\n\n // Только на изменение документа: иначе любой пересчёт layout сбрасывал бы transient preview state.\n if (node.enabled !== this.enabledState) this.applyEnabled(node.enabled);\n }\n\n /** Editor-only live preview of nine-slice border edits while dragging the canvas gizmo. */\n previewNineSlice(nineSlice: LayoutPadding): void {\n for (const state of BUTTON_STATE_KEYS) {\n const view = this.stateViews[state];\n if (!(view instanceof NineSliceSprite)) continue;\n view.leftWidth = nineSlice.left;\n view.topHeight = nineSlice.top;\n view.rightWidth = nineSlice.right;\n view.bottomHeight = nineSlice.bottom;\n }\n }\n\n get enabled(): boolean {\n return this.enabledState;\n }\n\n set enabled(value: boolean) {\n this.applyEnabled(value);\n }\n\n get stage(): number {\n const index = this.stagesData.findIndex((stage) => stage.id === this.currentStageId);\n return index === -1 ? 0 : index;\n }\n\n get value(): string {\n return this.currentStage().value;\n }\n\n get stageCount(): number {\n return this.stagesData.length;\n }\n\n /** Moves to the stage at `index` and emits `onStageChange`; an out-of-range index is a no-op. */\n setStage(index: number): void {\n const target = this.stagesData[index];\n if (target === undefined) return;\n this.applyStage(target.id);\n this.onStageChange.emit({ index, value: target.value });\n }\n\n /** Moves to the stage at `index` without emitting — used by editor preview to avoid feedback loops. */\n forceStage(index: number): void {\n const target = this.stagesData[index];\n if (target === undefined) return;\n this.applyStage(target.id);\n }\n\n /** Moves to the first stage whose `value` matches; an unknown value is ignored. */\n setValue(value: string): void {\n const index = this.stagesData.findIndex((stage) => stage.value === value);\n if (index === -1) return;\n this.setStage(index);\n }\n\n /** Advances one stage, as a click does; respects `wrap` at the last stage. */\n next(): void {\n const nextIndex = this.stage + 1;\n if (nextIndex >= this.stagesData.length) {\n if (!this.wrapState) return;\n this.setStage(0);\n return;\n }\n this.setStage(nextIndex);\n }\n\n /** Returns to `defaultStageId`, emitting `onStageChange`. */\n reset(): void {\n const index = this.stagesData.findIndex((stage) => stage.id === this.defaultStageId);\n this.setStage(index === -1 ? 0 : index);\n }\n\n /** Forces an editor-only visual state without changing `enabled`, mirroring `ButtonNodeView.setState`. */\n setState(state: ButtonStateKey): void {\n this.forcedState = state;\n this.button.setState(FANCY_STATE_BY_KEY[state]);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"StagedButtonNodeView.js","sourceRoot":"","sources":["../../src/views/StagedButtonNodeView.ts"],"names":[],"mappings":"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","sourcesContent":["import { BUTTON_STATE_KEYS, type ButtonStateKey, type LayoutPadding, type StagedButtonNode, type UINode } from \"@pixi-ui-editor/schema\";\nimport { FancyButton } from \"@pixi/ui\";\nimport type { Sound } from \"@pixi/sound\";\nimport { Container, Graphics, NineSliceSprite, Sprite, Texture } from \"pixi.js\";\nimport { Signal } from \"typed-signals\";\nimport { NodeView, type SceneInteractionMode } from \"./NodeView.js\";\n\nexport type { StagedButtonNode };\ntype StagedButtonStage = StagedButtonNode[\"stages\"][number];\ntype FancyButtonEventHandler = Parameters<FancyButton[\"onPress\"][\"connect\"]>[0];\n\nconst FANCY_STATE_BY_KEY = { normal: \"default\", hover: \"hover\", pressed: \"pressed\", disabled: \"disabled\" } as const;\n\n/**\n * Adapts a staged-button node to `@pixi/ui`'s `FancyButton`, exactly like `ButtonNodeView` does for a\n * plain button — this class never reimplements its pointer state machine either. It is a deliberately\n * separate component (see TASK-057): it does not extend or share behavior with `ButtonNodeView`.\n *\n * A click at runtime advances the current stage (`next()`), which respects `wrap`. Four state views\n * are created once and reused across stages: changing stage only reassigns their textures/size, never\n * replacing the view type — a full `FancyButton` rebuild only happens when a view's required *type*\n * changes (nine-slice toggled, or a texture just finished loading), because `FancyButton` gives no way\n * to swap a view after construction.\n */\nexport class StagedButtonNodeView extends NodeView {\n private button!: FancyButton;\n private stateViews!: Record<ButtonStateKey, Container>;\n private readonly interaction: SceneInteractionMode;\n private readonly sounds: ReadonlyMap<string, Sound> | undefined;\n private soundAssetIds: StagedButtonNode[\"sounds\"];\n private nineSlice: LayoutPadding | undefined;\n private stagesData: StagedButtonStage[];\n private currentStageId: string;\n private defaultStageId: string;\n private wrapState: boolean;\n private enabledState: boolean;\n /** Последнее editor-only визуальное состояние из `setState`: переживает пересборку `FancyButton`. */\n private forcedState: ButtonStateKey | undefined;\n\n /** Stable public signals: subscriptions survive an internal `FancyButton` rebuild. */\n readonly onPress: FancyButton[\"onPress\"] = new Signal<FancyButtonEventHandler>();\n readonly onDown: FancyButton[\"onDown\"] = new Signal<FancyButtonEventHandler>();\n readonly onUp: FancyButton[\"onUp\"] = new Signal<FancyButtonEventHandler>();\n readonly onHover: FancyButton[\"onHover\"] = new Signal<FancyButtonEventHandler>();\n readonly onOut: FancyButton[\"onOut\"] = new Signal<FancyButtonEventHandler>();\n readonly onStageChange = new Signal<(stage: { index: number; value: string }) => void>();\n\n constructor(node: StagedButtonNode, textures: ReadonlyMap<string, Texture> | undefined, interaction: SceneInteractionMode, sounds?: ReadonlyMap<string, Sound>) {\n super(textures);\n this.interaction = interaction;\n this.sounds = sounds;\n this.soundAssetIds = node.sounds;\n this.nineSlice = node.nineSlice;\n this.stagesData = node.stages;\n this.wrapState = node.wrap;\n this.defaultStageId = node.defaultStageId;\n this.currentStageId = node.stages.some((stage) => stage.id === node.defaultStageId) ? node.defaultStageId : node.stages[0]!.id;\n this.enabledState = node.enabled;\n this.buildButton();\n }\n\n private currentStage(): StagedButtonStage {\n return this.stagesData.find((stage) => stage.id === this.currentStageId) ?? this.stagesData[0]!;\n }\n\n private static createStateView(texture: Texture | undefined, nineSlice: LayoutPadding | undefined): Container {\n if (texture === undefined) return new Graphics();\n return nineSlice === undefined\n ? new Sprite(texture)\n : new NineSliceSprite({ texture, leftWidth: nineSlice.left, topHeight: nineSlice.top, rightWidth: nineSlice.right, bottomHeight: nineSlice.bottom });\n }\n\n /**\n * (Re)builds `FancyButton` and its four state views for the current stage. `size`, when provided,\n * sizes the freshly built views immediately — needed because a stage change may happen well outside\n * a `syncContent` pass (e.g. a runtime click), where nothing else would resize them afterwards.\n */\n private buildButton(size?: { width: number; height: number }): void {\n const stage = this.currentStage();\n const normalTexture = this.textureFor(stage.states.normalAssetId);\n const stateViews = {} as Record<ButtonStateKey, Container>;\n const viewFor = (state: ButtonStateKey, assetId: string | undefined): Container => {\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\n const view = StagedButtonNodeView.createStateView(texture, this.nineSlice);\n stateViews[state] = view;\n return view;\n };\n\n const button = new FancyButton({\n defaultView: viewFor(\"normal\", stage.states.normalAssetId),\n hoverView: viewFor(\"hover\", stage.states.hoverAssetId),\n pressedView: viewFor(\"pressed\", stage.states.pressedAssetId),\n disabledView: viewFor(\"disabled\", stage.states.disabledAssetId),\n });\n // Authoring-сцена инертна, как и у обычной кнопки: клики не должны крутить стадию, а попадание по\n // ноде остаётся ответственностью NodeView.containsPoint.\n if (this.interaction === \"authoring\") button.eventMode = \"none\";\n else {\n button.onPress.connect((source, event) => {\n this.next();\n const result = this.soundAssetIds?.pressAssetId === undefined ? undefined : this.sounds?.get(this.soundAssetIds.pressAssetId)?.play();\n if (result instanceof Promise) void result.catch(() => undefined);\n this.onPress.emit(source, event);\n });\n button.onDown.connect((source, event) => this.onDown.emit(source, event));\n button.onUp.connect((source, event) => this.onUp.emit(source, event));\n button.onHover.connect((source, event) => {\n const result = this.soundAssetIds?.hoverAssetId === undefined ? undefined : this.sounds?.get(this.soundAssetIds.hoverAssetId)?.play();\n if (result instanceof Promise) void result.catch(() => undefined);\n this.onHover.emit(source, event);\n });\n button.onOut.connect((source, event) => this.onOut.emit(source, event));\n }\n this.button = button;\n this.stateViews = stateViews;\n this.setContent(button);\n\n const resolved = size ?? this.layoutRectangle;\n this.applySizes(resolved.width, resolved.height);\n\n // Пересборка — деталь реализации `FancyButton`, а не действие пользователя: transient enabled и\n // визуальное состояние восстанавливаются на новом инстансе, иначе переключение nine-slice или\n // догрузка текстуры сбрасывали бы их.\n this.applyEnabled(this.enabledState);\n if (this.forcedState !== undefined) this.button.setState(FANCY_STATE_BY_KEY[this.forcedState]);\n }\n\n /**\n * Sizes the four state views to the resolved layout rectangle without touching their textures.\n *\n * `NineSliceSprite` проверяется отдельно: в PixiJS 8 он наследуется от `ViewContainer`, а не от\n * `Sprite`, поэтому одна ветка `instanceof Sprite` его молча пропускает.\n */\n private applySizes(width: number, height: number): void {\n for (const state of BUTTON_STATE_KEYS) {\n const view = this.stateViews[state];\n if (view instanceof NineSliceSprite) view.setSize(width, height);\n else if (view instanceof Sprite) view.setSize(width, height);\n else if (view instanceof Graphics) view.clear().rect(0, 0, width, height).fill(0x4a5568).stroke({ width: 1, color: 0x94a3b8 });\n }\n }\n\n /**\n * Applies the current stage's textures/border to the existing state views (or rebuilds them if a\n * view's required type no longer matches, the same rule `ButtonNodeView.syncContent` follows).\n */\n private refreshStateViews(width: number, height: number): void {\n const stage = this.currentStage();\n const normalTexture = this.textureFor(stage.states.normalAssetId);\n\n let needsRebuild = false;\n for (const state of BUTTON_STATE_KEYS) {\n const assetId = stage.states[`${state}AssetId`];\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\n const view = this.stateViews[state];\n const wantsNineSlice = texture !== undefined && this.nineSlice !== undefined;\n // `NineSliceSprite` не наследуется от `Sprite` в PixiJS 8, поэтому ветки не пересекаются.\n const matches = texture === undefined ? view instanceof Graphics : wantsNineSlice ? view instanceof NineSliceSprite : view instanceof Sprite;\n if (!matches) { needsRebuild = true; break; }\n }\n\n if (needsRebuild) {\n this.buildButton({ width, height });\n return;\n }\n\n for (const state of BUTTON_STATE_KEYS) {\n const view = this.stateViews[state];\n const assetId = stage.states[`${state}AssetId`];\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\n // Set the texture before the layout size: Sprite/NineSliceSprite preserve scale when their\n // texture changes, so sizing first would shift visual bounds for a differently-sized source.\n if (view instanceof NineSliceSprite) {\n if (texture !== undefined && view.texture !== texture) view.texture = texture;\n if (this.nineSlice !== undefined) {\n view.leftWidth = this.nineSlice.left;\n view.topHeight = this.nineSlice.top;\n view.rightWidth = this.nineSlice.right;\n view.bottomHeight = this.nineSlice.bottom;\n }\n } else if (view instanceof Sprite) {\n if (texture !== undefined && view.texture !== texture) view.texture = texture;\n }\n }\n this.applySizes(width, height);\n }\n\n private applyStage(stageId: string): void {\n this.currentStageId = stageId;\n const { width, height } = this.layoutRectangle;\n this.refreshStateViews(width, height);\n }\n\n /** In authoring mode `enabled` may only drive the visuals: `FancyButton` couples it to `eventMode`. */\n private applyEnabled(enabled: boolean): void {\n this.enabledState = enabled;\n if (this.interaction === \"runtime\") this.button.enabled = enabled;\n else this.button.setState(enabled ? \"default\" : \"disabled\");\n }\n\n protected syncContent(node: UINode, transform: UINode[\"transform\"]): void {\n if (node.type !== \"staged-button\") return;\n this.soundAssetIds = node.sounds;\n this.nineSlice = node.nineSlice;\n this.wrapState = node.wrap;\n this.defaultStageId = node.defaultStageId;\n\n // Сохраняет текущую стадию, если она ещё существует после изменения документа; если стадию\n // удалили — переходит на ближайшую валидную по прежнему индексу.\n const previousIndex = this.stagesData.findIndex((stage) => stage.id === this.currentStageId);\n this.stagesData = node.stages;\n if (!node.stages.some((stage) => stage.id === this.currentStageId)) {\n const clampedIndex = Math.min(Math.max(previousIndex, 0), node.stages.length - 1);\n this.currentStageId = node.stages[clampedIndex]!.id;\n }\n\n this.refreshStateViews(transform.width, transform.height);\n\n // Только на изменение документа: иначе любой пересчёт layout сбрасывал бы transient preview state.\n if (node.enabled !== this.enabledState) this.applyEnabled(node.enabled);\n }\n\n /** Editor-only live preview of nine-slice border edits while dragging the canvas gizmo. */\n previewNineSlice(nineSlice: LayoutPadding): void {\n for (const state of BUTTON_STATE_KEYS) {\n const view = this.stateViews[state];\n if (!(view instanceof NineSliceSprite)) continue;\n view.leftWidth = nineSlice.left;\n view.topHeight = nineSlice.top;\n view.rightWidth = nineSlice.right;\n view.bottomHeight = nineSlice.bottom;\n }\n }\n\n get enabled(): boolean {\n return this.enabledState;\n }\n\n set enabled(value: boolean) {\n this.applyEnabled(value);\n }\n\n get stage(): number {\n const index = this.stagesData.findIndex((stage) => stage.id === this.currentStageId);\n return index === -1 ? 0 : index;\n }\n\n get value(): string {\n return this.currentStage().value;\n }\n\n get stageCount(): number {\n return this.stagesData.length;\n }\n\n /** Moves to the stage at `index` and emits `onStageChange`; an out-of-range index is a no-op. */\n setStage(index: number): void {\n const target = this.stagesData[index];\n if (target === undefined) return;\n this.applyStage(target.id);\n this.onStageChange.emit({ index, value: target.value });\n }\n\n /** Moves to the stage at `index` without emitting — used by editor preview to avoid feedback loops. */\n forceStage(index: number): void {\n const target = this.stagesData[index];\n if (target === undefined) return;\n this.applyStage(target.id);\n }\n\n /** Moves to the first stage whose `value` matches; an unknown value is ignored. */\n setValue(value: string): void {\n const index = this.stagesData.findIndex((stage) => stage.value === value);\n if (index === -1) return;\n this.setStage(index);\n }\n\n /** Advances one stage, as a click does; respects `wrap` at the last stage. */\n next(): void {\n const nextIndex = this.stage + 1;\n if (nextIndex >= this.stagesData.length) {\n if (!this.wrapState) return;\n this.setStage(0);\n return;\n }\n this.setStage(nextIndex);\n }\n\n /** Returns to `defaultStageId`, emitting `onStageChange`. */\n reset(): void {\n const index = this.stagesData.findIndex((stage) => stage.id === this.defaultStageId);\n this.setStage(index === -1 ? 0 : index);\n }\n\n /** Forces an editor-only visual state without changing `enabled`, mirroring `ButtonNodeView.setState`. */\n setState(state: ButtonStateKey): void {\n this.forcedState = state;\n this.button.setState(FANCY_STATE_BY_KEY[state]);\n }\n}\n"]}
|