@fenglimg/cocos-state-controller 0.1.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/LICENSE +21 -0
- package/README.md +287 -0
- package/assets/script/controller/Capability.ts +100 -0
- package/assets/script/controller/Capability.ts.meta +10 -0
- package/assets/script/controller/CapabilityRegistry.ts +116 -0
- package/assets/script/controller/CapabilityRegistry.ts.meta +10 -0
- package/assets/script/controller/EnumPropRefMap.ts +232 -0
- package/assets/script/controller/EnumPropRefMap.ts.meta +10 -0
- package/assets/script/controller/NestedCtrlData.ts +199 -0
- package/assets/script/controller/NestedCtrlData.ts.meta +10 -0
- package/assets/script/controller/PrefabIntrospection.ts +151 -0
- package/assets/script/controller/PrefabIntrospection.ts.meta +10 -0
- package/assets/script/controller/Props.meta +13 -0
- package/assets/script/controller/StateControllerV2.ts +1957 -0
- package/assets/script/controller/StateControllerV2.ts.meta +10 -0
- package/assets/script/controller/StateEnumV2.ts +165 -0
- package/assets/script/controller/StateEnumV2.ts.meta +10 -0
- package/assets/script/controller/StateErrorManagerV2.ts +217 -0
- package/assets/script/controller/StateErrorManagerV2.ts.meta +10 -0
- package/assets/script/controller/StatePropHandlerV2.ts +316 -0
- package/assets/script/controller/StatePropHandlerV2.ts.meta +10 -0
- package/assets/script/controller/StatePropertyControlService.ts +148 -0
- package/assets/script/controller/StatePropertyControlService.ts.meta +10 -0
- package/assets/script/controller/StateSelectV2.ts +4542 -0
- package/assets/script/controller/StateSelectV2.ts.meta +10 -0
- package/assets/script/controller/capabilities/AutoSyncCapability.ts +30 -0
- package/assets/script/controller/capabilities/AutoSyncCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/EventCapability.ts +144 -0
- package/assets/script/controller/capabilities/EventCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/MigrationCapability.ts +94 -0
- package/assets/script/controller/capabilities/MigrationCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/MultiCtrlBindingCapability.ts +157 -0
- package/assets/script/controller/capabilities/MultiCtrlBindingCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/PropertyControlCapability.ts +124 -0
- package/assets/script/controller/capabilities/PropertyControlCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/RecordingCapability.ts +69 -0
- package/assets/script/controller/capabilities/RecordingCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/SelectedPageIdCapability.ts +88 -0
- package/assets/script/controller/capabilities/SelectedPageIdCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities.meta +13 -0
- package/assets/script/controller/props/CtrlInspectorGroups.ts +138 -0
- package/assets/script/controller/props/CtrlInspectorGroups.ts.meta +10 -0
- package/assets/script/controller/props/SelectInspectorGroups.ts +104 -0
- package/assets/script/controller/props/SelectInspectorGroups.ts.meta +10 -0
- package/bin/csc.js +286 -0
- package/package.json +60 -0
- package/packages/state-controller-v2-panel/README.md +80 -0
- package/packages/state-controller-v2-panel/inspector-inject.js +917 -0
- package/packages/state-controller-v2-panel/inspector-probe.json +3767 -0
- package/packages/state-controller-v2-panel/lib/handlers.js +534 -0
- package/packages/state-controller-v2-panel/main.js +149 -0
- package/packages/state-controller-v2-panel/package.json +32 -0
- package/packages/state-controller-v2-panel/panel/build.js +23 -0
- package/packages/state-controller-v2-panel/panel/logic.js +1207 -0
- package/packages/state-controller-v2-panel/panel/styles.css +454 -0
- package/packages/state-controller-v2-panel/panel/template.html +296 -0
- package/packages/state-controller-v2-panel/scene-accessor.js +657 -0
- package/skills/cocos-state-controller/SKILL.md +28 -0
- package/skills/cocos-state-controller/refs/cli-usage.md +78 -0
- package/skills/cocos-state-controller/refs/editor-guide.md +127 -0
- package/skills/cocos-state-controller/refs/migrate.md +106 -0
- package/skills/cocos-state-controller/refs/upstream-pr.md +66 -0
- package/tools/migration/migrate-prefab-v1-to-v2.js +608 -0
- package/tools/state-controller-sync-manifest.json +33 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 属性处理器系统
|
|
3
|
+
*
|
|
4
|
+
* Phase 2.1 重构: 把原 41 个手写 PropHandler class (~822 行) 改成表驱动:
|
|
5
|
+
* - 1 个通用 PropHandlerManager (3 个静态方法 + 注册 Map)
|
|
6
|
+
* - 2 个辅助 factory: nodeProp / compProp
|
|
7
|
+
* - 一张注册表枚举所有 prop ↔ accessor 映射
|
|
8
|
+
*
|
|
9
|
+
* 设计要点:
|
|
10
|
+
* - null node 守卫集中在 PropHandlerManager 三个静态方法
|
|
11
|
+
* 单 handler 内不再重复 null 检查
|
|
12
|
+
* - 组件依赖 prop 走 compProp(compType, fieldName), 自带"缺组件 -> undefined"
|
|
13
|
+
* - 节点直接 prop / 需要 clone 复合类型的特殊 case 走 nodeProp(get, set)
|
|
14
|
+
* - 扩展新 prop: 加一行 PropHandlerManager.register(...) 即可, 不再写新 class
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { EnumPropName } from "./StateEnumV2";
|
|
18
|
+
import { StateErrorManager } from "./StateErrorManagerV2";
|
|
19
|
+
|
|
20
|
+
/** 属性值统一类型 */
|
|
21
|
+
export type TPropValue
|
|
22
|
+
= | number
|
|
23
|
+
| boolean
|
|
24
|
+
| string
|
|
25
|
+
| cc.Vec3
|
|
26
|
+
| cc.Vec2
|
|
27
|
+
| cc.Color
|
|
28
|
+
| cc.Size
|
|
29
|
+
| cc.Quat
|
|
30
|
+
| cc.SpriteFrame
|
|
31
|
+
| cc.Font
|
|
32
|
+
| undefined;
|
|
33
|
+
|
|
34
|
+
/** 属性处理器接口 */
|
|
35
|
+
export interface IPropHandler {
|
|
36
|
+
/** 读取节点 / 组件当前属性值; 假定 node 非 null (manager 已做守卫) */
|
|
37
|
+
getValue(node: cc.Node): TPropValue | undefined
|
|
38
|
+
/** 写入节点 / 组件属性值; 假定 node 非 null */
|
|
39
|
+
setValue(node: cc.Node, value: TPropValue): void
|
|
40
|
+
/** 默认值, 通常等同 getValue */
|
|
41
|
+
getDefaultValue(node: cc.Node): TPropValue | undefined
|
|
42
|
+
/**
|
|
43
|
+
* 录制 diff 用: 判断两个值是否等同.
|
|
44
|
+
* - 基础类型 (number/string/boolean): ===
|
|
45
|
+
* - 复合类型 (Vec/Color/Size): 按字段值比 (含 alpha)
|
|
46
|
+
* - 资源类型 (SpriteFrame/Font): 引用比 (===)
|
|
47
|
+
* - undefined/null 双方为 nil 即 true
|
|
48
|
+
* 默认实现见 PropHandlerManager 注册路径 (factory 自带 isEqual)。
|
|
49
|
+
*/
|
|
50
|
+
isEqual(a: TPropValue, b: TPropValue): boolean
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 属性处理器统一管理类
|
|
55
|
+
*
|
|
56
|
+
* 三个静态访问方法集中做 null node 守卫, 之后委派给注册的 handler。
|
|
57
|
+
*/
|
|
58
|
+
export class PropHandlerManager {
|
|
59
|
+
private static handlers = new Map<EnumPropName, IPropHandler>();
|
|
60
|
+
|
|
61
|
+
public static register(propType: EnumPropName, handler: IPropHandler): void {
|
|
62
|
+
this.handlers.set(propType, handler);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public static getHandler(propType: EnumPropName): IPropHandler | undefined {
|
|
66
|
+
return this.handlers.get(propType);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** 列出所有已注册 prop type (供录制全 prop snapshot 等扫描场景). */
|
|
70
|
+
public static listRegisteredPropTypes(): EnumPropName[] {
|
|
71
|
+
const out: EnumPropName[] = [];
|
|
72
|
+
this.handlers.forEach((_, k) => out.push(k));
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public static getValue(propType: EnumPropName, node: cc.Node): TPropValue | undefined {
|
|
77
|
+
if (!node) return undefined;
|
|
78
|
+
const handler = this.getHandler(propType);
|
|
79
|
+
return handler ? handler.getValue(node) : undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public static setValue(propType: EnumPropName, node: cc.Node, value: TPropValue): void {
|
|
83
|
+
if (!node) return;
|
|
84
|
+
const handler = this.getHandler(propType);
|
|
85
|
+
if (handler) handler.setValue(node, value);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public static getDefaultValue(propType: EnumPropName, node: cc.Node): TPropValue | undefined {
|
|
89
|
+
if (!node) return undefined;
|
|
90
|
+
const handler = this.getHandler(propType);
|
|
91
|
+
return handler ? handler.getDefaultValue(node) : undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 录制 diff 用: 判断两个值是否等同. 未注册的 propType 视为 false (保守, 视为有变化)。
|
|
96
|
+
* 双方均为 nil (undefined / null) 视为 true。
|
|
97
|
+
*/
|
|
98
|
+
public static isEqual(propType: EnumPropName, a: TPropValue, b: TPropValue): boolean {
|
|
99
|
+
const aNil = a === undefined || a === null;
|
|
100
|
+
const bNil = b === undefined || b === null;
|
|
101
|
+
if (aNil && bNil) return true;
|
|
102
|
+
if (aNil !== bNil) return false;
|
|
103
|
+
const handler = this.getHandler(propType);
|
|
104
|
+
if (!handler) return false;
|
|
105
|
+
return handler.isEqual(a, b);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ============================== isEqual helpers ==============================
|
|
110
|
+
|
|
111
|
+
/** 默认 isEqual: ===, 用于基础类型与资源引用比 */
|
|
112
|
+
function eqStrict(a: TPropValue, b: TPropValue): boolean {
|
|
113
|
+
return a === b;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Vec3 按值比 (x/y/z) */
|
|
117
|
+
function eqVec3(a: TPropValue, b: TPropValue): boolean {
|
|
118
|
+
const av = a as cc.Vec3;
|
|
119
|
+
const bv = b as cc.Vec3;
|
|
120
|
+
return av.x === bv.x && av.y === bv.y && av.z === bv.z;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Vec2 按值比 (x/y) */
|
|
124
|
+
function eqVec2(a: TPropValue, b: TPropValue): boolean {
|
|
125
|
+
const av = a as cc.Vec2;
|
|
126
|
+
const bv = b as cc.Vec2;
|
|
127
|
+
return av.x === bv.x && av.y === bv.y;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Color 按值比 (r/g/b/a) */
|
|
131
|
+
function eqColor(a: TPropValue, b: TPropValue): boolean {
|
|
132
|
+
const ac = a as cc.Color;
|
|
133
|
+
const bc = b as cc.Color;
|
|
134
|
+
return ac.r === bc.r && ac.g === bc.g && ac.b === bc.b && ac.a === bc.a;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Size 按值比 (width/height) */
|
|
138
|
+
function eqSize(a: TPropValue, b: TPropValue): boolean {
|
|
139
|
+
const as = a as cc.Size;
|
|
140
|
+
const bs = b as cc.Size;
|
|
141
|
+
return as.width === bs.width && as.height === bs.height;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** 节点直接属性 (cc.Node 上的字段或方法访问); 需自定 get/set 控制复合类型 clone */
|
|
145
|
+
function nodeProp<T extends TPropValue>(
|
|
146
|
+
get: (n: cc.Node) => T | undefined,
|
|
147
|
+
set: (n: cc.Node, v: T) => void,
|
|
148
|
+
isEqual: (a: TPropValue, b: TPropValue) => boolean = eqStrict,
|
|
149
|
+
): IPropHandler {
|
|
150
|
+
return {
|
|
151
|
+
getValue: get as (n: cc.Node) => TPropValue | undefined,
|
|
152
|
+
setValue: (n, v) => set(n, v as T),
|
|
153
|
+
getDefaultValue: get as (n: cc.Node) => TPropValue | undefined,
|
|
154
|
+
isEqual,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** 组件依赖属性 (节点上挂载的 cc.Component, 直接 field 读写); 缺组件返回 undefined */
|
|
159
|
+
function compProp(
|
|
160
|
+
compType: typeof cc.Component | any,
|
|
161
|
+
fieldName: string,
|
|
162
|
+
isEqual: (a: TPropValue, b: TPropValue) => boolean = eqStrict,
|
|
163
|
+
): IPropHandler {
|
|
164
|
+
return {
|
|
165
|
+
getValue: (node) => {
|
|
166
|
+
const c = node.getComponent(compType);
|
|
167
|
+
return c ? (c as any)[fieldName] : undefined;
|
|
168
|
+
},
|
|
169
|
+
setValue: (node, value) => {
|
|
170
|
+
const c = node.getComponent(compType);
|
|
171
|
+
if (c) (c as any)[fieldName] = value;
|
|
172
|
+
},
|
|
173
|
+
getDefaultValue: (node) => {
|
|
174
|
+
const c = node.getComponent(compType);
|
|
175
|
+
return c ? (c as any)[fieldName] : undefined;
|
|
176
|
+
},
|
|
177
|
+
isEqual,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ============================== 注册表 ==============================
|
|
182
|
+
|
|
183
|
+
// ---- 节点直接属性 ----
|
|
184
|
+
PropHandlerManager.register(EnumPropName.Active,
|
|
185
|
+
nodeProp<boolean>(n => n.active, (n, v) => {
|
|
186
|
+
n.active = v;
|
|
187
|
+
}));
|
|
188
|
+
|
|
189
|
+
PropHandlerManager.register(EnumPropName.Position,
|
|
190
|
+
nodeProp<cc.Vec3>(n => cc.v3(n.position), (n, v) => {
|
|
191
|
+
n.position = v;
|
|
192
|
+
}, eqVec3));
|
|
193
|
+
|
|
194
|
+
PropHandlerManager.register(EnumPropName.Euler,
|
|
195
|
+
nodeProp<cc.Vec3>(n => cc.v3(n.eulerAngles), (n, v) => {
|
|
196
|
+
n.eulerAngles = v;
|
|
197
|
+
}, eqVec3));
|
|
198
|
+
|
|
199
|
+
PropHandlerManager.register(EnumPropName.Scale,
|
|
200
|
+
nodeProp<number>(n => n.scale, (n, v) => {
|
|
201
|
+
n.scale = v;
|
|
202
|
+
}));
|
|
203
|
+
|
|
204
|
+
PropHandlerManager.register(EnumPropName.Anchor,
|
|
205
|
+
nodeProp<cc.Vec2>(
|
|
206
|
+
n => cc.v2(n.anchorX, n.anchorY),
|
|
207
|
+
(n, v) => n.setAnchorPoint(v),
|
|
208
|
+
eqVec2,
|
|
209
|
+
));
|
|
210
|
+
|
|
211
|
+
PropHandlerManager.register(EnumPropName.Size,
|
|
212
|
+
nodeProp<cc.Size>(
|
|
213
|
+
(n) => {
|
|
214
|
+
const s = n.getContentSize();
|
|
215
|
+
return cc.size(s.width, s.height);
|
|
216
|
+
},
|
|
217
|
+
(n, v) => {
|
|
218
|
+
n.setContentSize(v);
|
|
219
|
+
},
|
|
220
|
+
eqSize,
|
|
221
|
+
));
|
|
222
|
+
|
|
223
|
+
PropHandlerManager.register(EnumPropName.Color,
|
|
224
|
+
nodeProp<cc.Color>(
|
|
225
|
+
(n) => {
|
|
226
|
+
const c = n.color;
|
|
227
|
+
return cc.color(c.r, c.g, c.b, c.a);
|
|
228
|
+
},
|
|
229
|
+
(n, v) => {
|
|
230
|
+
n.color = v;
|
|
231
|
+
},
|
|
232
|
+
eqColor,
|
|
233
|
+
));
|
|
234
|
+
|
|
235
|
+
PropHandlerManager.register(EnumPropName.Opacity,
|
|
236
|
+
nodeProp<number>(n => n.opacity, (n, v) => {
|
|
237
|
+
n.opacity = v;
|
|
238
|
+
}));
|
|
239
|
+
|
|
240
|
+
// ---- Label 组件 ----
|
|
241
|
+
PropHandlerManager.register(EnumPropName.LabelString, compProp(cc.Label, "string"));
|
|
242
|
+
PropHandlerManager.register(EnumPropName.LabelFontSize, compProp(cc.Label, "fontSize"));
|
|
243
|
+
PropHandlerManager.register(EnumPropName.LabelLineHeight, compProp(cc.Label, "lineHeight"));
|
|
244
|
+
PropHandlerManager.register(EnumPropName.LabelSpacingX, compProp(cc.Label, "spacingX"));
|
|
245
|
+
PropHandlerManager.register(EnumPropName.LabelWrapEnable, compProp(cc.Label, "enableWrapText"));
|
|
246
|
+
PropHandlerManager.register(EnumPropName.Font, compProp(cc.Label, "font"));
|
|
247
|
+
|
|
248
|
+
// ---- LabelOutline (颜色需 clone) ----
|
|
249
|
+
PropHandlerManager.register(EnumPropName.LabelOutlineColor, {
|
|
250
|
+
getValue: (node) => {
|
|
251
|
+
const c = node.getComponent(cc.LabelOutline);
|
|
252
|
+
if (!c) return undefined;
|
|
253
|
+
const col = c.color;
|
|
254
|
+
return cc.color(col.r, col.g, col.b, col.a);
|
|
255
|
+
},
|
|
256
|
+
setValue: (node, value) => {
|
|
257
|
+
const c = node.getComponent(cc.LabelOutline);
|
|
258
|
+
if (c) c.color = value as cc.Color;
|
|
259
|
+
},
|
|
260
|
+
getDefaultValue: (node) => {
|
|
261
|
+
const c = node.getComponent(cc.LabelOutline);
|
|
262
|
+
if (!c) return undefined;
|
|
263
|
+
const col = c.color;
|
|
264
|
+
return cc.color(col.r, col.g, col.b, col.a);
|
|
265
|
+
},
|
|
266
|
+
isEqual: eqColor,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// ---- Sprite ----
|
|
270
|
+
PropHandlerManager.register(EnumPropName.SpriteFrame, compProp(cc.Sprite, "spriteFrame"));
|
|
271
|
+
PropHandlerManager.register(EnumPropName.SpriteFillRange, compProp(cc.Sprite, "fillRange"));
|
|
272
|
+
|
|
273
|
+
// ---- 其他单字段组件 ----
|
|
274
|
+
PropHandlerManager.register(EnumPropName.SliderProgress, compProp(cc.Slider, "progress"));
|
|
275
|
+
PropHandlerManager.register(EnumPropName.EditboxString, compProp(cc.EditBox, "string"));
|
|
276
|
+
PropHandlerManager.register(EnumPropName.ButtonInteractable, compProp(cc.Button, "interactable"));
|
|
277
|
+
PropHandlerManager.register(EnumPropName.ProgressBarProgress, compProp(cc.ProgressBar, "progress"));
|
|
278
|
+
PropHandlerManager.register(EnumPropName.ToggleIsChecked, compProp(cc.Toggle, "isChecked"));
|
|
279
|
+
PropHandlerManager.register(EnumPropName.RichTextString, compProp(cc.RichText, "string"));
|
|
280
|
+
PropHandlerManager.register(EnumPropName.ScrollViewEnabled, compProp(cc.ScrollView, "enabled"));
|
|
281
|
+
PropHandlerManager.register(EnumPropName.MaskEnabled, compProp(cc.Mask, "enabled"));
|
|
282
|
+
|
|
283
|
+
// ---- GrayScale (Cocos 2.x 需要走材质, 这里保留 stub 行为) ----
|
|
284
|
+
PropHandlerManager.register(EnumPropName.GrayScale, {
|
|
285
|
+
getValue: (node) => {
|
|
286
|
+
const sprite = node.getComponent(cc.Sprite);
|
|
287
|
+
return sprite ? false : undefined;
|
|
288
|
+
},
|
|
289
|
+
setValue: (node, _value) => {
|
|
290
|
+
const sprite = node.getComponent(cc.Sprite);
|
|
291
|
+
if (sprite) {
|
|
292
|
+
StateErrorManager.warn("GrayScale属性在Cocos Creator 2.x中需要通过材质实现");
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
getDefaultValue: (node) => {
|
|
296
|
+
const sprite = node.getComponent(cc.Sprite);
|
|
297
|
+
return sprite ? false : undefined;
|
|
298
|
+
},
|
|
299
|
+
isEqual: eqStrict,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// ---- Widget (14 个统一 field 风格) ----
|
|
303
|
+
PropHandlerManager.register(EnumPropName.WidgetEnabled, compProp(cc.Widget, "enabled"));
|
|
304
|
+
PropHandlerManager.register(EnumPropName.WidgetAlignMode, compProp(cc.Widget, "alignMode"));
|
|
305
|
+
PropHandlerManager.register(EnumPropName.WidgetIsAlignTop, compProp(cc.Widget, "isAlignTop"));
|
|
306
|
+
PropHandlerManager.register(EnumPropName.WidgetIsAlignBottom, compProp(cc.Widget, "isAlignBottom"));
|
|
307
|
+
PropHandlerManager.register(EnumPropName.WidgetIsAlignLeft, compProp(cc.Widget, "isAlignLeft"));
|
|
308
|
+
PropHandlerManager.register(EnumPropName.WidgetIsAlignRight, compProp(cc.Widget, "isAlignRight"));
|
|
309
|
+
PropHandlerManager.register(EnumPropName.WidgetIsAlignHorizontalCenter, compProp(cc.Widget, "isAlignHorizontalCenter"));
|
|
310
|
+
PropHandlerManager.register(EnumPropName.WidgetIsAlignVerticalCenter, compProp(cc.Widget, "isAlignVerticalCenter"));
|
|
311
|
+
PropHandlerManager.register(EnumPropName.WidgetTop, compProp(cc.Widget, "top"));
|
|
312
|
+
PropHandlerManager.register(EnumPropName.WidgetBottom, compProp(cc.Widget, "bottom"));
|
|
313
|
+
PropHandlerManager.register(EnumPropName.WidgetLeft, compProp(cc.Widget, "left"));
|
|
314
|
+
PropHandlerManager.register(EnumPropName.WidgetRight, compProp(cc.Widget, "right"));
|
|
315
|
+
PropHandlerManager.register(EnumPropName.WidgetHorizontalCenter, compProp(cc.Widget, "horizontalCenter"));
|
|
316
|
+
PropHandlerManager.register(EnumPropName.WidgetVerticalCenter, compProp(cc.Widget, "verticalCenter"));
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 属性控制服务 (Phase 5.2)
|
|
3
|
+
*
|
|
4
|
+
* 从 StateSelectV2 抽出的 stateless 服务, 负责回答三个问题:
|
|
5
|
+
* 1. 节点上是否有挂支持某 prop 类型的组件? (isPropertyAvailable)
|
|
6
|
+
* 2. propData 中某 prop 是否处于受控状态? (isPropertyControlled)
|
|
7
|
+
* 3. 列出节点上当前所有可用的 prop. (scanAvailableProperties)
|
|
8
|
+
*
|
|
9
|
+
* 设计点:
|
|
10
|
+
* - 全部为静态方法, 状态从外部传入 (node / propData), 不持有任何实例状态
|
|
11
|
+
* - 组件依赖检查改为注册表驱动 (原 56 行 switch → 14 行 register),
|
|
12
|
+
* 插件可通过 registerComponentProp 注入自定义 prop 类型 ↔ 组件 的关联
|
|
13
|
+
*
|
|
14
|
+
* 不在本服务范围:
|
|
15
|
+
* - togglePropertyControl / addPropertyControl / removePropertyControl /
|
|
16
|
+
* autoConfigureAllProperties — 这些紧耦合 StateSelectV2 内部状态 (propData
|
|
17
|
+
* 的写入 + setPropValue + sync 链路), 留在 StateSelectV2.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { EnumPropName } from "./StateEnumV2";
|
|
21
|
+
import { TProp } from "./StateSelectV2";
|
|
22
|
+
|
|
23
|
+
/** 组件可用性检查函数: 给一个节点, 回答 "节点上是否挂了支持本 prop 的组件?" */
|
|
24
|
+
export type ComponentAvailabilityCheck = (node: cc.Node) => boolean;
|
|
25
|
+
|
|
26
|
+
export class PropertyControlService {
|
|
27
|
+
/** 节点基础属性 — 任何节点上都可用, 与组件无关. */
|
|
28
|
+
private static nodeBasicProps: ReadonlySet<EnumPropName> = new Set([
|
|
29
|
+
EnumPropName.Active,
|
|
30
|
+
EnumPropName.Position,
|
|
31
|
+
EnumPropName.Scale,
|
|
32
|
+
EnumPropName.Color,
|
|
33
|
+
EnumPropName.Size,
|
|
34
|
+
EnumPropName.Euler,
|
|
35
|
+
EnumPropName.Anchor,
|
|
36
|
+
EnumPropName.Opacity,
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
/** 组件依赖 prop 的可用性检查注册表. 插件扩展点. */
|
|
40
|
+
private static componentAvailability = new Map<EnumPropName, ComponentAvailabilityCheck>();
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 注册一个组件依赖的 prop ↔ 组件 关联.
|
|
44
|
+
*
|
|
45
|
+
* 调用形如:
|
|
46
|
+
* PropertyControlService.registerComponentProp(EnumPropName.LabelString,
|
|
47
|
+
* node => !!node.getComponent(cc.Label));
|
|
48
|
+
*
|
|
49
|
+
* 后续 isPropertyAvailable / scanAvailableProperties 自动识别该 prop.
|
|
50
|
+
*/
|
|
51
|
+
public static registerComponentProp(propType: EnumPropName, check: ComponentAvailabilityCheck): void {
|
|
52
|
+
this.componentAvailability.set(propType, check);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** 节点上是否可以使用某 prop 类型. */
|
|
56
|
+
public static isPropertyAvailable(node: cc.Node, propType: EnumPropName): boolean {
|
|
57
|
+
if (!node || !node.isValid) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (this.nodeBasicProps.has(propType)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
const check = this.componentAvailability.get(propType);
|
|
64
|
+
return check ? check(node) : false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** propData 中某 prop 是否标记为受控. */
|
|
68
|
+
public static isPropertyControlled(propData: TProp | null | undefined, propType: EnumPropName): boolean {
|
|
69
|
+
if (!propData) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const controlledProps = propData.$$controlledProps$$ || {};
|
|
73
|
+
const propName = EnumPropName[propType];
|
|
74
|
+
if (controlledProps[propName] !== undefined) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
// 兼容旧 $$changedProp$$ 结构 (迁移期遗留)
|
|
78
|
+
const changedProp = propData.$$changedProp$$ || {};
|
|
79
|
+
return !!changedProp[propName];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** 列出当前节点上所有可用的 prop. */
|
|
83
|
+
public static scanAvailableProperties(node: cc.Node): EnumPropName[] {
|
|
84
|
+
if (!node || !node.isValid) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
const out: EnumPropName[] = [];
|
|
88
|
+
// cc.Enum(EnumPropName) 把数字反向映射 key 设为不可枚举, for-in 只剩名字 key,
|
|
89
|
+
// 通过名字反查数字值 (与 StateSelectV2.scanAvailableProperties Phase 4.3 修复对应).
|
|
90
|
+
for (const propKey in EnumPropName) {
|
|
91
|
+
const propType = (EnumPropName as any)[propKey];
|
|
92
|
+
if (typeof propType !== "number" || propType === EnumPropName.Non) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (this.isPropertyAvailable(node, propType as EnumPropName)) {
|
|
96
|
+
out.push(propType as EnumPropName);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ============================== 内置组件 prop 注册 ==============================
|
|
104
|
+
// 等价于原 StateSelectV2.checkNodeHasComponentForProp 的 switch 表, 改为表驱动.
|
|
105
|
+
|
|
106
|
+
PropertyControlService.registerComponentProp(EnumPropName.LabelString, n => !!n.getComponent(cc.Label));
|
|
107
|
+
PropertyControlService.registerComponentProp(EnumPropName.Font, n => !!n.getComponent(cc.Label));
|
|
108
|
+
PropertyControlService.registerComponentProp(EnumPropName.LabelFontSize, n => !!n.getComponent(cc.Label));
|
|
109
|
+
PropertyControlService.registerComponentProp(EnumPropName.LabelLineHeight, n => !!n.getComponent(cc.Label));
|
|
110
|
+
PropertyControlService.registerComponentProp(EnumPropName.LabelSpacingX, n => !!n.getComponent(cc.Label));
|
|
111
|
+
PropertyControlService.registerComponentProp(EnumPropName.LabelWrapEnable, n => !!n.getComponent(cc.Label));
|
|
112
|
+
|
|
113
|
+
PropertyControlService.registerComponentProp(EnumPropName.LabelOutlineColor, n => !!n.getComponent(cc.LabelOutline));
|
|
114
|
+
|
|
115
|
+
PropertyControlService.registerComponentProp(EnumPropName.SpriteFrame, n => !!n.getComponent(cc.Sprite));
|
|
116
|
+
PropertyControlService.registerComponentProp(EnumPropName.SpriteFillRange, n => !!n.getComponent(cc.Sprite));
|
|
117
|
+
|
|
118
|
+
PropertyControlService.registerComponentProp(EnumPropName.SliderProgress, n => !!n.getComponent(cc.Slider));
|
|
119
|
+
PropertyControlService.registerComponentProp(EnumPropName.EditboxString, n => !!n.getComponent(cc.EditBox));
|
|
120
|
+
// GrayScale 是项目自定义组件, 用字符串名查
|
|
121
|
+
PropertyControlService.registerComponentProp(EnumPropName.GrayScale, n => !!n.getComponent("GrayScale"));
|
|
122
|
+
PropertyControlService.registerComponentProp(EnumPropName.ButtonInteractable, n => !!n.getComponent(cc.Button));
|
|
123
|
+
PropertyControlService.registerComponentProp(EnumPropName.ProgressBarProgress, n => !!n.getComponent(cc.ProgressBar));
|
|
124
|
+
PropertyControlService.registerComponentProp(EnumPropName.ToggleIsChecked, n => !!n.getComponent(cc.Toggle));
|
|
125
|
+
PropertyControlService.registerComponentProp(EnumPropName.RichTextString, n => !!n.getComponent(cc.RichText));
|
|
126
|
+
PropertyControlService.registerComponentProp(EnumPropName.ScrollViewEnabled, n => !!n.getComponent(cc.ScrollView));
|
|
127
|
+
PropertyControlService.registerComponentProp(EnumPropName.MaskEnabled, n => !!n.getComponent(cc.Mask));
|
|
128
|
+
|
|
129
|
+
// Widget 14 个字段都依赖 cc.Widget
|
|
130
|
+
const widgetProps = [
|
|
131
|
+
EnumPropName.WidgetEnabled,
|
|
132
|
+
EnumPropName.WidgetAlignMode,
|
|
133
|
+
EnumPropName.WidgetIsAlignTop,
|
|
134
|
+
EnumPropName.WidgetIsAlignBottom,
|
|
135
|
+
EnumPropName.WidgetIsAlignLeft,
|
|
136
|
+
EnumPropName.WidgetIsAlignRight,
|
|
137
|
+
EnumPropName.WidgetIsAlignHorizontalCenter,
|
|
138
|
+
EnumPropName.WidgetIsAlignVerticalCenter,
|
|
139
|
+
EnumPropName.WidgetTop,
|
|
140
|
+
EnumPropName.WidgetBottom,
|
|
141
|
+
EnumPropName.WidgetLeft,
|
|
142
|
+
EnumPropName.WidgetRight,
|
|
143
|
+
EnumPropName.WidgetHorizontalCenter,
|
|
144
|
+
EnumPropName.WidgetVerticalCenter,
|
|
145
|
+
];
|
|
146
|
+
for (const p of widgetProps) {
|
|
147
|
+
PropertyControlService.registerComponentProp(p, n => !!n.getComponent(cc.Widget));
|
|
148
|
+
}
|