@pixi-ui-editor/runtime-pixi 0.3.0 → 0.4.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/README.md +81 -2
- package/dist/SceneViewport.d.ts +17 -0
- package/dist/SceneViewport.d.ts.map +1 -0
- package/dist/SceneViewport.js +35 -0
- package/dist/SceneViewport.js.map +1 -0
- package/dist/assets/cache.d.ts +27 -0
- package/dist/assets/cache.d.ts.map +1 -0
- package/dist/assets/cache.js +17 -0
- package/dist/assets/cache.js.map +1 -0
- package/dist/assets/fonts.d.ts +1 -1
- package/dist/assets/fonts.d.ts.map +1 -1
- package/dist/assets/fonts.js +2 -2
- package/dist/assets/fonts.js.map +1 -1
- package/dist/controls.d.ts +5 -1
- package/dist/controls.d.ts.map +1 -1
- package/dist/controls.js +12 -0
- package/dist/controls.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +93 -3
- package/dist/index.test.js.map +1 -1
- package/dist/layout.d.ts +10 -4
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +13 -0
- package/dist/layout.js.map +1 -1
- package/dist/localization.d.ts +42 -0
- package/dist/localization.d.ts.map +1 -0
- package/dist/localization.js +73 -0
- package/dist/localization.js.map +1 -0
- package/dist/scene.d.ts +23 -13
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +61 -11
- package/dist/scene.js.map +1 -1
- package/dist/views/ButtonNodeView.d.ts +16 -3
- package/dist/views/ButtonNodeView.d.ts.map +1 -1
- package/dist/views/ButtonNodeView.js +94 -27
- package/dist/views/ButtonNodeView.js.map +1 -1
- package/dist/views/CheckboxNodeView.d.ts +47 -0
- package/dist/views/CheckboxNodeView.d.ts.map +1 -0
- package/dist/views/CheckboxNodeView.js +127 -0
- package/dist/views/CheckboxNodeView.js.map +1 -0
- package/dist/views/basic.d.ts +45 -2
- package/dist/views/basic.d.ts.map +1 -1
- package/dist/views/basic.js +116 -7
- package/dist/views/basic.js.map +1 -1
- package/dist/views/createNodeView.d.ts.map +1 -1
- package/dist/views/createNodeView.js +16 -3
- package/dist/views/createNodeView.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -17,12 +17,12 @@ import { loadPrefabView } from "@pixi-ui-editor/runtime-pixi";
|
|
|
17
17
|
import { materializedPrefabNodeId } from "@pixi-ui-editor/schema";
|
|
18
18
|
|
|
19
19
|
const configured = scene.nodes.find(
|
|
20
|
-
(node) => node.type === "prefab-instance" && node.
|
|
20
|
+
(node) => node.type === "prefab-instance" && node.bindingKey === "rewardCardTemplate",
|
|
21
21
|
);
|
|
22
22
|
if (configured?.type !== "prefab-instance") throw new Error("Configured preset instance is missing.");
|
|
23
23
|
const labelDefinitionNodeId = document.prefabs
|
|
24
24
|
.find((prefab) => prefab.id === configured.prefabId)!
|
|
25
|
-
.nodes.find((node) => node.
|
|
25
|
+
.nodes.find((node) => node.bindingKey === "label")!.id;
|
|
26
26
|
|
|
27
27
|
const mounted = await loadPrefabView(
|
|
28
28
|
document,
|
|
@@ -44,3 +44,82 @@ const labelView = mounted.nodeViews.get(
|
|
|
44
44
|
|
|
45
45
|
Omit the last argument to instantiate the prefab definition defaults. `nodeViews` contains the outer
|
|
46
46
|
instance plus deterministic instance-scoped IDs for inherited children and stable IDs for local children.
|
|
47
|
+
|
|
48
|
+
The lookup above is two-step on purpose, and it is the contract: a `bindingKey` is unique within the scope
|
|
49
|
+
it resolves against, not across the whole document. A key on a scene node — including a preset instance
|
|
50
|
+
itself — is unique within that scene. A key inside a preset definition is unique within that definition and
|
|
51
|
+
resolves against one instance, which is what makes it usable for presets you spawn many times: a hundred
|
|
52
|
+
symbol views can each carry `"spine"` without colliding. So you find the instance by its scene-level key,
|
|
53
|
+
then find the node inside it by its definition-level key.
|
|
54
|
+
|
|
55
|
+
## Reuse parsed assets across loads
|
|
56
|
+
|
|
57
|
+
`loadPrefabView` and `loadSceneView` fetch and parse every asset a scene references. Spawning presets one
|
|
58
|
+
by one — a symbol view per reel stop, a card per deal — repeats that work for each call: the skeleton JSON
|
|
59
|
+
is re-parsed, the atlas is rebuilt, and every texture becomes a separate GPU upload of the same file.
|
|
60
|
+
|
|
61
|
+
Pass an `assetCache` you own to share the parsed results. Its lifetime is yours: keep one per scene session
|
|
62
|
+
and drop it with the scene.
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { createSceneAssetCache, loadPrefabView } from "@pixi-ui-editor/runtime-pixi";
|
|
66
|
+
|
|
67
|
+
const assetCache = createSceneAssetCache();
|
|
68
|
+
|
|
69
|
+
const mounted = await loadPrefabView(document, prefabId, "desktop", resolveFileUrl, {
|
|
70
|
+
interaction: "runtime",
|
|
71
|
+
assetCache,
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Each map is keyed by the asset file's URI, so the same file assigned to different assets loads once. Without
|
|
76
|
+
`assetCache` every call starts from empty maps, which is fine for a single scene load and wasteful for
|
|
77
|
+
anything spawned repeatedly. The lower-level `loadSceneTextures` / `loadSceneSpines` / `loadSceneFonts` /
|
|
78
|
+
`loadSceneSounds` take the individual maps directly if you drive loading yourself.
|
|
79
|
+
|
|
80
|
+
## Scene viewport
|
|
81
|
+
|
|
82
|
+
`buildSceneView` and `loadSceneView` return a runtime-owned `SceneViewport` as `root`. With
|
|
83
|
+
`interaction: "runtime"`, that object clips content to the authored reference viewport and owns the
|
|
84
|
+
only contain-fit calculation used by Preview and consuming applications:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const { root } = await loadSceneView(document, sceneId, profile, resolveFileUrl, {
|
|
88
|
+
interaction: "runtime",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
app.stage.addChild(root);
|
|
92
|
+
root.resize({ width: app.screen.width, height: app.screen.height });
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Call `root.resize(...)` after the Pixi renderer changes size. It applies one uniform scale and centers
|
|
96
|
+
the unchanged authored viewport; letterbox and pillarbox space never reveal off-viewport content.
|
|
97
|
+
Profile selection remains shared through `resolveProfileForViewport`, and crossing its breakpoint
|
|
98
|
+
requires building the scene again with the newly selected profile. Hosts must not reproduce the fit
|
|
99
|
+
formula or add their own viewport mask. Spawned prefab views stay unclipped because they are scene
|
|
100
|
+
content rather than player-screen boundaries.
|
|
101
|
+
|
|
102
|
+
## Localization
|
|
103
|
+
|
|
104
|
+
`buildSceneView`, `loadSceneView`, `buildPrefabView` and `loadPrefabView` return a `SceneLocalization`
|
|
105
|
+
next to `root` and `nodeViews`. A `Text` node that carries a `textKey` already renders its base-locale
|
|
106
|
+
translation without any extra call; a document without keys behaves exactly as before.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
const { root, localization } = await loadSceneView(document, sceneId, "desktop", resolveFileUrl, {
|
|
110
|
+
interaction: "runtime",
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
localization.setLocale("pt-BR");
|
|
114
|
+
// Values ship as a flat Record<string, string> in locales/<locale>.json, so any dictionary works.
|
|
115
|
+
localization.setTranslate((key, { locale }) => dictionaries[locale]?.[key]);
|
|
116
|
+
// `{amount}` inside a translation is a runtime parameter, not a second key.
|
|
117
|
+
localization.setParams({ amount: 48 });
|
|
118
|
+
// An immutable editor/game store hands over a new document copy without rebuilding the scene.
|
|
119
|
+
localization.syncDocument(nextDocument);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Resolution order is `translate` → requested locale → base locale → `Text.text` → the key itself; a
|
|
123
|
+
`translate` returning `undefined` keeps the chain going. Every call mutates the existing `TextNodeView`
|
|
124
|
+
objects: the scene root, `NodeView` identity and live control state are preserved, and re-rendering the
|
|
125
|
+
same string does no work at all.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Container } from "pixi.js";
|
|
2
|
+
import { type FittedViewport, type LayoutSize } from "./layout.js";
|
|
3
|
+
/**
|
|
4
|
+
* The single host boundary for a built scene.
|
|
5
|
+
*
|
|
6
|
+
* Runtime scenes are clipped to the authored reference viewport and can only be fitted with one
|
|
7
|
+
* uniform scale. Authoring scenes use the same wrapper without clipping so the editor can expose
|
|
8
|
+
* off-viewport content while arranging it.
|
|
9
|
+
*/
|
|
10
|
+
export declare class SceneViewport extends Container {
|
|
11
|
+
readonly referenceViewport: LayoutSize;
|
|
12
|
+
readonly content: Container;
|
|
13
|
+
constructor(content: Container, referenceViewport: LayoutSize, clip: boolean);
|
|
14
|
+
/** Uniformly contains and centers this authored viewport inside the physical host. */
|
|
15
|
+
resize(host: LayoutSize): FittedViewport;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=SceneViewport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SceneViewport.d.ts","sourceRoot":"","sources":["../src/SceneViewport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAY,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAqB,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAEtF;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,SAAS;IAC1C,QAAQ,CAAC,iBAAiB,EAAE,UAAU,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;gBAEhB,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO;IAiB5E,sFAAsF;IACtF,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc;CAMzC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Container, Graphics } from "pixi.js";
|
|
2
|
+
import { fitViewportToHost } from "./layout.js";
|
|
3
|
+
/**
|
|
4
|
+
* The single host boundary for a built scene.
|
|
5
|
+
*
|
|
6
|
+
* Runtime scenes are clipped to the authored reference viewport and can only be fitted with one
|
|
7
|
+
* uniform scale. Authoring scenes use the same wrapper without clipping so the editor can expose
|
|
8
|
+
* off-viewport content while arranging it.
|
|
9
|
+
*/
|
|
10
|
+
export class SceneViewport extends Container {
|
|
11
|
+
referenceViewport;
|
|
12
|
+
content;
|
|
13
|
+
constructor(content, referenceViewport, clip) {
|
|
14
|
+
super();
|
|
15
|
+
this.content = content;
|
|
16
|
+
this.referenceViewport = { ...referenceViewport };
|
|
17
|
+
if (clip) {
|
|
18
|
+
const viewportMask = new Graphics()
|
|
19
|
+
.rect(0, 0, referenceViewport.width, referenceViewport.height)
|
|
20
|
+
.fill(0xffffff);
|
|
21
|
+
viewportMask.eventMode = "none";
|
|
22
|
+
content.mask = viewportMask;
|
|
23
|
+
this.addChild(viewportMask);
|
|
24
|
+
}
|
|
25
|
+
this.addChild(content);
|
|
26
|
+
}
|
|
27
|
+
/** Uniformly contains and centers this authored viewport inside the physical host. */
|
|
28
|
+
resize(host) {
|
|
29
|
+
const fitted = fitViewportToHost(this.referenceViewport, host);
|
|
30
|
+
this.scale.set(fitted.scale);
|
|
31
|
+
this.position.set(fitted.x, fitted.y);
|
|
32
|
+
return fitted;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=SceneViewport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SceneViewport.js","sourceRoot":"","sources":["../src/SceneViewport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAwC,MAAM,aAAa,CAAC;AAEtF;;;;;;GAMG;AACH,MAAM,OAAO,aAAc,SAAQ,SAAS;IACjC,iBAAiB,CAAa;IAC9B,OAAO,CAAY;IAE5B,YAAY,OAAkB,EAAE,iBAA6B,EAAE,IAAa;QAC1E,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,iBAAiB,GAAG,EAAE,GAAG,iBAAiB,EAAE,CAAC;QAElD,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,YAAY,GAAG,IAAI,QAAQ,EAAE;iBAChC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,CAAC,MAAM,CAAC;iBAC7D,IAAI,CAAC,QAAQ,CAAC,CAAC;YAClB,YAAY,CAAC,SAAS,GAAG,MAAM,CAAC;YAChC,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC;YAC5B,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QAC9B,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IAED,sFAAsF;IACtF,MAAM,CAAC,IAAgB;QACrB,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;QAC/D,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QACtC,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SkeletonData } from "@esotericsoftware/spine-pixi-v8";
|
|
2
|
+
import type { Sound } from "@pixi/sound";
|
|
3
|
+
import type { Spritesheet, Texture } from "pixi.js";
|
|
4
|
+
/**
|
|
5
|
+
* Разобранные ассеты, переиспользуемые между загрузками сцен и пресетов. Каждая карта ключуется URI
|
|
6
|
+
* файла ассета, а не его id: один и тот же файл, назначенный разным ассетам, скачивается и парсится один раз.
|
|
7
|
+
*
|
|
8
|
+
* Владелец кэша — вызывающая сторона: время жизни задаёт она (сессия сцены в игре, открытый проект в
|
|
9
|
+
* редакторе), поэтому глобального состояния и скрытого сброса здесь нет.
|
|
10
|
+
*/
|
|
11
|
+
export interface SceneAssetCache {
|
|
12
|
+
textures: Map<string, Texture>;
|
|
13
|
+
spritesheets: Map<string, Spritesheet>;
|
|
14
|
+
spines: Map<string, SkeletonData>;
|
|
15
|
+
/** Обещание, а не готовое имя family: параллельные загрузки одного файла должны ждать один и тот же `FontFace.load()`. */
|
|
16
|
+
fonts: Map<string, Promise<string | undefined>>;
|
|
17
|
+
sounds: Map<string, Sound>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Шрифты кэшируются глобально, потому что `loadSceneFonts` регистрирует `FontFace` в общем для документа
|
|
21
|
+
* `document.fonts`: повторная загрузка того же файла добавила бы туда дубликат, независимо от того, чей
|
|
22
|
+
* это кэш. Остальные ассеты живут в GPU-памяти вызывающей стороны и такого общего реестра не имеют.
|
|
23
|
+
*/
|
|
24
|
+
export declare const sharedFontCache: Map<string, Promise<string | undefined>>;
|
|
25
|
+
/** Создаёт пустой кэш ассетов. Шрифты по умолчанию разделяются со всеми остальными кэшами — см. `sharedFontCache`. */
|
|
26
|
+
export declare function createSceneAssetCache(): SceneAssetCache;
|
|
27
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/assets/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACvC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAClC,0HAA0H;IAC1H,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAChD,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC5B;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,0CAAiD,CAAC;AAE9E,sHAAsH;AACtH,wBAAgB,qBAAqB,IAAI,eAAe,CAQvD"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Шрифты кэшируются глобально, потому что `loadSceneFonts` регистрирует `FontFace` в общем для документа
|
|
3
|
+
* `document.fonts`: повторная загрузка того же файла добавила бы туда дубликат, независимо от того, чей
|
|
4
|
+
* это кэш. Остальные ассеты живут в GPU-памяти вызывающей стороны и такого общего реестра не имеют.
|
|
5
|
+
*/
|
|
6
|
+
export const sharedFontCache = new Map();
|
|
7
|
+
/** Создаёт пустой кэш ассетов. Шрифты по умолчанию разделяются со всеми остальными кэшами — см. `sharedFontCache`. */
|
|
8
|
+
export function createSceneAssetCache() {
|
|
9
|
+
return {
|
|
10
|
+
textures: new Map(),
|
|
11
|
+
spritesheets: new Map(),
|
|
12
|
+
spines: new Map(),
|
|
13
|
+
fonts: sharedFontCache,
|
|
14
|
+
sounds: new Map(),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/assets/cache.ts"],"names":[],"mappings":"AAoBA;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,GAAG,EAAuC,CAAC;AAE9E,sHAAsH;AACtH,MAAM,UAAU,qBAAqB;IACnC,OAAO;QACL,QAAQ,EAAE,IAAI,GAAG,EAAE;QACnB,YAAY,EAAE,IAAI,GAAG,EAAE;QACvB,MAAM,EAAE,IAAI,GAAG,EAAE;QACjB,KAAK,EAAE,eAAe;QACtB,MAAM,EAAE,IAAI,GAAG,EAAE;KAClB,CAAC;AACJ,CAAC"}
|
package/dist/assets/fonts.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ProjectDocument } from "@pixi-ui-editor/schema";
|
|
2
2
|
import type { FileUrlResolver } from "./textures.js";
|
|
3
3
|
/** Loads scene fonts before views are built. A load failure is a warning and keeps system fallback usable. */
|
|
4
|
-
export declare function loadSceneFonts(document: ProjectDocument, sceneId: string, resolveFileUrl: FileUrlResolver): Promise<Map<string, string>>;
|
|
4
|
+
export declare function loadSceneFonts(document: ProjectDocument, sceneId: string, resolveFileUrl: FileUrlResolver, cache?: Map<string, Promise<string | undefined>>): Promise<Map<string, string>>;
|
|
5
5
|
//# sourceMappingURL=fonts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../../src/assets/fonts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,eAAe,EAAU,MAAM,wBAAwB,CAAC;AAC7E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAYrD,8GAA8G;AAC9G,wBAAsB,cAAc,
|
|
1
|
+
{"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../../src/assets/fonts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,eAAe,EAAU,MAAM,wBAAwB,CAAC;AAC7E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAYrD,8GAA8G;AAC9G,wBAAsB,cAAc,CAClC,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,eAAe,EAC/B,KAAK,GAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAmB,GAChE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAsB9B"}
|
package/dist/assets/fonts.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { sharedFontCache } from "./cache.js";
|
|
1
2
|
import { collectRenderedNodes } from "../scene.js";
|
|
2
|
-
const cache = new Map();
|
|
3
3
|
const keyOf = (asset) => `${asset.source.uri}#${asset.source.version ?? ""}`;
|
|
4
4
|
const fromNodes = (nodes) => nodes.flatMap((node) => {
|
|
5
5
|
if (node.type === "text" && node.style?.fontAssetId)
|
|
@@ -11,7 +11,7 @@ const fromNodes = (nodes) => nodes.flatMap((node) => {
|
|
|
11
11
|
return [];
|
|
12
12
|
});
|
|
13
13
|
/** Loads scene fonts before views are built. A load failure is a warning and keeps system fallback usable. */
|
|
14
|
-
export async function loadSceneFonts(document, sceneId, resolveFileUrl) {
|
|
14
|
+
export async function loadSceneFonts(document, sceneId, resolveFileUrl, cache = sharedFontCache) {
|
|
15
15
|
const scene = document.scenes.find((item) => item.id === sceneId);
|
|
16
16
|
if (scene === undefined)
|
|
17
17
|
throw new Error(`Scene '${sceneId}' does not exist in the project document.`);
|
package/dist/assets/fonts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fonts.js","sourceRoot":"","sources":["../../src/assets/fonts.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"fonts.js","sourceRoot":"","sources":["../../src/assets/fonts.ts"],"names":[],"mappings":"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"}
|
package/dist/controls.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type SkeletonData } from "@esotericsoftware/spine-pixi-v8";
|
|
2
|
-
import { type ButtonStateKey } from "@pixi-ui-editor/schema";
|
|
2
|
+
import { type ButtonStateKey, type LayoutPadding } from "@pixi-ui-editor/schema";
|
|
3
3
|
import { Container } from "pixi.js";
|
|
4
4
|
/** Reads the current 1-based animation frame for an editor Spine node. */
|
|
5
5
|
export declare function getSpineViewPlayback(view: Container, skeletonData: SkeletonData, animation: string): {
|
|
@@ -10,6 +10,10 @@ export declare function getSpineViewPlayback(view: Container, skeletonData: Skel
|
|
|
10
10
|
export declare function setSpineViewFrame(view: Container, frame: number, skeletonData: SkeletonData, animation: string): void;
|
|
11
11
|
/** Forces an editor-only button state; the node's serialized `enabled` and states stay untouched. */
|
|
12
12
|
export declare function setButtonViewState(view: Container, state: ButtonStateKey): void;
|
|
13
|
+
/** Forces an editor-only checkbox checked state; the node's serialized `defaultChecked` stays untouched. */
|
|
14
|
+
export declare function setCheckboxViewChecked(view: Container, checked: boolean): void;
|
|
15
|
+
/** Live-previews nine-slice border edits while dragging the canvas gizmo; commits only on release. */
|
|
16
|
+
export declare function previewNineSlice(view: Container, nineSlice: LayoutPadding): void;
|
|
13
17
|
/** Applies an editor-only slider preview value without touching the document. */
|
|
14
18
|
export declare function setSliderViewValue(view: Container, value: number): void;
|
|
15
19
|
/** Applies an editor-only progress preview without touching the document. */
|
package/dist/controls.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controls.d.ts","sourceRoot":"","sources":["../src/controls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"controls.d.ts","sourceRoot":"","sources":["../src/controls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAYpC,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CASnJ;AAED,kHAAkH;AAClH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CASrH;AAED,qGAAqG;AACrG,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAE/E;AAED,4GAA4G;AAC5G,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAE9E;AAED,sGAAsG;AACtG,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,GAAG,IAAI,CAEhF;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEvE;AAED,6EAA6E;AAC7E,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAElF;AAED,+FAA+F;AAC/F,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,CAG7E"}
|
package/dist/controls.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Spine } from "@esotericsoftware/spine-pixi-v8";
|
|
2
2
|
import { ButtonNodeView } from "./views/ButtonNodeView.js";
|
|
3
|
+
import { CheckboxNodeView } from "./views/CheckboxNodeView.js";
|
|
4
|
+
import { ImageNodeView } from "./views/basic.js";
|
|
3
5
|
import { NodeView } from "./views/NodeView.js";
|
|
4
6
|
import { ProgressBarNodeView, SliderNodeView } from "./views/ValueControlNodeViews.js";
|
|
5
7
|
function findSpineChild(view) {
|
|
@@ -36,6 +38,16 @@ export function setButtonViewState(view, state) {
|
|
|
36
38
|
if (view instanceof ButtonNodeView)
|
|
37
39
|
view.setState(state);
|
|
38
40
|
}
|
|
41
|
+
/** Forces an editor-only checkbox checked state; the node's serialized `defaultChecked` stays untouched. */
|
|
42
|
+
export function setCheckboxViewChecked(view, checked) {
|
|
43
|
+
if (view instanceof CheckboxNodeView)
|
|
44
|
+
view.forceCheck(checked);
|
|
45
|
+
}
|
|
46
|
+
/** Live-previews nine-slice border edits while dragging the canvas gizmo; commits only on release. */
|
|
47
|
+
export function previewNineSlice(view, nineSlice) {
|
|
48
|
+
if (view instanceof ImageNodeView || view instanceof ButtonNodeView)
|
|
49
|
+
view.previewNineSlice(nineSlice);
|
|
50
|
+
}
|
|
39
51
|
/** Applies an editor-only slider preview value without touching the document. */
|
|
40
52
|
export function setSliderViewValue(view, value) {
|
|
41
53
|
if (view instanceof SliderNodeView)
|
package/dist/controls.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controls.js","sourceRoot":"","sources":["../src/controls.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"controls.js","sourceRoot":"","sources":["../src/controls.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,10 +2,12 @@ export type { SkeletonData } from "@esotericsoftware/spine-pixi-v8";
|
|
|
2
2
|
export type { Sound } from "@pixi/sound";
|
|
3
3
|
export * from "./document.js";
|
|
4
4
|
export * from "./layout.js";
|
|
5
|
+
export { SceneViewport } from "./SceneViewport.js";
|
|
5
6
|
export * from "./layoutGroups.js";
|
|
6
7
|
export * from "./scrollView.js";
|
|
7
8
|
export * from "./views/NodeView.js";
|
|
8
9
|
export { ButtonNodeView } from "./views/ButtonNodeView.js";
|
|
10
|
+
export { CheckboxNodeView } from "./views/CheckboxNodeView.js";
|
|
9
11
|
export { InputNodeView } from "./views/InputNodeView.js";
|
|
10
12
|
export { ScrollViewNodeView } from "./views/ScrollViewNodeView.js";
|
|
11
13
|
export { SpineNodeView } from "./views/SpineNodeView.js";
|
|
@@ -13,9 +15,14 @@ export { ProgressBarNodeView, SliderNodeView } from "./views/ValueControlNodeVie
|
|
|
13
15
|
export { ParticleEmitterNodeView } from "./views/ParticleEmitterNodeView.js";
|
|
14
16
|
export * from "./particles.js";
|
|
15
17
|
export { collectNodeAssetIds } from "./views/createNodeView.js";
|
|
16
|
-
export
|
|
18
|
+
export { SceneLocalization } from "./localization.js";
|
|
19
|
+
export type { SceneLocalizationOptions } from "./localization.js";
|
|
20
|
+
export { ImageNodeView, MaskNodeView, TextNodeView } from "./views/basic.js";
|
|
21
|
+
export type { BuildSceneViewOptions, PrefabViewConfiguration, SceneViewResult } from "./scene.js";
|
|
17
22
|
export { buildPrefabView, buildSceneView, collectRenderedNodes, loadPrefabView, previewNodeView, updateNodeView, updateParticleEmitters, loadSceneView } from "./scene.js";
|
|
18
23
|
export * from "./controls.js";
|
|
24
|
+
export { createSceneAssetCache } from "./assets/cache.js";
|
|
25
|
+
export type { SceneAssetCache } from "./assets/cache.js";
|
|
19
26
|
export * from "./assets/textures.js";
|
|
20
27
|
export * from "./assets/spine.js";
|
|
21
28
|
export * from "./assets/fonts.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpE,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,YAAY,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpE,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,YAAY,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC7E,YAAY,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClG,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,oBAAoB,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3K,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export * from "./document.js";
|
|
2
2
|
export * from "./layout.js";
|
|
3
|
+
export { SceneViewport } from "./SceneViewport.js";
|
|
3
4
|
export * from "./layoutGroups.js";
|
|
4
5
|
export * from "./scrollView.js";
|
|
5
6
|
export * from "./views/NodeView.js";
|
|
6
7
|
export { ButtonNodeView } from "./views/ButtonNodeView.js";
|
|
8
|
+
export { CheckboxNodeView } from "./views/CheckboxNodeView.js";
|
|
7
9
|
export { InputNodeView } from "./views/InputNodeView.js";
|
|
8
10
|
export { ScrollViewNodeView } from "./views/ScrollViewNodeView.js";
|
|
9
11
|
export { SpineNodeView } from "./views/SpineNodeView.js";
|
|
@@ -11,8 +13,11 @@ export { ProgressBarNodeView, SliderNodeView } from "./views/ValueControlNodeVie
|
|
|
11
13
|
export { ParticleEmitterNodeView } from "./views/ParticleEmitterNodeView.js";
|
|
12
14
|
export * from "./particles.js";
|
|
13
15
|
export { collectNodeAssetIds } from "./views/createNodeView.js";
|
|
16
|
+
export { SceneLocalization } from "./localization.js";
|
|
17
|
+
export { ImageNodeView, MaskNodeView, TextNodeView } from "./views/basic.js";
|
|
14
18
|
export { buildPrefabView, buildSceneView, collectRenderedNodes, loadPrefabView, previewNodeView, updateNodeView, updateParticleEmitters, loadSceneView } from "./scene.js";
|
|
15
19
|
export * from "./controls.js";
|
|
20
|
+
export { createSceneAssetCache } from "./assets/cache.js";
|
|
16
21
|
export * from "./assets/textures.js";
|
|
17
22
|
export * from "./assets/spine.js";
|
|
18
23
|
export * from "./assets/fonts.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,oBAAoB,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3K,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC"}
|
package/dist/index.test.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
3
|
import { materializedPrefabNodeId, serializeProjectDocument } from "@pixi-ui-editor/schema";
|
|
4
|
-
import { Sprite, Texture } from "pixi.js";
|
|
4
|
+
import { Graphics, NineSliceSprite, Sprite, Texture } from "pixi.js";
|
|
5
5
|
import { TextureAtlas } from "@esotericsoftware/spine-pixi-v8";
|
|
6
|
-
import { assignAtlasPageTextures, buildPrefabView, buildSceneView, collectNodeAssetIds, fitSpineToTransform, getCanvasLogicalSize, getCanvasScale, parseProjectDocumentJson, ProjectDocumentJsonParseError, resolveAnchoredTransform, resolveProfileForViewport, resolveProfileTransform, resolveSpineSettings, resolveTextStyle } from "./index.js";
|
|
6
|
+
import { assignAtlasPageTextures, buildPrefabView, buildSceneView, collectNodeAssetIds, fitSpineToTransform, getCanvasLogicalSize, getCanvasScale, MaskNodeView, parseProjectDocumentJson, ProjectDocumentJsonParseError, resolveAnchoredTransform, resolveProfileForViewport, resolveProfileTransform, resolveSpineSettings, resolveTextStyle } from "./index.js";
|
|
7
7
|
import { createNodeView } from "./views/createNodeView.js";
|
|
8
8
|
import { ContainerNodeView } from "./views/basic.js";
|
|
9
9
|
const sampleUrl = new URL("../../../examples/sample-project/project.json", import.meta.url);
|
|
@@ -57,6 +57,7 @@ function documentWithButton() {
|
|
|
57
57
|
*/
|
|
58
58
|
const NODE_TYPE_FIXTURES = {
|
|
59
59
|
container: (base) => ({ ...base, type: "container" }),
|
|
60
|
+
mask: (base) => ({ ...base, type: "mask" }),
|
|
60
61
|
"horizontal-layout": (base) => ({ ...base, type: "horizontal-layout", layoutGroup: { base: { padding: { left: 0, right: 0, top: 0, bottom: 0 }, spacing: 0, childAlignment: "upper-left", reverseOrder: false, controlChildWidth: true, controlChildHeight: true, forceExpandWidth: false, forceExpandHeight: false } } }),
|
|
61
62
|
"vertical-layout": (base) => ({ ...base, type: "vertical-layout", layoutGroup: { base: { padding: { left: 0, right: 0, top: 0, bottom: 0 }, spacing: 0, childAlignment: "upper-left", reverseOrder: false, controlChildWidth: true, controlChildHeight: true, forceExpandWidth: false, forceExpandHeight: false } } }),
|
|
62
63
|
"grid-layout": (base) => ({ ...base, type: "grid-layout", layoutGroup: { base: { padding: { left: 0, right: 0, top: 0, bottom: 0 }, spacingX: 0, spacingY: 0, cellWidth: 20, cellHeight: 20, startCorner: "upper-left", startAxis: "horizontal", childAlignment: "upper-left", constraint: "flexible" } } }),
|
|
@@ -64,6 +65,7 @@ const NODE_TYPE_FIXTURES = {
|
|
|
64
65
|
text: (base) => ({ ...base, type: "text", text: "Label" }),
|
|
65
66
|
spine: (base) => ({ ...base, type: "spine", assetId: ids.asset }),
|
|
66
67
|
button: (base) => ({ ...base, type: "button", enabled: true, states: { normalAssetId: ids.asset } }),
|
|
68
|
+
checkbox: (base) => ({ ...base, type: "checkbox", enabled: true, defaultChecked: false, states: { uncheckedAssetId: ids.asset, checkedAssetId: ids.asset } }),
|
|
67
69
|
"prefab-instance": (base) => ({ ...base, type: "prefab-instance", prefabId: "40000000-0000-4000-8000-00000000000f" }),
|
|
68
70
|
"scroll-view": (base) => ({ ...base, type: "scroll-view", scrollView: { direction: "vertical", padding: { left: 0, right: 0, top: 0, bottom: 0 }, itemSpacing: 0, cornerRadius: 0, easingEnabled: true } }),
|
|
69
71
|
input: (base) => ({ ...base, type: "input", placeholder: "Enter text", defaultValue: "", secure: false, align: "left", padding: { left: 0, right: 0, top: 0, bottom: 0 }, cleanOnFocus: false, clipText: true, textStyle: { fontFamily: "Arial", fontSize: 24, fontWeight: "normal", fontStyle: "normal", fill: "#FFFFFF", align: "left", verticalAlign: "top", wordWrap: false, breakWords: false, letterSpacing: 0 } }),
|
|
@@ -105,10 +107,17 @@ describe("sample project loader smoke test", () => {
|
|
|
105
107
|
const sceneView = buildSceneView(document, scene.id, "desktop", { interaction: "runtime" });
|
|
106
108
|
expect(sceneView.nodeViews.has(materializedPrefabNodeId(instanceId, definitionNode.id))).toBe(true);
|
|
107
109
|
expect(sceneView.nodeViews.has(localId)).toBe(true);
|
|
110
|
+
const viewport = scene.layout.referenceViewports.desktop;
|
|
111
|
+
sceneView.root.resize({ width: viewport.width * 2, height: viewport.height * 3 });
|
|
112
|
+
expect(sceneView.root.scale.x).toBe(2);
|
|
113
|
+
expect(sceneView.root.scale.y).toBe(2);
|
|
114
|
+
expect(sceneView.root.position).toMatchObject({ x: 0, y: viewport.height / 2 });
|
|
115
|
+
expect(sceneView.root.content.mask).not.toBeNull();
|
|
108
116
|
const spawned = buildPrefabView(document, prefabId, "desktop", { interaction: "runtime" }, {
|
|
109
117
|
propertyOverrides: [{ nodeId: definitionNode.id, patch: { text: "Spawned" } }],
|
|
110
118
|
});
|
|
111
119
|
expect(spawned.nodeViews.size).toBeGreaterThan(1);
|
|
120
|
+
expect(spawned.root.content.mask).toBeUndefined();
|
|
112
121
|
});
|
|
113
122
|
it("matches atlas pages to texture files by name", () => {
|
|
114
123
|
const atlas = new TextureAtlas("second.png\nsize: 1,1\nformat: RGBA8888\nfilter: Linear,Linear\nrepeat: none\n\nfirst.png\nsize: 1,1\nformat: RGBA8888\nfilter: Linear,Linear\nrepeat: none\n");
|
|
@@ -265,6 +274,27 @@ describe("sample project loader smoke test", () => {
|
|
|
265
274
|
expect(grabbedInsideRect).toEqual(types.map((type) => [type, true]));
|
|
266
275
|
expect(missedOutsideRect).toEqual(types.map((type) => [type, false]));
|
|
267
276
|
});
|
|
277
|
+
it("clips a mask subtree to the resolved rectangular layout area and resizes the mask in place", () => {
|
|
278
|
+
const mask = {
|
|
279
|
+
id: "40000000-0000-4000-8000-000000000099",
|
|
280
|
+
name: "Mask",
|
|
281
|
+
type: "mask",
|
|
282
|
+
parentId: null,
|
|
283
|
+
children: [],
|
|
284
|
+
visible: true,
|
|
285
|
+
transform: { x: 0, y: 0, width: 120, height: 40, scaleX: 1, scaleY: 1, rotation: 0 },
|
|
286
|
+
};
|
|
287
|
+
const view = createNodeView(mask, "runtime");
|
|
288
|
+
view.update(mask, "desktop");
|
|
289
|
+
expect(view).toBeInstanceOf(MaskNodeView);
|
|
290
|
+
expect(view.mask).toBeInstanceOf(Graphics);
|
|
291
|
+
expect(view.children).toContain(view.mask);
|
|
292
|
+
expect(view.mask.getLocalBounds()).toMatchObject({ x: 0, y: 0, width: 120, height: 40 });
|
|
293
|
+
mask.transform.width = 75;
|
|
294
|
+
mask.transform.height = 25;
|
|
295
|
+
view.update(mask, "desktop");
|
|
296
|
+
expect(view.mask.getLocalBounds()).toMatchObject({ x: 0, y: 0, width: 75, height: 25 });
|
|
297
|
+
});
|
|
268
298
|
it("keeps a child grabbable where it reaches past its parent's rectangle", () => {
|
|
269
299
|
const document = renderDocument();
|
|
270
300
|
const scene = document.scenes[0];
|
|
@@ -301,6 +331,66 @@ describe("sample project loader smoke test", () => {
|
|
|
301
331
|
["disabled", Texture.WHITE],
|
|
302
332
|
]);
|
|
303
333
|
});
|
|
334
|
+
it("uses a NineSliceSprite for every button state once nineSlice is set, carrying the authored borders", () => {
|
|
335
|
+
const document = documentWithButton();
|
|
336
|
+
const buttonNode = document.scenes[0].nodes.find((node) => node.id === buttonIds.node);
|
|
337
|
+
buttonNode.nineSlice = { left: 6, top: 4, right: 6, bottom: 4 };
|
|
338
|
+
const textures = new Map([[ids.asset, Texture.WHITE], [buttonIds.pressedAsset, Texture.EMPTY]]);
|
|
339
|
+
const view = buildSceneView(document, ids.scene, "desktop", { interaction: "runtime", textures }).nodeViews.get(buttonIds.node);
|
|
340
|
+
const stateViews = [];
|
|
341
|
+
const walk = (container) => { if (container instanceof Sprite || container instanceof NineSliceSprite)
|
|
342
|
+
stateViews.push(container); container.children.forEach(walk); };
|
|
343
|
+
walk(view);
|
|
344
|
+
// normal/hover/disabled fall back to the normal asset; pressed has its own — all four are sliced.
|
|
345
|
+
expect(stateViews).toHaveLength(4);
|
|
346
|
+
expect(stateViews.every((child) => child instanceof NineSliceSprite)).toBe(true);
|
|
347
|
+
expect(stateViews.map((child) => [child.leftWidth, child.topHeight, child.rightWidth, child.bottomHeight])).toEqual([[6, 4, 6, 4], [6, 4, 6, 4], [6, 4, 6, 4], [6, 4, 6, 4]]);
|
|
348
|
+
});
|
|
349
|
+
it("renders a plain Sprite without nineSlice and a NineSliceSprite with it, carrying the authored borders", () => {
|
|
350
|
+
const document = renderDocument();
|
|
351
|
+
const textures = new Map([[ids.asset, Texture.WHITE]]);
|
|
352
|
+
const plain = buildSceneView(document, ids.scene, "desktop", { interaction: "runtime", textures }).nodeViews.get(ids.image);
|
|
353
|
+
expect(plain.children[0]).toBeInstanceOf(Sprite);
|
|
354
|
+
expect(plain.children[0]).not.toBeInstanceOf(NineSliceSprite);
|
|
355
|
+
const imageNode = document.scenes[0].nodes.find((node) => node.id === ids.image && node.type === "image");
|
|
356
|
+
imageNode.nineSlice = { left: 8, top: 6, right: 8, bottom: 6 };
|
|
357
|
+
const sliced = buildSceneView(document, ids.scene, "desktop", { interaction: "runtime", textures }).nodeViews.get(ids.image);
|
|
358
|
+
const sprite = sliced.children[0];
|
|
359
|
+
expect(sprite).toBeInstanceOf(NineSliceSprite);
|
|
360
|
+
expect(sprite).toMatchObject({ leftWidth: 8, topHeight: 6, rightWidth: 8, bottomHeight: 6 });
|
|
361
|
+
});
|
|
362
|
+
it("swaps a checkbox's shown texture on hover, independently for its checked and unchecked views", () => {
|
|
363
|
+
const document = renderDocument();
|
|
364
|
+
const scene = document.scenes[0];
|
|
365
|
+
const root = scene.nodes.find((node) => node.id === ids.root);
|
|
366
|
+
const checkboxId = "10000000-0000-4000-8000-000000000011";
|
|
367
|
+
const uncheckedHoverAsset = "10000000-0000-4000-8000-000000000012";
|
|
368
|
+
const checkedHoverAsset = "10000000-0000-4000-8000-000000000013";
|
|
369
|
+
document.assets.push({ id: uncheckedHoverAsset, name: "Unchecked hover", type: "image", source: { uri: "assets/unchecked-hover.png", mediaType: "image/png" } });
|
|
370
|
+
document.assets.push({ id: checkedHoverAsset, name: "Checked hover", type: "image", source: { uri: "assets/checked-hover.png", mediaType: "image/png" } });
|
|
371
|
+
scene.nodes.push({
|
|
372
|
+
id: checkboxId, name: "Agree", type: "checkbox", parentId: root.id, children: [], visible: true,
|
|
373
|
+
enabled: true, defaultChecked: false,
|
|
374
|
+
// Hover textures deliberately mirror the *other* base state, so a swap is unambiguous with only two Texture identities available offscreen.
|
|
375
|
+
states: { uncheckedAssetId: ids.asset, checkedAssetId: buttonIds.pressedAsset, uncheckedHoverAssetId: buttonIds.pressedAsset, checkedHoverAssetId: ids.asset },
|
|
376
|
+
transform: { x: 0, y: 0, width: 32, height: 32, scaleX: 1, scaleY: 1, rotation: 0 },
|
|
377
|
+
});
|
|
378
|
+
root.children.push(checkboxId);
|
|
379
|
+
const textures = new Map([[ids.asset, Texture.WHITE], [buttonIds.pressedAsset, Texture.EMPTY]]);
|
|
380
|
+
const view = buildSceneView(document, ids.scene, "desktop", { interaction: "runtime", textures }).nodeViews.get(checkboxId);
|
|
381
|
+
// Pixi's typed Container.emit requires a payload for known federated event names; the listener
|
|
382
|
+
// only reads the event name, so a loosely-typed emit is enough to exercise it here.
|
|
383
|
+
const innerView = view.children[0].innerView;
|
|
384
|
+
expect(shownStateTexture(view)).toBe(Texture.WHITE);
|
|
385
|
+
innerView.emit("pointerover");
|
|
386
|
+
expect(shownStateTexture(view)).toBe(Texture.EMPTY);
|
|
387
|
+
innerView.emit("pointerout");
|
|
388
|
+
expect(shownStateTexture(view)).toBe(Texture.WHITE);
|
|
389
|
+
view.checked = true;
|
|
390
|
+
expect(shownStateTexture(view)).toBe(Texture.EMPTY);
|
|
391
|
+
innerView.emit("pointerover");
|
|
392
|
+
expect(shownStateTexture(view)).toBe(Texture.WHITE);
|
|
393
|
+
});
|
|
304
394
|
it("stops dispatching pointer events to a disabled button without writing runtime state back to the document", () => {
|
|
305
395
|
const document = documentWithButton();
|
|
306
396
|
const view = buildSceneView(document, ids.scene, "desktop", { interaction: "runtime", textures: new Map([[ids.asset, Texture.WHITE]]) }).nodeViews.get(buttonIds.node);
|
|
@@ -351,7 +441,7 @@ describe("sample project loader smoke test", () => {
|
|
|
351
441
|
expect(scene.id).toBe(ids.scene);
|
|
352
442
|
expect(scene.rootNodeIds).toEqual([ids.root]);
|
|
353
443
|
expect(scene.nodes.find((node) => node.id === ids.image && node.type === "image")).toMatchObject({ assetId: ids.asset });
|
|
354
|
-
expect(scene.nodes.find((node) => node.id === ids.text)).toMatchObject({
|
|
444
|
+
expect(scene.nodes.find((node) => node.id === ids.text)).toMatchObject({ bindingKey: "showcase.title" });
|
|
355
445
|
expect(scene.layout.referenceViewports).toHaveProperty("desktop");
|
|
356
446
|
expect(scene.layout.referenceViewports).toHaveProperty("mobile");
|
|
357
447
|
});
|