@pixi-ui-editor/runtime-pixi 0.18.0 → 0.21.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/controls.d.ts +5 -0
- package/dist/controls.d.ts.map +1 -1
- package/dist/controls.js +9 -0
- package/dist/controls.js.map +1 -1
- package/dist/layout.d.ts +1 -0
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +1 -0
- package/dist/layout.js.map +1 -1
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +6 -3
- package/dist/scene.js.map +1 -1
- package/dist/views/ButtonNodeView.d.ts.map +1 -1
- package/dist/views/ButtonNodeView.js +12 -0
- package/dist/views/ButtonNodeView.js.map +1 -1
- package/dist/views/PaginationNodeView.d.ts +24 -23
- package/dist/views/PaginationNodeView.d.ts.map +1 -1
- package/dist/views/PaginationNodeView.js +32 -66
- package/dist/views/PaginationNodeView.js.map +1 -1
- package/dist/views/SpineNodeView.d.ts +1 -1
- package/dist/views/SpineNodeView.d.ts.map +1 -1
- package/dist/views/SpineNodeView.js +16 -2
- package/dist/views/SpineNodeView.js.map +1 -1
- package/dist/views/createNodeView.js +2 -2
- package/dist/views/createNodeView.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ButtonNodeView.js","sourceRoot":"","sources":["../../src/views/ButtonNodeView.ts"],"names":[],"mappings":"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","sourcesContent":["import { BUTTON_STATE_KEYS, type ButtonStateKey, type LayoutPadding, type UINode } from \"@pixi-ui-editor/schema\";\r\nimport { FancyButton } from \"@pixi/ui\";\r\nimport { Container, Graphics, NineSliceSprite, Sprite, Texture } from \"pixi.js\";\r\nimport { NodeView, type SceneInteractionMode } from \"./NodeView.js\";\r\nimport type { SceneAudio } from \"../audio/bus.js\";\r\n\r\nexport type ButtonNode = Extract<UINode, { type: \"button\" }>;\r\n\r\nconst FANCY_STATE_BY_KEY = { normal: \"default\", hover: \"hover\", pressed: \"pressed\", disabled: \"disabled\" } as const;\r\nconst KEY_BY_FANCY_STATE = { default: \"normal\", hover: \"hover\", pressed: \"pressed\", disabled: \"disabled\" } as const;\r\n\r\n/**\r\n * Adapts a button node to `@pixi/ui`'s `FancyButton`, which owns the pointer state machine —\r\n * this class never reimplements it. Optional states fall back to the normal image, and runtime\r\n * changes to `enabled` or the current state stay in the view: they never touch the document.\r\n *\r\n * `nineSlice`, when set, applies the same borders to all four state views (`NineSliceSprite`\r\n * instead of `Sprite`) — the button's frame shape is normally the same regardless of which state\r\n * image is currently shown.\r\n */\r\nexport class ButtonNodeView extends NodeView {\r\n private button!: FancyButton;\r\n private stateViews!: Record<ButtonStateKey, Container>;\r\n private readonly interaction: SceneInteractionMode;\r\n private readonly audio: SceneAudio | undefined;\r\n private readonly nodeId: string;\r\n private soundAssetIds: ButtonNode[\"sounds\"];\r\n private enabledState: boolean;\r\n\r\n constructor(node: ButtonNode, textures: ReadonlyMap<string, Texture> | undefined, interaction: SceneInteractionMode, audio?: SceneAudio) {\r\n super(textures);\r\n this.interaction = interaction;\r\n this.audio = audio;\r\n this.nodeId = node.id;\r\n this.soundAssetIds = node.sounds;\r\n this.enabledState = node.enabled;\r\n this.buildButton(node);\r\n this.applyEnabled(node.enabled);\r\n }\r\n\r\n /**\r\n * Invisible (alpha 0) but still hit-testable: Pixi's `containsPoint` hit-tests the fill's geometry\r\n * regardless of its alpha. Painted with a real size *before* the view ever reaches `FancyButton`:\r\n * `FancyButton` reads `defaultView`'s bounds exactly once, synchronously during construction, to\r\n * freeze its own `hitArea` — it never re-reads them afterwards. A `Graphics` still at its default\r\n * (0, 0) size at that moment would freeze a zero-size, permanently unclickable hit area.\r\n */\r\n private static paintPlaceholder(graphics: Graphics, width: number, height: number): void {\r\n graphics.clear().rect(0, 0, width, height).fill({ color: 0x000000, alpha: 0 });\r\n }\r\n\r\n private static createStateView(texture: Texture | undefined, nineSlice: LayoutPadding | undefined, size: { width: number; height: number }): Container {\r\n if (texture === undefined) {\r\n const graphics = new Graphics();\r\n ButtonNodeView.paintPlaceholder(graphics, size.width, size.height);\r\n return graphics;\r\n }\r\n return nineSlice === undefined\r\n ? new Sprite(texture)\r\n : new NineSliceSprite({ texture, leftWidth: nineSlice.left, topHeight: nineSlice.top, rightWidth: nineSlice.right, bottomHeight: nineSlice.bottom });\r\n }\r\n\r\n private static applyStateAppearance(view: Container, node: ButtonNode, state: ButtonStateKey): void {\r\n view.tint = node.states[`${state}Tint`] ?? \"#ffffff\";\r\n view.alpha = node.states[`${state}Opacity`] ?? 1;\r\n }\r\n\r\n /**\r\n * (Re)builds `FancyButton` and its four state views. `FancyButton` has no API to replace a view\r\n * after construction, so any change to a view's *type* — nine-slice toggled, or an asset\r\n * finishing an async load — goes through here instead of `syncContent`'s cheap in-place path.\r\n *\r\n * `size` is the best size known *right now*: the resolved layout rectangle when called from\r\n * `syncContent`, or the authored transform when called from the constructor (before any layout\r\n * pass has run). Only feeds the initial placeholder paint above — see its doc comment for why.\r\n */\r\n private buildButton(node: ButtonNode, size?: { width: number; height: number }): void {\r\n const nineSlice = node.nineSlice;\r\n const resolvedSize = size ?? { width: node.transform.width, height: node.transform.height };\r\n const normalTexture = this.textureFor(node.states.normalAssetId);\r\n const stateViews = {} as Record<ButtonStateKey, Container>;\r\n const viewFor = (state: ButtonStateKey, assetId: string | undefined): Container => {\r\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\r\n const view = ButtonNodeView.createStateView(texture, nineSlice, resolvedSize);\r\n ButtonNodeView.applyStateAppearance(view, node, state);\r\n stateViews[state] = view;\r\n return view;\r\n };\r\n\r\n const button = new FancyButton({\r\n defaultView: viewFor(\"normal\", node.states.normalAssetId),\r\n hoverView: viewFor(\"hover\", node.states.hoverAssetId),\r\n pressedView: viewFor(\"pressed\", node.states.pressedAssetId),\r\n disabledView: viewFor(\"disabled\", node.states.disabledAssetId),\r\n });\r\n // Authoring-сцена инертна: FancyButton уже включил себе eventMode \"static\", снимаем его до applyEnabled.\r\n // Выделение и drag при этом не страдают: попадание ловит hitArea базового NodeView, а не это поддерево.\r\n if (this.interaction === \"authoring\") button.eventMode = \"none\";\r\n else if (this.audio !== undefined) {\r\n // Вьюха только объявляет, что произошло: играть или нет — решает подписчик шины (см. audio/bus.ts).\r\n button.onPress.connect(() => this.audio?.cue(\"button.press\", this.nodeId, this.soundAssetIds?.pressAssetId));\r\n button.onHover.connect(() => this.audio?.cue(\"button.hover\", this.nodeId, this.soundAssetIds?.hoverAssetId));\r\n }\r\n this.button = button;\r\n this.stateViews = stateViews;\r\n this.setContent(button);\r\n }\r\n\r\n /** In authoring mode `enabled` may only drive the visuals: `FancyButton` couples it to `eventMode`. */\r\n private applyEnabled(enabled: boolean): void {\r\n this.enabledState = enabled;\r\n if (this.interaction === \"runtime\") this.button.enabled = enabled;\r\n else this.button.setState(enabled ? \"default\" : \"disabled\");\r\n }\r\n\r\n protected syncContent(node: UINode, transform: UINode[\"transform\"]): void {\r\n if (node.type !== \"button\") return;\r\n this.soundAssetIds = node.sounds;\r\n const nineSlice = node.nineSlice;\r\n const normalTexture = this.textureFor(node.states.normalAssetId);\r\n\r\n // A state view's required *type* (Graphics/Sprite/NineSliceSprite) may no longer match what's\r\n // needed — nine-slice toggled, or a texture finished loading after this view had to fall back\r\n // to Graphics. FancyButton can't swap a view after construction, so that goes through a full\r\n // rebuild; everything else (texture identity, border values, size) updates the live views in\r\n // place, which is the path a nine-slice gizmo drag runs every frame.\r\n let needsRebuild = false;\r\n for (const state of BUTTON_STATE_KEYS) {\r\n const assetId = node.states[`${state}AssetId`];\r\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\r\n const view = this.stateViews[state];\r\n const wantsNineSlice = texture !== undefined && nineSlice !== undefined;\r\n const matches = texture === undefined ? view instanceof Graphics : wantsNineSlice ? view instanceof NineSliceSprite : view instanceof Sprite && !(view instanceof NineSliceSprite);\r\n if (!matches) { needsRebuild = true; break; }\r\n }\r\n\r\n if (needsRebuild) {\r\n this.buildButton(node, transform);\r\n } else {\r\n for (const state of BUTTON_STATE_KEYS) {\r\n const view = this.stateViews[state];\r\n const assetId = node.states[`${state}AssetId`];\r\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\r\n // Set the texture before the layout size: Sprite/NineSliceSprite preserve scale when their\r\n // texture changes, so sizing first would shift visual bounds for a differently-sized source.\r\n if (view instanceof NineSliceSprite) {\r\n if (texture !== undefined && view.texture !== texture) view.texture = texture;\r\n if (nineSlice !== undefined) {\r\n view.leftWidth = nineSlice.left;\r\n view.topHeight = nineSlice.top;\r\n view.rightWidth = nineSlice.right;\r\n view.bottomHeight = nineSlice.bottom;\r\n }\r\n view.setSize(transform.width, transform.height);\r\n } else if (view instanceof Sprite) {\r\n if (texture !== undefined && view.texture !== texture) view.texture = texture;\r\n view.setSize(transform.width, transform.height);\r\n } else if (view instanceof Graphics) {\r\n ButtonNodeView.paintPlaceholder(view, transform.width, transform.height);\r\n }\r\n ButtonNodeView.applyStateAppearance(view, node, state);\r\n }\r\n }\r\n\r\n // Только на изменение документа: иначе любой пересчёт layout сбрасывал бы transient preview state.\r\n if (node.enabled !== this.enabledState) this.applyEnabled(node.enabled);\r\n }\r\n\r\n /** Editor-only live preview of nine-slice border edits while dragging the canvas gizmo. */\r\n previewNineSlice(nineSlice: LayoutPadding): void {\r\n for (const state of BUTTON_STATE_KEYS) {\r\n const view = this.stateViews[state];\r\n if (!(view instanceof NineSliceSprite)) continue;\r\n view.leftWidth = nineSlice.left;\r\n view.topHeight = nineSlice.top;\r\n view.rightWidth = nineSlice.right;\r\n view.bottomHeight = nineSlice.bottom;\r\n }\r\n }\r\n\r\n get enabled(): boolean {\r\n return this.enabledState;\r\n }\r\n\r\n set enabled(value: boolean) {\r\n this.applyEnabled(value);\r\n }\r\n\r\n get state(): ButtonStateKey {\r\n return KEY_BY_FANCY_STATE[this.button.state];\r\n }\r\n\r\n /** Forces a visual state without changing `enabled`, so a disabled button still emits no press. */\r\n setState(state: ButtonStateKey): void {\r\n this.button.setState(FANCY_STATE_BY_KEY[state]);\r\n }\r\n\r\n get onPress(): FancyButton[\"onPress\"] { return this.button.onPress; }\r\n get onDown(): FancyButton[\"onDown\"] { return this.button.onDown; }\r\n get onUp(): FancyButton[\"onUp\"] { return this.button.onUp; }\r\n get onHover(): FancyButton[\"onHover\"] { return this.button.onHover; }\r\n get onOut(): FancyButton[\"onOut\"] { return this.button.onOut; }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"ButtonNodeView.js","sourceRoot":"","sources":["../../src/views/ButtonNodeView.ts"],"names":[],"mappings":"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","sourcesContent":["import { BUTTON_STATE_KEYS, type ButtonStateKey, type LayoutPadding, type UINode } from \"@pixi-ui-editor/schema\";\r\nimport { FancyButton } from \"@pixi/ui\";\r\nimport { Container, Graphics, NineSliceSprite, Sprite, Texture } from \"pixi.js\";\r\nimport { NodeView, type SceneInteractionMode } from \"./NodeView.js\";\r\nimport type { SceneAudio } from \"../audio/bus.js\";\r\n\r\nexport type ButtonNode = Extract<UINode, { type: \"button\" }>;\r\n\r\nconst FANCY_STATE_BY_KEY = { normal: \"default\", hover: \"hover\", pressed: \"pressed\", disabled: \"disabled\" } as const;\r\nconst KEY_BY_FANCY_STATE = { default: \"normal\", hover: \"hover\", pressed: \"pressed\", disabled: \"disabled\" } as const;\r\n\r\n/**\r\n * Adapts a button node to `@pixi/ui`'s `FancyButton`, which owns the pointer state machine —\r\n * this class never reimplements it. Optional states fall back to the normal image, and runtime\r\n * changes to `enabled` or the current state stay in the view: they never touch the document.\r\n *\r\n * `nineSlice`, when set, applies the same borders to all four state views (`NineSliceSprite`\r\n * instead of `Sprite`) — the button's frame shape is normally the same regardless of which state\r\n * image is currently shown.\r\n */\r\nexport class ButtonNodeView extends NodeView {\r\n private button!: FancyButton;\r\n private stateViews!: Record<ButtonStateKey, Container>;\r\n private readonly interaction: SceneInteractionMode;\r\n private readonly audio: SceneAudio | undefined;\r\n private readonly nodeId: string;\r\n private soundAssetIds: ButtonNode[\"sounds\"];\r\n private enabledState: boolean;\r\n\r\n constructor(node: ButtonNode, textures: ReadonlyMap<string, Texture> | undefined, interaction: SceneInteractionMode, audio?: SceneAudio) {\r\n super(textures);\r\n this.interaction = interaction;\r\n this.audio = audio;\r\n this.nodeId = node.id;\r\n this.soundAssetIds = node.sounds;\r\n this.enabledState = node.enabled;\r\n this.buildButton(node);\r\n this.applyEnabled(node.enabled);\r\n }\r\n\r\n /**\r\n * Invisible (alpha 0) but still hit-testable: Pixi's `containsPoint` hit-tests the fill's geometry\r\n * regardless of its alpha. Painted with a real size *before* the view ever reaches `FancyButton`:\r\n * `FancyButton` reads `defaultView`'s bounds exactly once, synchronously during construction, to\r\n * freeze its own `hitArea` — it never re-reads them afterwards. A `Graphics` still at its default\r\n * (0, 0) size at that moment would freeze a zero-size, permanently unclickable hit area.\r\n */\r\n private static paintPlaceholder(graphics: Graphics, width: number, height: number): void {\r\n graphics.clear().rect(0, 0, width, height).fill({ color: 0x000000, alpha: 0 });\r\n }\r\n\r\n private static createStateView(texture: Texture | undefined, nineSlice: LayoutPadding | undefined, size: { width: number; height: number }): Container {\r\n if (texture === undefined) {\r\n const graphics = new Graphics();\r\n ButtonNodeView.paintPlaceholder(graphics, size.width, size.height);\r\n return graphics;\r\n }\r\n return nineSlice === undefined\r\n ? new Sprite(texture)\r\n : new NineSliceSprite({ texture, leftWidth: nineSlice.left, topHeight: nineSlice.top, rightWidth: nineSlice.right, bottomHeight: nineSlice.bottom });\r\n }\r\n\r\n private static applyStateAppearance(view: Container, node: ButtonNode, state: ButtonStateKey): void {\r\n view.tint = node.states[`${state}Tint`] ?? \"#ffffff\";\r\n view.alpha = node.states[`${state}Opacity`] ?? 1;\r\n }\r\n\r\n /**\r\n * (Re)builds `FancyButton` and its four state views. `FancyButton` has no API to replace a view\r\n * after construction, so any change to a view's *type* — nine-slice toggled, or an asset\r\n * finishing an async load — goes through here instead of `syncContent`'s cheap in-place path.\r\n *\r\n * `size` is the best size known *right now*: the resolved layout rectangle when called from\r\n * `syncContent`, or the authored transform when called from the constructor (before any layout\r\n * pass has run). Only feeds the initial placeholder paint above — see its doc comment for why.\r\n */\r\n private buildButton(node: ButtonNode, size?: { width: number; height: number }): void {\r\n // `FancyButton` always starts in `default`. An internal rebuild must stay invisible to hosts\r\n // and to the Inspector's transient state preview: otherwise rebuilding after a nine-slice or\r\n // asset-type change leaves the Inspector on (say) Hover while the canvas silently shows Normal.\r\n const previousState = this.button === undefined ? undefined : this.state;\r\n const nineSlice = node.nineSlice;\r\n const resolvedSize = size ?? { width: node.transform.width, height: node.transform.height };\r\n const normalTexture = this.textureFor(node.states.normalAssetId);\r\n const stateViews = {} as Record<ButtonStateKey, Container>;\r\n const viewFor = (state: ButtonStateKey, assetId: string | undefined): Container => {\r\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\r\n const view = ButtonNodeView.createStateView(texture, nineSlice, resolvedSize);\r\n ButtonNodeView.applyStateAppearance(view, node, state);\r\n stateViews[state] = view;\r\n return view;\r\n };\r\n\r\n const button = new FancyButton({\r\n defaultView: viewFor(\"normal\", node.states.normalAssetId),\r\n hoverView: viewFor(\"hover\", node.states.hoverAssetId),\r\n pressedView: viewFor(\"pressed\", node.states.pressedAssetId),\r\n disabledView: viewFor(\"disabled\", node.states.disabledAssetId),\r\n });\r\n // Authoring-сцена инертна: FancyButton уже включил себе eventMode \"static\", снимаем его до applyEnabled.\r\n // Выделение и drag при этом не страдают: попадание ловит hitArea базового NodeView, а не это поддерево.\r\n if (this.interaction === \"authoring\") button.eventMode = \"none\";\r\n else if (this.audio !== undefined) {\r\n // Вьюха только объявляет, что произошло: играть или нет — решает подписчик шины (см. audio/bus.ts).\r\n button.onPress.connect(() => this.audio?.cue(\"button.press\", this.nodeId, this.soundAssetIds?.pressAssetId));\r\n button.onHover.connect(() => this.audio?.cue(\"button.hover\", this.nodeId, this.soundAssetIds?.hoverAssetId));\r\n }\r\n this.button = button;\r\n this.stateViews = stateViews;\r\n this.setContent(button);\r\n if (previousState !== undefined) {\r\n this.applyEnabled(this.enabledState);\r\n this.button.setState(FANCY_STATE_BY_KEY[previousState]);\r\n }\r\n }\r\n\r\n /** In authoring mode `enabled` may only drive the visuals: `FancyButton` couples it to `eventMode`. */\r\n private applyEnabled(enabled: boolean): void {\r\n this.enabledState = enabled;\r\n if (this.interaction === \"runtime\") this.button.enabled = enabled;\r\n else this.button.setState(enabled ? \"default\" : \"disabled\");\r\n }\r\n\r\n protected syncContent(node: UINode, transform: UINode[\"transform\"]): void {\r\n if (node.type !== \"button\") return;\r\n this.soundAssetIds = node.sounds;\r\n const nineSlice = node.nineSlice;\r\n const normalTexture = this.textureFor(node.states.normalAssetId);\r\n\r\n // A state view's required *type* (Graphics/Sprite/NineSliceSprite) may no longer match what's\r\n // needed — nine-slice toggled, or a texture finished loading after this view had to fall back\r\n // to Graphics. FancyButton can't swap a view after construction, so that goes through a full\r\n // rebuild; everything else (texture identity, border values, size) updates the live views in\r\n // place, which is the path a nine-slice gizmo drag runs every frame.\r\n let needsRebuild = false;\r\n for (const state of BUTTON_STATE_KEYS) {\r\n const assetId = node.states[`${state}AssetId`];\r\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\r\n const view = this.stateViews[state];\r\n const wantsNineSlice = texture !== undefined && nineSlice !== undefined;\r\n const matches = texture === undefined ? view instanceof Graphics : wantsNineSlice ? view instanceof NineSliceSprite : view instanceof Sprite && !(view instanceof NineSliceSprite);\r\n if (!matches) { needsRebuild = true; break; }\r\n }\r\n\r\n if (needsRebuild) {\r\n this.buildButton(node, transform);\r\n } else {\r\n for (const state of BUTTON_STATE_KEYS) {\r\n const view = this.stateViews[state];\r\n const assetId = node.states[`${state}AssetId`];\r\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\r\n // Set the texture before the layout size: Sprite/NineSliceSprite preserve scale when their\r\n // texture changes, so sizing first would shift visual bounds for a differently-sized source.\r\n if (view instanceof NineSliceSprite) {\r\n if (texture !== undefined && view.texture !== texture) view.texture = texture;\r\n if (nineSlice !== undefined) {\r\n view.leftWidth = nineSlice.left;\r\n view.topHeight = nineSlice.top;\r\n view.rightWidth = nineSlice.right;\r\n view.bottomHeight = nineSlice.bottom;\r\n }\r\n view.setSize(transform.width, transform.height);\r\n } else if (view instanceof Sprite) {\r\n if (texture !== undefined && view.texture !== texture) view.texture = texture;\r\n view.setSize(transform.width, transform.height);\r\n } else if (view instanceof Graphics) {\r\n ButtonNodeView.paintPlaceholder(view, transform.width, transform.height);\r\n }\r\n ButtonNodeView.applyStateAppearance(view, node, state);\r\n }\r\n // Re-activate the current view after changing its appearance. Besides keeping FancyButton's\r\n // visibility bookkeeping authoritative, this invalidates the shown branch immediately when\r\n // Inspector color/opacity previews update an already-built button.\r\n this.button.setState(this.button.state, true);\r\n }\r\n\r\n // Только на изменение документа: иначе любой пересчёт layout сбрасывал бы transient preview state.\r\n if (node.enabled !== this.enabledState) this.applyEnabled(node.enabled);\r\n }\r\n\r\n /** Editor-only live preview of nine-slice border edits while dragging the canvas gizmo. */\r\n previewNineSlice(nineSlice: LayoutPadding): void {\r\n for (const state of BUTTON_STATE_KEYS) {\r\n const view = this.stateViews[state];\r\n if (!(view instanceof NineSliceSprite)) continue;\r\n view.leftWidth = nineSlice.left;\r\n view.topHeight = nineSlice.top;\r\n view.rightWidth = nineSlice.right;\r\n view.bottomHeight = nineSlice.bottom;\r\n }\r\n }\r\n\r\n get enabled(): boolean {\r\n return this.enabledState;\r\n }\r\n\r\n set enabled(value: boolean) {\r\n this.applyEnabled(value);\r\n }\r\n\r\n get state(): ButtonStateKey {\r\n return KEY_BY_FANCY_STATE[this.button.state];\r\n }\r\n\r\n /** Forces a visual state without changing `enabled`, so a disabled button still emits no press. */\r\n setState(state: ButtonStateKey): void {\r\n this.button.setState(FANCY_STATE_BY_KEY[state]);\r\n }\r\n\r\n get onPress(): FancyButton[\"onPress\"] { return this.button.onPress; }\r\n get onDown(): FancyButton[\"onDown\"] { return this.button.onDown; }\r\n get onUp(): FancyButton[\"onUp\"] { return this.button.onUp; }\r\n get onHover(): FancyButton[\"onHover\"] { return this.button.onHover; }\r\n get onOut(): FancyButton[\"onOut\"] { return this.button.onOut; }\r\n}\r\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type PaginationNode, type UINode } from "@pixi-ui-editor/schema";
|
|
2
|
-
import { Container,
|
|
2
|
+
import { Container, Text, Texture } from "pixi.js";
|
|
3
3
|
import { Signal } from "typed-signals";
|
|
4
4
|
import { NodeView, type SceneInteractionMode } from "./NodeView.js";
|
|
5
5
|
export type { PaginationNode };
|
|
@@ -15,28 +15,22 @@ export type PaginationArrow = {
|
|
|
15
15
|
enabled: boolean;
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
18
|
-
* One `numbers`-style item: a centered page-number label
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* One `numbers`-style item: a centered page-number label. `width`/`height` are overridden to the
|
|
19
|
+
* authored `dotSize` — the same fixed-footprint trick `ScrollItemContainer` uses — so the `List`
|
|
20
|
+
* spaces every item evenly. The active-page highlight is a separate authored node the widget only
|
|
21
|
+
* toggles `visible` on (see `PaginationNodeView.setActiveBackground`), never a child of this item.
|
|
22
22
|
*/
|
|
23
|
-
/** @internal Exported only so DOM-free render-contract tests can exercise the
|
|
23
|
+
/** @internal Exported only so DOM-free render-contract tests can exercise the footprint in isolation. */
|
|
24
24
|
export declare class NumberItem extends Container {
|
|
25
25
|
readonly numberText: Text;
|
|
26
26
|
private itemWidth;
|
|
27
27
|
private itemHeight;
|
|
28
|
-
private background?;
|
|
29
|
-
private backgroundMask?;
|
|
30
|
-
private backgroundCornerRadius?;
|
|
31
28
|
constructor(numberText: Text);
|
|
32
29
|
get width(): number;
|
|
33
30
|
set width(value: number);
|
|
34
31
|
get height(): number;
|
|
35
32
|
set height(value: number);
|
|
36
33
|
resize(width: number, height: number): void;
|
|
37
|
-
/** Replaces the pill behind the label (destroying the previous one); `undefined` removes it. */
|
|
38
|
-
setBackground(view: Sprite | NineSliceSprite | undefined, cornerRadius: number | undefined): void;
|
|
39
|
-
private syncBackgroundMask;
|
|
40
34
|
}
|
|
41
35
|
/**
|
|
42
36
|
* Standalone page indicator: N procedurally generated items arranged by `@pixi/ui`'s `List` (the same
|
|
@@ -47,12 +41,11 @@ export declare class NumberItem extends Container {
|
|
|
47
41
|
*
|
|
48
42
|
* `style` picks between two mutually exclusive, differently-rendered item kinds — image dots
|
|
49
43
|
* (`Sprite`, two states) or rendered page-number text (`NumberItem`: a label, base style + an optional
|
|
50
|
-
* highlight color
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* is not structural: switching pages only recreates the (one) background view that needs to move.
|
|
44
|
+
* highlight color) — never both. Switching `style` rebuilds every item, the same "view's required
|
|
45
|
+
* *type* changed" rule `StagedButtonNodeView` follows for its state views; `pageCount`/`dotSize`
|
|
46
|
+
* changes do too, since the item count/footprint is a runtime fact about this control, not scene
|
|
47
|
+
* topology — it only rebuilds this view's own item children, never the owning `NodeView` or the wider
|
|
48
|
+
* scene tree.
|
|
56
49
|
*
|
|
57
50
|
* With `visiblePageCount` set, the indicator becomes a fixed strip of that many slots with the active
|
|
58
51
|
* page always in the middle one (4 pages, page 3 active → 2-3-4). A slot pointing outside the real
|
|
@@ -60,7 +53,10 @@ export declare class NumberItem extends Container {
|
|
|
60
53
|
*
|
|
61
54
|
* The prev/next arrows are ordinary authored button nodes placed wherever the layout needs them; the
|
|
62
55
|
* widget only takes their views (`scene.ts`'s `wireNodeReferences`, the same address → view resolution
|
|
63
|
-
* Spine connector targets use) and steps the page on press. It never draws an arrow itself.
|
|
56
|
+
* Spine connector targets use) and steps the page on press. It never draws an arrow itself. The
|
|
57
|
+
* `numbers`-style active-page highlight follows the same reference-not-owned contract: it is an
|
|
58
|
+
* authored node the widget only toggles `visible` on while `style: "numbers"` — the node carries its
|
|
59
|
+
* own image / nine-slice / rounded corners / size / position, none of it configured here.
|
|
64
60
|
*/
|
|
65
61
|
export declare class PaginationNodeView extends NodeView {
|
|
66
62
|
private readonly list;
|
|
@@ -74,9 +70,8 @@ export declare class PaginationNodeView extends NodeView {
|
|
|
74
70
|
private inactiveTexture;
|
|
75
71
|
private numberStyle;
|
|
76
72
|
private numberActiveColor;
|
|
77
|
-
|
|
78
|
-
private
|
|
79
|
-
private numberActiveCornerRadius;
|
|
73
|
+
/** The authored highlight node; the widget owns only its `visible` flag while `style: "numbers"`. */
|
|
74
|
+
private activeBackground;
|
|
80
75
|
private pageCountState;
|
|
81
76
|
private currentPage;
|
|
82
77
|
private dotWidth;
|
|
@@ -85,7 +80,6 @@ export declare class PaginationNodeView extends NodeView {
|
|
|
85
80
|
/** Fires only from a click on an item (when `clickable`); setting `page` programmatically never emits. */
|
|
86
81
|
readonly onPageChange: Signal<(page: number) => void>;
|
|
87
82
|
constructor(node: PaginationNode, textures: ReadonlyMap<string, Texture> | undefined, interaction: SceneInteractionMode, fonts: ReadonlyMap<string, string> | undefined);
|
|
88
|
-
private createBackgroundView;
|
|
89
83
|
/** How many indicator slots are drawn: the fixed authored window, or one slot per real page. */
|
|
90
84
|
private get slotCount();
|
|
91
85
|
/** The page a slot points at. Windowed slots slide with the current page, which keeps its middle seat. */
|
|
@@ -107,6 +101,13 @@ export declare class PaginationNodeView extends NodeView {
|
|
|
107
101
|
}): void;
|
|
108
102
|
/** На краях диапазона соответствующая стрелка недоступна — она никуда не ведёт. */
|
|
109
103
|
private applyArrowState;
|
|
104
|
+
/**
|
|
105
|
+
* Связывает виджет с авторской нодой-подсветкой активной страницы. Как и стрелки, нода остаётся
|
|
106
|
+
* обычным узлом сцены со своим визуалом — виджет лишь владеет её флагом `visible`: показывает при
|
|
107
|
+
* `style: "numbers"` (активная страница есть всегда) и прячет в стиле `dots`.
|
|
108
|
+
*/
|
|
109
|
+
setActiveBackground(view: Container | undefined): void;
|
|
110
|
+
private applyActiveBackgroundVisibility;
|
|
110
111
|
protected syncContent(node: UINode): void;
|
|
111
112
|
get pageCount(): number;
|
|
112
113
|
/** Used by `setPaginationViewPageCount` once game code knows the real content length. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PaginationNodeView.d.ts","sourceRoot":"","sources":["../../src/views/PaginationNodeView.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"PaginationNodeView.d.ts","sourceRoot":"","sources":["../../src/views/PaginationNodeView.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { List } from "@pixi/ui";
|
|
2
|
-
import { Container,
|
|
2
|
+
import { Container, Sprite, Text, Texture } from "pixi.js";
|
|
3
3
|
import { Signal } from "typed-signals";
|
|
4
4
|
import { NodeView } from "./NodeView.js";
|
|
5
5
|
const DEFAULT_NUMBER_STYLE = { fontFamily: "Arial", fontSize: 18, fontWeight: "normal", fontStyle: "normal", fill: "#FFFFFF", align: "center", verticalAlign: "middle", wordWrap: false, breakWords: false, letterSpacing: 0 };
|
|
@@ -26,19 +26,16 @@ function clampPage(page, pageCount) {
|
|
|
26
26
|
return Math.min(Math.max(0, Math.round(page)), Math.max(0, pageCount - 1));
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
* One `numbers`-style item: a centered page-number label
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* One `numbers`-style item: a centered page-number label. `width`/`height` are overridden to the
|
|
30
|
+
* authored `dotSize` — the same fixed-footprint trick `ScrollItemContainer` uses — so the `List`
|
|
31
|
+
* spaces every item evenly. The active-page highlight is a separate authored node the widget only
|
|
32
|
+
* toggles `visible` on (see `PaginationNodeView.setActiveBackground`), never a child of this item.
|
|
33
33
|
*/
|
|
34
|
-
/** @internal Exported only so DOM-free render-contract tests can exercise the
|
|
34
|
+
/** @internal Exported only so DOM-free render-contract tests can exercise the footprint in isolation. */
|
|
35
35
|
export class NumberItem extends Container {
|
|
36
36
|
numberText;
|
|
37
37
|
itemWidth = 0;
|
|
38
38
|
itemHeight = 0;
|
|
39
|
-
background;
|
|
40
|
-
backgroundMask;
|
|
41
|
-
backgroundCornerRadius;
|
|
42
39
|
// Named `numberText`, not `label`: `Container.label` is pixi.js's own display-object name string.
|
|
43
40
|
constructor(numberText) {
|
|
44
41
|
super();
|
|
@@ -54,39 +51,6 @@ export class NumberItem extends Container {
|
|
|
54
51
|
this.itemWidth = width;
|
|
55
52
|
this.itemHeight = height;
|
|
56
53
|
this.numberText.position.set(width / 2, height / 2);
|
|
57
|
-
this.background?.setSize(width, height);
|
|
58
|
-
this.syncBackgroundMask();
|
|
59
|
-
}
|
|
60
|
-
/** Replaces the pill behind the label (destroying the previous one); `undefined` removes it. */
|
|
61
|
-
setBackground(view, cornerRadius) {
|
|
62
|
-
if (this.background !== undefined) {
|
|
63
|
-
super.removeChild(this.background);
|
|
64
|
-
this.background.destroy();
|
|
65
|
-
}
|
|
66
|
-
if (this.backgroundMask !== undefined) {
|
|
67
|
-
super.removeChild(this.backgroundMask);
|
|
68
|
-
this.backgroundMask.destroy();
|
|
69
|
-
this.backgroundMask = undefined;
|
|
70
|
-
}
|
|
71
|
-
this.background = view;
|
|
72
|
-
this.backgroundCornerRadius = cornerRadius;
|
|
73
|
-
if (view === undefined)
|
|
74
|
-
return;
|
|
75
|
-
view.setSize(this.itemWidth, this.itemHeight);
|
|
76
|
-
super.addChildAt(view, 0);
|
|
77
|
-
if (cornerRadius !== undefined) {
|
|
78
|
-
this.backgroundMask = new Graphics();
|
|
79
|
-
this.syncBackgroundMask();
|
|
80
|
-
// Keep the mask before the background in this item's render tree, as SceneViewport does.
|
|
81
|
-
super.addChildAt(this.backgroundMask, 0);
|
|
82
|
-
view.mask = this.backgroundMask;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
syncBackgroundMask() {
|
|
86
|
-
if (this.backgroundMask === undefined)
|
|
87
|
-
return;
|
|
88
|
-
const radius = Math.min(this.backgroundCornerRadius ?? 0, this.itemWidth / 2, this.itemHeight / 2);
|
|
89
|
-
this.backgroundMask.clear().roundRect(0, 0, this.itemWidth, this.itemHeight, radius).fill(0xffffff);
|
|
90
54
|
}
|
|
91
55
|
}
|
|
92
56
|
/**
|
|
@@ -98,12 +62,11 @@ export class NumberItem extends Container {
|
|
|
98
62
|
*
|
|
99
63
|
* `style` picks between two mutually exclusive, differently-rendered item kinds — image dots
|
|
100
64
|
* (`Sprite`, two states) or rendered page-number text (`NumberItem`: a label, base style + an optional
|
|
101
|
-
* highlight color
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* is not structural: switching pages only recreates the (one) background view that needs to move.
|
|
65
|
+
* highlight color) — never both. Switching `style` rebuilds every item, the same "view's required
|
|
66
|
+
* *type* changed" rule `StagedButtonNodeView` follows for its state views; `pageCount`/`dotSize`
|
|
67
|
+
* changes do too, since the item count/footprint is a runtime fact about this control, not scene
|
|
68
|
+
* topology — it only rebuilds this view's own item children, never the owning `NodeView` or the wider
|
|
69
|
+
* scene tree.
|
|
107
70
|
*
|
|
108
71
|
* With `visiblePageCount` set, the indicator becomes a fixed strip of that many slots with the active
|
|
109
72
|
* page always in the middle one (4 pages, page 3 active → 2-3-4). A slot pointing outside the real
|
|
@@ -111,7 +74,10 @@ export class NumberItem extends Container {
|
|
|
111
74
|
*
|
|
112
75
|
* The prev/next arrows are ordinary authored button nodes placed wherever the layout needs them; the
|
|
113
76
|
* widget only takes their views (`scene.ts`'s `wireNodeReferences`, the same address → view resolution
|
|
114
|
-
* Spine connector targets use) and steps the page on press. It never draws an arrow itself.
|
|
77
|
+
* Spine connector targets use) and steps the page on press. It never draws an arrow itself. The
|
|
78
|
+
* `numbers`-style active-page highlight follows the same reference-not-owned contract: it is an
|
|
79
|
+
* authored node the widget only toggles `visible` on while `style: "numbers"` — the node carries its
|
|
80
|
+
* own image / nine-slice / rounded corners / size / position, none of it configured here.
|
|
115
81
|
*/
|
|
116
82
|
export class PaginationNodeView extends NodeView {
|
|
117
83
|
list;
|
|
@@ -125,9 +91,8 @@ export class PaginationNodeView extends NodeView {
|
|
|
125
91
|
inactiveTexture;
|
|
126
92
|
numberStyle;
|
|
127
93
|
numberActiveColor;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
numberActiveCornerRadius;
|
|
94
|
+
/** The authored highlight node; the widget owns only its `visible` flag while `style: "numbers"`. */
|
|
95
|
+
activeBackground;
|
|
131
96
|
pageCountState;
|
|
132
97
|
currentPage;
|
|
133
98
|
dotWidth;
|
|
@@ -144,9 +109,6 @@ export class PaginationNodeView extends NodeView {
|
|
|
144
109
|
this.inactiveTexture = node.inactiveAssetId === undefined ? Texture.WHITE : this.textureFor(node.inactiveAssetId) ?? Texture.WHITE;
|
|
145
110
|
this.numberStyle = node.numberStyle;
|
|
146
111
|
this.numberActiveColor = node.numberActiveColor;
|
|
147
|
-
this.numberActiveBackgroundTexture = node.numberActiveBackgroundAssetId === undefined ? undefined : this.textureFor(node.numberActiveBackgroundAssetId);
|
|
148
|
-
this.numberActiveNineSlice = node.numberActiveNineSlice;
|
|
149
|
-
this.numberActiveCornerRadius = node.numberActiveNineSlice === undefined ? node.numberActiveCornerRadius : undefined;
|
|
150
112
|
this.pageCountState = Math.max(1, Math.round(node.pageCount));
|
|
151
113
|
this.visibleSlots = node.visiblePageCount;
|
|
152
114
|
this.currentPage = clampPage(node.defaultPage, this.pageCount);
|
|
@@ -157,11 +119,6 @@ export class PaginationNodeView extends NodeView {
|
|
|
157
119
|
this.buildItems();
|
|
158
120
|
this.setContent(this.list);
|
|
159
121
|
}
|
|
160
|
-
createBackgroundView(texture) {
|
|
161
|
-
return this.numberActiveNineSlice === undefined
|
|
162
|
-
? new Sprite(texture)
|
|
163
|
-
: new NineSliceSprite({ texture, leftWidth: this.numberActiveNineSlice.left, topHeight: this.numberActiveNineSlice.top, rightWidth: this.numberActiveNineSlice.right, bottomHeight: this.numberActiveNineSlice.bottom });
|
|
164
|
-
}
|
|
165
122
|
/** How many indicator slots are drawn: the fixed authored window, or one slot per real page. */
|
|
166
123
|
get slotCount() {
|
|
167
124
|
return this.visibleSlots === undefined ? this.pageCount : Math.max(1, Math.round(this.visibleSlots));
|
|
@@ -185,8 +142,6 @@ export class PaginationNodeView extends NodeView {
|
|
|
185
142
|
const fill = isActive ? this.numberActiveColor ?? baseFill : baseFill;
|
|
186
143
|
const item = new NumberItem(new Text({ text: exists ? String(page + 1) : "", style: numberTextStyle(this.numberStyle, fill, this.fonts) }));
|
|
187
144
|
item.resize(this.dotWidth, this.dotHeight);
|
|
188
|
-
if (isActive && this.numberActiveBackgroundTexture !== undefined)
|
|
189
|
-
item.setBackground(this.createBackgroundView(this.numberActiveBackgroundTexture), this.numberActiveCornerRadius);
|
|
190
145
|
return item;
|
|
191
146
|
}
|
|
192
147
|
/** Recreates every item for the current `styleMode`/`slotCount`/`dotWidth`/`dotHeight`. */
|
|
@@ -225,7 +180,6 @@ export class PaginationNodeView extends NodeView {
|
|
|
225
180
|
if (item instanceof NumberItem) {
|
|
226
181
|
item.numberText.text = exists ? String(page + 1) : "";
|
|
227
182
|
item.numberText.style = numberTextStyle(this.numberStyle, isActive ? this.numberActiveColor ?? baseFill : baseFill, this.fonts);
|
|
228
|
-
item.setBackground(isActive && this.numberActiveBackgroundTexture !== undefined ? this.createBackgroundView(this.numberActiveBackgroundTexture) : undefined, this.numberActiveCornerRadius);
|
|
229
183
|
return;
|
|
230
184
|
}
|
|
231
185
|
item.texture = isActive ? this.activeTexture : this.inactiveTexture;
|
|
@@ -254,6 +208,20 @@ export class PaginationNodeView extends NodeView {
|
|
|
254
208
|
if (this.arrows.next !== undefined)
|
|
255
209
|
this.arrows.next.enabled = this.currentPage < this.pageCount - 1;
|
|
256
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* Связывает виджет с авторской нодой-подсветкой активной страницы. Как и стрелки, нода остаётся
|
|
213
|
+
* обычным узлом сцены со своим визуалом — виджет лишь владеет её флагом `visible`: показывает при
|
|
214
|
+
* `style: "numbers"` (активная страница есть всегда) и прячет в стиле `dots`.
|
|
215
|
+
*/
|
|
216
|
+
setActiveBackground(view) {
|
|
217
|
+
this.activeBackground = view;
|
|
218
|
+
this.applyActiveBackgroundVisibility();
|
|
219
|
+
}
|
|
220
|
+
applyActiveBackgroundVisibility() {
|
|
221
|
+
if (this.activeBackground === undefined)
|
|
222
|
+
return;
|
|
223
|
+
this.activeBackground.visible = this.styleMode === "numbers";
|
|
224
|
+
}
|
|
257
225
|
syncContent(node) {
|
|
258
226
|
if (node.type !== "pagination")
|
|
259
227
|
return;
|
|
@@ -270,9 +238,7 @@ export class PaginationNodeView extends NodeView {
|
|
|
270
238
|
this.inactiveTexture = inactive;
|
|
271
239
|
this.numberStyle = node.numberStyle;
|
|
272
240
|
this.numberActiveColor = node.numberActiveColor;
|
|
273
|
-
this.
|
|
274
|
-
this.numberActiveNineSlice = node.numberActiveNineSlice;
|
|
275
|
-
this.numberActiveCornerRadius = node.numberActiveNineSlice === undefined ? node.numberActiveCornerRadius : undefined;
|
|
241
|
+
this.applyActiveBackgroundVisibility();
|
|
276
242
|
this.dotWidth = node.dotSize.width;
|
|
277
243
|
this.dotHeight = node.dotSize.height;
|
|
278
244
|
this.currentPage = clampPage(this.currentPage, this.pageCount);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PaginationNodeView.js","sourceRoot":"","sources":["../../src/views/PaginationNodeView.ts"],"names":[],"mappings":"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","sourcesContent":["import { type LayoutPadding, type PaginationNode, type TextStyleDefinition, type UINode } from \"@pixi-ui-editor/schema\";\nimport { List } from \"@pixi/ui\";\nimport { Container, Graphics, NineSliceSprite, Sprite, Text, Texture, type TextStyleOptions } from \"pixi.js\";\nimport { Signal } from \"typed-signals\";\nimport { NodeView, type SceneInteractionMode } from \"./NodeView.js\";\n\nexport type { PaginationNode };\n\nconst DEFAULT_NUMBER_STYLE: TextStyleDefinition = { fontFamily: \"Arial\", fontSize: 18, fontWeight: \"normal\", fontStyle: \"normal\", fill: \"#FFFFFF\", align: \"center\", verticalAlign: \"middle\", wordWrap: false, breakWords: false, letterSpacing: 0 };\n\n/** Same shape `ValueControlNodeViews.ts`'s own `textStyle` resolves — kept as its own small copy rather\n * than shared, the same call ADR 0029 makes for two similar-but-separately-evolving style resolvers. */\nfunction numberTextStyle(style: TextStyleDefinition | undefined, fill: string, fonts: ReadonlyMap<string, string> | undefined): Partial<TextStyleOptions> {\n const resolved = style ?? DEFAULT_NUMBER_STYLE;\n return {\n fontFamily: resolved.fontAssetId === undefined ? resolved.fontFamily : fonts?.get(resolved.fontAssetId) ?? resolved.fontFamily,\n fontSize: resolved.fontSize,\n fontWeight: resolved.fontWeight,\n fontStyle: resolved.fontStyle,\n fill,\n align: resolved.align,\n wordWrap: resolved.wordWrap,\n breakWords: resolved.breakWords,\n lineHeight: resolved.lineHeight,\n letterSpacing: resolved.letterSpacing,\n stroke: resolved.stroke === undefined ? undefined : { color: resolved.stroke.color, width: resolved.stroke.width },\n };\n}\n\n/**\n * Кнопка-стрелка пагинатора: ровно та часть `ButtonNodeView`/`StagedButtonNodeView`, которая нужна\n * виджету, — нажатие и «доступна ли». Тип объявлен структурно, чтобы этот файл не зависел от обоих\n * классов кнопок и не решал, какой из них «настоящий».\n */\nexport type PaginationArrow = { onPress: { connect(handler: () => void): unknown }; enabled: boolean };\n\n/** Clamps a page index into the valid `[0, pageCount)` range; an empty widget still shows dot 0. */\nfunction clampPage(page: number, pageCount: number): number {\n return Math.min(Math.max(0, Math.round(page)), Math.max(0, pageCount - 1));\n}\n\n/**\n * One `numbers`-style item: a centered page-number label, plus an optional \"pill\" background shown\n * only while it is the current page. `width`/`height` are overridden to the authored `dotSize` — the\n * same fixed-footprint trick `ScrollItemContainer` uses — so the `List` spaces every item evenly\n * whether or not its (rarely-present) background happens to be attached right now.\n */\n/** @internal Exported only so DOM-free render-contract tests can exercise the background in isolation. */\nexport class NumberItem extends Container {\n private itemWidth = 0;\n private itemHeight = 0;\n private background?: Sprite | NineSliceSprite;\n private backgroundMask?: Graphics;\n private backgroundCornerRadius?: number;\n\n // Named `numberText`, not `label`: `Container.label` is pixi.js's own display-object name string.\n constructor(readonly numberText: Text) {\n super();\n numberText.anchor.set(0.5);\n super.addChild(numberText);\n }\n\n override get width(): number { return this.itemWidth; }\n override set width(value: number) { this.itemWidth = value; }\n override get height(): number { return this.itemHeight; }\n override set height(value: number) { this.itemHeight = value; }\n\n resize(width: number, height: number): void {\n this.itemWidth = width;\n this.itemHeight = height;\n this.numberText.position.set(width / 2, height / 2);\n this.background?.setSize(width, height);\n this.syncBackgroundMask();\n }\n\n /** Replaces the pill behind the label (destroying the previous one); `undefined` removes it. */\n setBackground(view: Sprite | NineSliceSprite | undefined, cornerRadius: number | undefined): void {\n if (this.background !== undefined) { super.removeChild(this.background); this.background.destroy(); }\n if (this.backgroundMask !== undefined) { super.removeChild(this.backgroundMask); this.backgroundMask.destroy(); this.backgroundMask = undefined; }\n this.background = view;\n this.backgroundCornerRadius = cornerRadius;\n if (view === undefined) return;\n view.setSize(this.itemWidth, this.itemHeight);\n super.addChildAt(view, 0);\n if (cornerRadius !== undefined) {\n this.backgroundMask = new Graphics();\n this.syncBackgroundMask();\n // Keep the mask before the background in this item's render tree, as SceneViewport does.\n super.addChildAt(this.backgroundMask, 0);\n view.mask = this.backgroundMask;\n }\n }\n\n private syncBackgroundMask(): void {\n if (this.backgroundMask === undefined) return;\n const radius = Math.min(this.backgroundCornerRadius ?? 0, this.itemWidth / 2, this.itemHeight / 2);\n this.backgroundMask.clear().roundRect(0, 0, this.itemWidth, this.itemHeight, radius).fill(0xffffff);\n }\n}\n\n/**\n * Standalone page indicator: N procedurally generated items arranged by `@pixi/ui`'s `List` (the same\n * approach `ScrollViewNodeView`/`scrollView.ts` use for arranging children) — this view never\n * reimplements arrangement. Deliberately not wired to any scroll-view: game code owns the actual page\n * transition and drives the widget one-way through `setPaginationViewPage`/`setPaginationViewPageCount`,\n * the same relationship a slider or progress bar has to whatever value it displays.\n *\n * `style` picks between two mutually exclusive, differently-rendered item kinds — image dots\n * (`Sprite`, two states) or rendered page-number text (`NumberItem`: a label, base style + an optional\n * highlight color, plus an optional pill background behind only the current page) — never both.\n * Switching `style` rebuilds every item, the same \"view's required *type* changed\" rule\n * `StagedButtonNodeView` follows for its state views; `pageCount`/`dotSize` changes do too, since the\n * item count/footprint is a runtime fact about this control, not scene topology — it only rebuilds this\n * view's own item children, never the owning `NodeView` or the wider scene tree. The active pill itself\n * is not structural: switching pages only recreates the (one) background view that needs to move.\n *\n * With `visiblePageCount` set, the indicator becomes a fixed strip of that many slots with the active\n * page always in the middle one (4 pages, page 3 active → 2-3-4). A slot pointing outside the real\n * page range keeps its footprint and renders nothing, so the active page never shifts sideways.\n *\n * The prev/next arrows are ordinary authored button nodes placed wherever the layout needs them; the\n * widget only takes their views (`scene.ts`'s `wireNodeReferences`, the same address → view resolution\n * Spine connector targets use) and steps the page on press. It never draws an arrow itself.\n */\nexport class PaginationNodeView extends NodeView {\n private readonly list: List;\n private readonly interaction: SceneInteractionMode;\n private readonly fonts: ReadonlyMap<string, string> | undefined;\n private items: (Sprite | NumberItem)[] = [];\n private visibleSlots: number | undefined;\n private arrows?: { previous?: PaginationArrow; next?: PaginationArrow };\n private styleMode: PaginationNode[\"style\"];\n private activeTexture: Texture;\n private inactiveTexture: Texture;\n private numberStyle: TextStyleDefinition | undefined;\n private numberActiveColor: string | undefined;\n private numberActiveBackgroundTexture: Texture | undefined;\n private numberActiveNineSlice: LayoutPadding | undefined;\n private numberActiveCornerRadius: number | undefined;\n private pageCountState: number;\n private currentPage: number;\n private dotWidth: number;\n private dotHeight: number;\n private clickableState: boolean;\n\n /** Fires only from a click on an item (when `clickable`); setting `page` programmatically never emits. */\n readonly onPageChange = new Signal<(page: number) => void>();\n\n constructor(node: PaginationNode, textures: ReadonlyMap<string, Texture> | undefined, interaction: SceneInteractionMode, fonts: ReadonlyMap<string, string> | undefined) {\n super(textures);\n this.interaction = interaction;\n this.fonts = fonts;\n this.styleMode = node.style;\n this.activeTexture = node.activeAssetId === undefined ? Texture.WHITE : this.textureFor(node.activeAssetId) ?? Texture.WHITE;\n this.inactiveTexture = node.inactiveAssetId === undefined ? Texture.WHITE : this.textureFor(node.inactiveAssetId) ?? Texture.WHITE;\n this.numberStyle = node.numberStyle;\n this.numberActiveColor = node.numberActiveColor;\n this.numberActiveBackgroundTexture = node.numberActiveBackgroundAssetId === undefined ? undefined : this.textureFor(node.numberActiveBackgroundAssetId);\n this.numberActiveNineSlice = node.numberActiveNineSlice;\n this.numberActiveCornerRadius = node.numberActiveNineSlice === undefined ? node.numberActiveCornerRadius : undefined;\n this.pageCountState = Math.max(1, Math.round(node.pageCount));\n this.visibleSlots = node.visiblePageCount;\n this.currentPage = clampPage(node.defaultPage, this.pageCount);\n this.dotWidth = node.dotSize.width;\n this.dotHeight = node.dotSize.height;\n this.clickableState = node.clickable;\n this.list = new List({ type: node.direction, elementsMargin: node.spacing });\n this.buildItems();\n this.setContent(this.list);\n }\n\n private createBackgroundView(texture: Texture): Sprite | NineSliceSprite {\n return this.numberActiveNineSlice === undefined\n ? new Sprite(texture)\n : new NineSliceSprite({ texture, leftWidth: this.numberActiveNineSlice.left, topHeight: this.numberActiveNineSlice.top, rightWidth: this.numberActiveNineSlice.right, bottomHeight: this.numberActiveNineSlice.bottom });\n }\n\n /** How many indicator slots are drawn: the fixed authored window, or one slot per real page. */\n private get slotCount(): number {\n return this.visibleSlots === undefined ? this.pageCount : Math.max(1, Math.round(this.visibleSlots));\n }\n\n /** The page a slot points at. Windowed slots slide with the current page, which keeps its middle seat. */\n private pageForSlot(slot: number): number {\n return this.visibleSlots === undefined ? slot : this.currentPage - Math.floor(this.slotCount / 2) + slot;\n }\n\n private createItem(slot: number): Sprite | NumberItem {\n const page = this.pageForSlot(slot);\n const exists = page >= 0 && page < this.pageCount;\n const isActive = exists && page === this.currentPage;\n if (this.styleMode === \"dots\") {\n const dot = new Sprite(isActive ? this.activeTexture : this.inactiveTexture);\n dot.setSize(this.dotWidth, this.dotHeight);\n // An out-of-range slot renders nothing but keeps its size, so `List` still reserves its place.\n dot.visible = exists;\n return dot;\n }\n const baseFill = this.numberStyle?.fill ?? DEFAULT_NUMBER_STYLE.fill;\n const fill = isActive ? this.numberActiveColor ?? baseFill : baseFill;\n const item = new NumberItem(new Text({ text: exists ? String(page + 1) : \"\", style: numberTextStyle(this.numberStyle, fill, this.fonts) }));\n item.resize(this.dotWidth, this.dotHeight);\n if (isActive && this.numberActiveBackgroundTexture !== undefined) item.setBackground(this.createBackgroundView(this.numberActiveBackgroundTexture), this.numberActiveCornerRadius);\n return item;\n }\n\n /** Recreates every item for the current `styleMode`/`slotCount`/`dotWidth`/`dotHeight`. */\n private buildItems(): void {\n this.list.removeChildren();\n this.items = [];\n for (let slot = 0; slot < this.slotCount; slot++) {\n const item = this.createItem(slot);\n this.wireItem(item, slot);\n this.items.push(item);\n this.list.addChild(item);\n }\n this.applyArrowState();\n }\n\n /** Authoring canvas stays inert like every other control; a clickable item is only live at runtime. */\n private wireItem(item: Sprite | NumberItem, slot: number): void {\n if (this.interaction === \"authoring\" || !this.clickableState) { item.eventMode = \"none\"; return; }\n item.eventMode = \"static\";\n item.cursor = \"pointer\";\n // Resolved on click, not captured: a windowed slot points at a different page after every move.\n item.on(\"pointertap\", () => {\n const page = this.pageForSlot(slot);\n if (page >= 0 && page < this.pageCount) this.setPage(page);\n });\n }\n\n private applyCurrentPage(): void {\n const baseFill = this.numberStyle?.fill ?? DEFAULT_NUMBER_STYLE.fill;\n this.items.forEach((item, slot) => {\n const page = this.pageForSlot(slot);\n const exists = page >= 0 && page < this.pageCount;\n const isActive = exists && page === this.currentPage;\n if (item instanceof NumberItem) {\n item.numberText.text = exists ? String(page + 1) : \"\";\n item.numberText.style = numberTextStyle(this.numberStyle, isActive ? this.numberActiveColor ?? baseFill : baseFill, this.fonts);\n item.setBackground(isActive && this.numberActiveBackgroundTexture !== undefined ? this.createBackgroundView(this.numberActiveBackgroundTexture) : undefined, this.numberActiveCornerRadius);\n return;\n }\n item.texture = isActive ? this.activeTexture : this.inactiveTexture;\n item.setSize(this.dotWidth, this.dotHeight);\n item.visible = exists;\n });\n this.applyArrowState();\n }\n\n /**\n * Подключает авторские кнопки-стрелки: нажатие шагает на страницу назад/вперёд и эмитит\n * `onPageChange`, как клик по самому пункту. Кнопки остаются обычными нодами сцены — виджет только\n * подписывается на них и гасит ту, за которой страниц больше нет.\n */\n setArrows(arrows: { previous?: PaginationArrow; next?: PaginationArrow }): void {\n this.arrows = arrows;\n arrows.previous?.onPress.connect(() => this.setPage(this.currentPage - 1));\n arrows.next?.onPress.connect(() => this.setPage(this.currentPage + 1));\n this.applyArrowState();\n }\n\n /** На краях диапазона соответствующая стрелка недоступна — она никуда не ведёт. */\n private applyArrowState(): void {\n if (this.arrows === undefined) return;\n if (this.arrows.previous !== undefined) this.arrows.previous.enabled = this.currentPage > 0;\n if (this.arrows.next !== undefined) this.arrows.next.enabled = this.currentPage < this.pageCount - 1;\n }\n\n protected syncContent(node: UINode): void {\n if (node.type !== \"pagination\") return;\n const active = node.activeAssetId === undefined ? Texture.WHITE : this.textureFor(node.activeAssetId) ?? Texture.WHITE;\n const inactive = node.inactiveAssetId === undefined ? Texture.WHITE : this.textureFor(node.inactiveAssetId) ?? Texture.WHITE;\n const pageCount = Math.max(1, Math.round(node.pageCount));\n this.clickableState = node.clickable;\n\n const previousSlotCount = this.slotCount;\n this.visibleSlots = node.visiblePageCount;\n this.pageCountState = pageCount;\n const structureChanged = node.style !== this.styleMode || this.slotCount !== previousSlotCount || node.dotSize.width !== this.dotWidth || node.dotSize.height !== this.dotHeight;\n this.styleMode = node.style;\n this.activeTexture = active;\n this.inactiveTexture = inactive;\n this.numberStyle = node.numberStyle;\n this.numberActiveColor = node.numberActiveColor;\n this.numberActiveBackgroundTexture = node.numberActiveBackgroundAssetId === undefined ? undefined : this.textureFor(node.numberActiveBackgroundAssetId);\n this.numberActiveNineSlice = node.numberActiveNineSlice;\n this.numberActiveCornerRadius = node.numberActiveNineSlice === undefined ? node.numberActiveCornerRadius : undefined;\n this.dotWidth = node.dotSize.width;\n this.dotHeight = node.dotSize.height;\n this.currentPage = clampPage(this.currentPage, this.pageCount);\n\n if (this.list.type !== node.direction) this.list.type = node.direction;\n if (this.list.elementsMargin !== node.spacing) this.list.elementsMargin = node.spacing;\n\n if (structureChanged) this.buildItems();\n else this.applyCurrentPage();\n }\n\n get pageCount(): number { return this.pageCountState; }\n\n /** Used by `setPaginationViewPageCount` once game code knows the real content length. */\n set pageCount(value: number) {\n const next = Math.max(1, Math.round(value));\n if (next === this.pageCountState) return;\n const previousSlotCount = this.slotCount;\n this.pageCountState = next;\n this.currentPage = clampPage(this.currentPage, this.pageCount);\n if (this.slotCount === previousSlotCount) this.applyCurrentPage();\n else this.buildItems();\n }\n\n get page(): number { return this.currentPage; }\n\n /** External sync from game code (or editor preview): highlights an item without emitting `onPageChange`. */\n set page(value: number) {\n const next = clampPage(value, this.pageCountState);\n if (next === this.currentPage) return;\n this.currentPage = next;\n this.applyCurrentPage();\n }\n\n /** An item click: highlights immediately, then notifies game code — the same order a checkbox/slider follows. */\n private setPage(index: number): void {\n const next = clampPage(index, this.pageCountState);\n if (next !== this.currentPage) { this.currentPage = next; this.applyCurrentPage(); }\n this.onPageChange.emit(next);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"PaginationNodeView.js","sourceRoot":"","sources":["../../src/views/PaginationNodeView.ts"],"names":[],"mappings":"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","sourcesContent":["import { type PaginationNode, type TextStyleDefinition, type UINode } from \"@pixi-ui-editor/schema\";\nimport { List } from \"@pixi/ui\";\nimport { Container, Sprite, Text, Texture, type TextStyleOptions } from \"pixi.js\";\nimport { Signal } from \"typed-signals\";\nimport { NodeView, type SceneInteractionMode } from \"./NodeView.js\";\n\nexport type { PaginationNode };\n\nconst DEFAULT_NUMBER_STYLE: TextStyleDefinition = { fontFamily: \"Arial\", fontSize: 18, fontWeight: \"normal\", fontStyle: \"normal\", fill: \"#FFFFFF\", align: \"center\", verticalAlign: \"middle\", wordWrap: false, breakWords: false, letterSpacing: 0 };\n\n/** Same shape `ValueControlNodeViews.ts`'s own `textStyle` resolves — kept as its own small copy rather\n * than shared, the same call ADR 0029 makes for two similar-but-separately-evolving style resolvers. */\nfunction numberTextStyle(style: TextStyleDefinition | undefined, fill: string, fonts: ReadonlyMap<string, string> | undefined): Partial<TextStyleOptions> {\n const resolved = style ?? DEFAULT_NUMBER_STYLE;\n return {\n fontFamily: resolved.fontAssetId === undefined ? resolved.fontFamily : fonts?.get(resolved.fontAssetId) ?? resolved.fontFamily,\n fontSize: resolved.fontSize,\n fontWeight: resolved.fontWeight,\n fontStyle: resolved.fontStyle,\n fill,\n align: resolved.align,\n wordWrap: resolved.wordWrap,\n breakWords: resolved.breakWords,\n lineHeight: resolved.lineHeight,\n letterSpacing: resolved.letterSpacing,\n stroke: resolved.stroke === undefined ? undefined : { color: resolved.stroke.color, width: resolved.stroke.width },\n };\n}\n\n/**\n * Кнопка-стрелка пагинатора: ровно та часть `ButtonNodeView`/`StagedButtonNodeView`, которая нужна\n * виджету, — нажатие и «доступна ли». Тип объявлен структурно, чтобы этот файл не зависел от обоих\n * классов кнопок и не решал, какой из них «настоящий».\n */\nexport type PaginationArrow = { onPress: { connect(handler: () => void): unknown }; enabled: boolean };\n\n/** Clamps a page index into the valid `[0, pageCount)` range; an empty widget still shows dot 0. */\nfunction clampPage(page: number, pageCount: number): number {\n return Math.min(Math.max(0, Math.round(page)), Math.max(0, pageCount - 1));\n}\n\n/**\n * One `numbers`-style item: a centered page-number label. `width`/`height` are overridden to the\n * authored `dotSize` — the same fixed-footprint trick `ScrollItemContainer` uses — so the `List`\n * spaces every item evenly. The active-page highlight is a separate authored node the widget only\n * toggles `visible` on (see `PaginationNodeView.setActiveBackground`), never a child of this item.\n */\n/** @internal Exported only so DOM-free render-contract tests can exercise the footprint in isolation. */\nexport class NumberItem extends Container {\n private itemWidth = 0;\n private itemHeight = 0;\n\n // Named `numberText`, not `label`: `Container.label` is pixi.js's own display-object name string.\n constructor(readonly numberText: Text) {\n super();\n numberText.anchor.set(0.5);\n super.addChild(numberText);\n }\n\n override get width(): number { return this.itemWidth; }\n override set width(value: number) { this.itemWidth = value; }\n override get height(): number { return this.itemHeight; }\n override set height(value: number) { this.itemHeight = value; }\n\n resize(width: number, height: number): void {\n this.itemWidth = width;\n this.itemHeight = height;\n this.numberText.position.set(width / 2, height / 2);\n }\n}\n\n/**\n * Standalone page indicator: N procedurally generated items arranged by `@pixi/ui`'s `List` (the same\n * approach `ScrollViewNodeView`/`scrollView.ts` use for arranging children) — this view never\n * reimplements arrangement. Deliberately not wired to any scroll-view: game code owns the actual page\n * transition and drives the widget one-way through `setPaginationViewPage`/`setPaginationViewPageCount`,\n * the same relationship a slider or progress bar has to whatever value it displays.\n *\n * `style` picks between two mutually exclusive, differently-rendered item kinds — image dots\n * (`Sprite`, two states) or rendered page-number text (`NumberItem`: a label, base style + an optional\n * highlight color) — never both. Switching `style` rebuilds every item, the same \"view's required\n * *type* changed\" rule `StagedButtonNodeView` follows for its state views; `pageCount`/`dotSize`\n * changes do too, since the item count/footprint is a runtime fact about this control, not scene\n * topology — it only rebuilds this view's own item children, never the owning `NodeView` or the wider\n * scene tree.\n *\n * With `visiblePageCount` set, the indicator becomes a fixed strip of that many slots with the active\n * page always in the middle one (4 pages, page 3 active → 2-3-4). A slot pointing outside the real\n * page range keeps its footprint and renders nothing, so the active page never shifts sideways.\n *\n * The prev/next arrows are ordinary authored button nodes placed wherever the layout needs them; the\n * widget only takes their views (`scene.ts`'s `wireNodeReferences`, the same address → view resolution\n * Spine connector targets use) and steps the page on press. It never draws an arrow itself. The\n * `numbers`-style active-page highlight follows the same reference-not-owned contract: it is an\n * authored node the widget only toggles `visible` on while `style: \"numbers\"` — the node carries its\n * own image / nine-slice / rounded corners / size / position, none of it configured here.\n */\nexport class PaginationNodeView extends NodeView {\n private readonly list: List;\n private readonly interaction: SceneInteractionMode;\n private readonly fonts: ReadonlyMap<string, string> | undefined;\n private items: (Sprite | NumberItem)[] = [];\n private visibleSlots: number | undefined;\n private arrows?: { previous?: PaginationArrow; next?: PaginationArrow };\n private styleMode: PaginationNode[\"style\"];\n private activeTexture: Texture;\n private inactiveTexture: Texture;\n private numberStyle: TextStyleDefinition | undefined;\n private numberActiveColor: string | undefined;\n /** The authored highlight node; the widget owns only its `visible` flag while `style: \"numbers\"`. */\n private activeBackground: Container | undefined;\n private pageCountState: number;\n private currentPage: number;\n private dotWidth: number;\n private dotHeight: number;\n private clickableState: boolean;\n\n /** Fires only from a click on an item (when `clickable`); setting `page` programmatically never emits. */\n readonly onPageChange = new Signal<(page: number) => void>();\n\n constructor(node: PaginationNode, textures: ReadonlyMap<string, Texture> | undefined, interaction: SceneInteractionMode, fonts: ReadonlyMap<string, string> | undefined) {\n super(textures);\n this.interaction = interaction;\n this.fonts = fonts;\n this.styleMode = node.style;\n this.activeTexture = node.activeAssetId === undefined ? Texture.WHITE : this.textureFor(node.activeAssetId) ?? Texture.WHITE;\n this.inactiveTexture = node.inactiveAssetId === undefined ? Texture.WHITE : this.textureFor(node.inactiveAssetId) ?? Texture.WHITE;\n this.numberStyle = node.numberStyle;\n this.numberActiveColor = node.numberActiveColor;\n this.pageCountState = Math.max(1, Math.round(node.pageCount));\n this.visibleSlots = node.visiblePageCount;\n this.currentPage = clampPage(node.defaultPage, this.pageCount);\n this.dotWidth = node.dotSize.width;\n this.dotHeight = node.dotSize.height;\n this.clickableState = node.clickable;\n this.list = new List({ type: node.direction, elementsMargin: node.spacing });\n this.buildItems();\n this.setContent(this.list);\n }\n\n /** How many indicator slots are drawn: the fixed authored window, or one slot per real page. */\n private get slotCount(): number {\n return this.visibleSlots === undefined ? this.pageCount : Math.max(1, Math.round(this.visibleSlots));\n }\n\n /** The page a slot points at. Windowed slots slide with the current page, which keeps its middle seat. */\n private pageForSlot(slot: number): number {\n return this.visibleSlots === undefined ? slot : this.currentPage - Math.floor(this.slotCount / 2) + slot;\n }\n\n private createItem(slot: number): Sprite | NumberItem {\n const page = this.pageForSlot(slot);\n const exists = page >= 0 && page < this.pageCount;\n const isActive = exists && page === this.currentPage;\n if (this.styleMode === \"dots\") {\n const dot = new Sprite(isActive ? this.activeTexture : this.inactiveTexture);\n dot.setSize(this.dotWidth, this.dotHeight);\n // An out-of-range slot renders nothing but keeps its size, so `List` still reserves its place.\n dot.visible = exists;\n return dot;\n }\n const baseFill = this.numberStyle?.fill ?? DEFAULT_NUMBER_STYLE.fill;\n const fill = isActive ? this.numberActiveColor ?? baseFill : baseFill;\n const item = new NumberItem(new Text({ text: exists ? String(page + 1) : \"\", style: numberTextStyle(this.numberStyle, fill, this.fonts) }));\n item.resize(this.dotWidth, this.dotHeight);\n return item;\n }\n\n /** Recreates every item for the current `styleMode`/`slotCount`/`dotWidth`/`dotHeight`. */\n private buildItems(): void {\n this.list.removeChildren();\n this.items = [];\n for (let slot = 0; slot < this.slotCount; slot++) {\n const item = this.createItem(slot);\n this.wireItem(item, slot);\n this.items.push(item);\n this.list.addChild(item);\n }\n this.applyArrowState();\n }\n\n /** Authoring canvas stays inert like every other control; a clickable item is only live at runtime. */\n private wireItem(item: Sprite | NumberItem, slot: number): void {\n if (this.interaction === \"authoring\" || !this.clickableState) { item.eventMode = \"none\"; return; }\n item.eventMode = \"static\";\n item.cursor = \"pointer\";\n // Resolved on click, not captured: a windowed slot points at a different page after every move.\n item.on(\"pointertap\", () => {\n const page = this.pageForSlot(slot);\n if (page >= 0 && page < this.pageCount) this.setPage(page);\n });\n }\n\n private applyCurrentPage(): void {\n const baseFill = this.numberStyle?.fill ?? DEFAULT_NUMBER_STYLE.fill;\n this.items.forEach((item, slot) => {\n const page = this.pageForSlot(slot);\n const exists = page >= 0 && page < this.pageCount;\n const isActive = exists && page === this.currentPage;\n if (item instanceof NumberItem) {\n item.numberText.text = exists ? String(page + 1) : \"\";\n item.numberText.style = numberTextStyle(this.numberStyle, isActive ? this.numberActiveColor ?? baseFill : baseFill, this.fonts);\n return;\n }\n item.texture = isActive ? this.activeTexture : this.inactiveTexture;\n item.setSize(this.dotWidth, this.dotHeight);\n item.visible = exists;\n });\n this.applyArrowState();\n }\n\n /**\n * Подключает авторские кнопки-стрелки: нажатие шагает на страницу назад/вперёд и эмитит\n * `onPageChange`, как клик по самому пункту. Кнопки остаются обычными нодами сцены — виджет только\n * подписывается на них и гасит ту, за которой страниц больше нет.\n */\n setArrows(arrows: { previous?: PaginationArrow; next?: PaginationArrow }): void {\n this.arrows = arrows;\n arrows.previous?.onPress.connect(() => this.setPage(this.currentPage - 1));\n arrows.next?.onPress.connect(() => this.setPage(this.currentPage + 1));\n this.applyArrowState();\n }\n\n /** На краях диапазона соответствующая стрелка недоступна — она никуда не ведёт. */\n private applyArrowState(): void {\n if (this.arrows === undefined) return;\n if (this.arrows.previous !== undefined) this.arrows.previous.enabled = this.currentPage > 0;\n if (this.arrows.next !== undefined) this.arrows.next.enabled = this.currentPage < this.pageCount - 1;\n }\n\n /**\n * Связывает виджет с авторской нодой-подсветкой активной страницы. Как и стрелки, нода остаётся\n * обычным узлом сцены со своим визуалом — виджет лишь владеет её флагом `visible`: показывает при\n * `style: \"numbers\"` (активная страница есть всегда) и прячет в стиле `dots`.\n */\n setActiveBackground(view: Container | undefined): void {\n this.activeBackground = view;\n this.applyActiveBackgroundVisibility();\n }\n\n private applyActiveBackgroundVisibility(): void {\n if (this.activeBackground === undefined) return;\n this.activeBackground.visible = this.styleMode === \"numbers\";\n }\n\n protected syncContent(node: UINode): void {\n if (node.type !== \"pagination\") return;\n const active = node.activeAssetId === undefined ? Texture.WHITE : this.textureFor(node.activeAssetId) ?? Texture.WHITE;\n const inactive = node.inactiveAssetId === undefined ? Texture.WHITE : this.textureFor(node.inactiveAssetId) ?? Texture.WHITE;\n const pageCount = Math.max(1, Math.round(node.pageCount));\n this.clickableState = node.clickable;\n\n const previousSlotCount = this.slotCount;\n this.visibleSlots = node.visiblePageCount;\n this.pageCountState = pageCount;\n const structureChanged = node.style !== this.styleMode || this.slotCount !== previousSlotCount || node.dotSize.width !== this.dotWidth || node.dotSize.height !== this.dotHeight;\n this.styleMode = node.style;\n this.activeTexture = active;\n this.inactiveTexture = inactive;\n this.numberStyle = node.numberStyle;\n this.numberActiveColor = node.numberActiveColor;\n this.applyActiveBackgroundVisibility();\n this.dotWidth = node.dotSize.width;\n this.dotHeight = node.dotSize.height;\n this.currentPage = clampPage(this.currentPage, this.pageCount);\n\n if (this.list.type !== node.direction) this.list.type = node.direction;\n if (this.list.elementsMargin !== node.spacing) this.list.elementsMargin = node.spacing;\n\n if (structureChanged) this.buildItems();\n else this.applyCurrentPage();\n }\n\n get pageCount(): number { return this.pageCountState; }\n\n /** Used by `setPaginationViewPageCount` once game code knows the real content length. */\n set pageCount(value: number) {\n const next = Math.max(1, Math.round(value));\n if (next === this.pageCountState) return;\n const previousSlotCount = this.slotCount;\n this.pageCountState = next;\n this.currentPage = clampPage(this.currentPage, this.pageCount);\n if (this.slotCount === previousSlotCount) this.applyCurrentPage();\n else this.buildItems();\n }\n\n get page(): number { return this.currentPage; }\n\n /** External sync from game code (or editor preview): highlights an item without emitting `onPageChange`. */\n set page(value: number) {\n const next = clampPage(value, this.pageCountState);\n if (next === this.currentPage) return;\n this.currentPage = next;\n this.applyCurrentPage();\n }\n\n /** An item click: highlights immediately, then notifies game code — the same order a checkbox/slider follows. */\n private setPage(index: number): void {\n const next = clampPage(index, this.pageCountState);\n if (next !== this.currentPage) { this.currentPage = next; this.applyCurrentPage(); }\n this.onPageChange.emit(next);\n }\n}\n"]}
|
|
@@ -18,7 +18,7 @@ export declare class SpineNodeView extends NodeView {
|
|
|
18
18
|
private connectorBonesActive;
|
|
19
19
|
/** Layout profile the view was last synced with; connector multipliers resolve against it. */
|
|
20
20
|
private activeProfile;
|
|
21
|
-
constructor(skeletonData: SkeletonData | undefined, animation: string | undefined, autoplay: boolean, loop: boolean, animationSpeed: number, initializeForAuthoring: boolean, referenceFrame?: SpineReferenceFrame, ticker?: Ticker);
|
|
21
|
+
constructor(skeletonData: SkeletonData | undefined, animation: string | undefined, autoplay: boolean, loop: boolean, animationSpeed: number, initializeForAuthoring: boolean, referenceFrame?: SpineReferenceFrame, ticker?: Ticker, animationMix?: number);
|
|
22
22
|
/** Runtime switch used by game code; authored connector definitions are inactive by default. */
|
|
23
23
|
get activeConnectorBones(): boolean;
|
|
24
24
|
set activeConnectorBones(active: boolean);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpineNodeView.d.ts","sourceRoot":"","sources":["../../src/views/SpineNodeView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqD,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACvH,OAAO,EAA2C,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,mBAAmB,EAAE,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACnK,OAAO,EAAE,SAAS,EAAoB,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAEnE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,KAAK,gBAAgB,GAAG,cAAc,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC;AAC/D,KAAK,uBAAuB,GAAG,gBAAgB,GAAG,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAqB9I,qBAAa,aAAc,SAAQ,QAAQ;IACzC,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAU;IACjD,OAAO,CAAC,iBAAiB,CAAiC;IAC1D,OAAO,CAAC,gBAAgB,CAAyB;IACjD,OAAO,CAAC,oBAAoB,CAAS;IACrC,8FAA8F;IAC9F,OAAO,CAAC,aAAa,CAA8B;IAEnD,YAAY,YAAY,EAAE,YAAY,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,sBAAsB,EAAE,OAAO,EAAE,cAAc,CAAC,EAAE,mBAAmB,EAAE,MAAM,CAAC,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"SpineNodeView.d.ts","sourceRoot":"","sources":["../../src/views/SpineNodeView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqD,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACvH,OAAO,EAA2C,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,mBAAmB,EAAE,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACnK,OAAO,EAAE,SAAS,EAAoB,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAEnE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,KAAK,gBAAgB,GAAG,cAAc,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC;AAC/D,KAAK,uBAAuB,GAAG,gBAAgB,GAAG,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAqB9I,qBAAa,aAAc,SAAQ,QAAQ;IACzC,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAU;IACjD,OAAO,CAAC,iBAAiB,CAAiC;IAC1D,OAAO,CAAC,gBAAgB,CAAyB;IACjD,OAAO,CAAC,oBAAoB,CAAS;IACrC,8FAA8F;IAC9F,OAAO,CAAC,aAAa,CAA8B;IAEnD,YAAY,YAAY,EAAE,YAAY,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,sBAAsB,EAAE,OAAO,EAAE,cAAc,CAAC,EAAE,mBAAmB,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,YAAY,SAAI,EAkCpP;IAED,gGAAgG;IAChG,IAAI,oBAAoB,IAAI,OAAO,CAElC;IAED,IAAI,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAEvC;IAED,0FAA0F;IAC1F,iBAAiB,CAAC,QAAQ,EAAE,SAAS,uBAAuB,EAAE,GAAG,IAAI,CAKpE;IAED,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAwC7C;IAED,8EAA8E;IAC9E,qBAAqB,IAAI,IAAI,CAkD5B;IAED,OAAO,CAAC,uBAAuB;IAiB/B,iBAAiB,CAAC,cAAc,EAAE,mBAAmB,GAAG,SAAS,GAAG,IAAI,CAEvE;IAED;;;;;OAKG;IACH,SAAS,CAAC,wBAAwB,IAAI,IAAI,CAEzC;IAED,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAuClG;CACF"}
|
|
@@ -18,7 +18,7 @@ export class SpineNodeView extends NodeView {
|
|
|
18
18
|
connectorBonesActive = false;
|
|
19
19
|
/** Layout profile the view was last synced with; connector multipliers resolve against it. */
|
|
20
20
|
activeProfile = "desktop";
|
|
21
|
-
constructor(skeletonData, animation, autoplay, loop, animationSpeed, initializeForAuthoring, referenceFrame, ticker) {
|
|
21
|
+
constructor(skeletonData, animation, autoplay, loop, animationSpeed, initializeForAuthoring, referenceFrame, ticker, animationMix = 0) {
|
|
22
22
|
super();
|
|
23
23
|
this.referenceFrame = referenceFrame;
|
|
24
24
|
this.initializeForAuthoring = initializeForAuthoring;
|
|
@@ -36,6 +36,11 @@ export class SpineNodeView extends NodeView {
|
|
|
36
36
|
// the game touching `autoUpdate` itself.
|
|
37
37
|
const spine = new Spine({ skeletonData, autoUpdate: initializeForAuthoring ? autoplay : true, ...(ticker === undefined ? {} : { ticker }) });
|
|
38
38
|
spine.afterUpdateWorldTransforms = () => this.refreshConnectorBones();
|
|
39
|
+
// Кроссфейд между анимациями живёт на `AnimationStateData.defaultMix`, а не на конкретном
|
|
40
|
+
// треке: так плавность получают и переключения из инспектора, и любые `state.setAnimation`
|
|
41
|
+
// из игрового кода — без того, чтобы игра знала про авторскую настройку. 0 сохраняет прежний
|
|
42
|
+
// резкий переход.
|
|
43
|
+
spine.state.data.defaultMix = animationMix;
|
|
39
44
|
// A configured animation always gets a track, posed to its own first frame — a skeleton left
|
|
40
45
|
// without one instead renders Spine's setup/bind pose, which is almost never the intended "static"
|
|
41
46
|
// look and was reported as a visibly broken/twitching pose (still-running physics/IK constraints
|
|
@@ -187,6 +192,8 @@ export class SpineNodeView extends NodeView {
|
|
|
187
192
|
}
|
|
188
193
|
syncContent(node, transform, profile) {
|
|
189
194
|
this.activeProfile = profile;
|
|
195
|
+
if (node.type === "spine" && this.content !== undefined)
|
|
196
|
+
this.content.alpha = node.opacity ?? 1;
|
|
190
197
|
if (this.content instanceof Spine) {
|
|
191
198
|
if (node.type === "spine") {
|
|
192
199
|
const settings = resolveSpineSettings(node, profile);
|
|
@@ -198,12 +205,19 @@ export class SpineNodeView extends NodeView {
|
|
|
198
205
|
// never Spine's setup/bind pose.
|
|
199
206
|
const playing = settings.autoplay || this.initializeForAuthoring;
|
|
200
207
|
const desiredTimeScale = playing ? settings.animationSpeed : 0;
|
|
208
|
+
// Правка ползунка должна влиять на следующий переход, а не только на момент создания view.
|
|
209
|
+
this.content.state.data.defaultMix = settings.animationMix;
|
|
201
210
|
if (settings.animation === undefined) {
|
|
202
211
|
if (track !== null && track !== undefined)
|
|
203
212
|
this.content.state.clearTrack(0);
|
|
204
213
|
}
|
|
205
214
|
else if ((animationChanged || loopChanged) && this.content.skeleton.data.findAnimation(settings.animation) !== null) {
|
|
206
|
-
this.content.state.setAnimation(0, settings.animation, settings.loop)
|
|
215
|
+
const entry = this.content.state.setAnimation(0, settings.animation, settings.loop);
|
|
216
|
+
entry.timeScale = desiredTimeScale;
|
|
217
|
+
// Замороженный на кадре 0 скелет не проигрывает микс (mixTime двигается временем трека),
|
|
218
|
+
// и остался бы навсегда в полусмешанной позе — статичной ноде переход всегда мгновенный.
|
|
219
|
+
if (!playing)
|
|
220
|
+
entry.mixDuration = 0;
|
|
207
221
|
}
|
|
208
222
|
else if (track !== null && track !== undefined) {
|
|
209
223
|
track.timeScale = desiredTimeScale;
|