drgame-cc 1.0.45 → 1.0.46

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.
Files changed (121) hide show
  1. package/drres/focus/focus_1/gesture.atlas +34 -0
  2. package/drres/focus/focus_1/gesture.atlas.meta +6 -0
  3. package/drres/focus/focus_1/gesture.json +1 -0
  4. package/drres/focus/focus_1/gesture.json.meta +10 -0
  5. package/drres/focus/focus_1/gesture.png +0 -0
  6. package/drres/{icons/pointer-yellow-60.png.meta → focus/focus_1/gesture.png.meta} +14 -14
  7. package/drres/focus/focus_1.meta +13 -0
  8. package/drres/focus/focus_2/gesture.atlas +41 -0
  9. package/drres/focus/focus_2/gesture.atlas.meta +6 -0
  10. package/drres/focus/focus_2/gesture.json +1 -0
  11. package/drres/focus/focus_2/gesture.json.meta +10 -0
  12. package/drres/focus/focus_2/gesture.png +0 -0
  13. package/drres/{icons/pointer-blue-60.png.meta → focus/focus_2/gesture.png.meta} +13 -13
  14. package/drres/focus/focus_2.meta +13 -0
  15. package/drres/focus/focus_3/gesture.atlas +34 -0
  16. package/drres/focus/focus_3/gesture.atlas.meta +6 -0
  17. package/drres/focus/focus_3/gesture.json +1 -0
  18. package/drres/focus/focus_3/gesture.json.meta +10 -0
  19. package/drres/focus/focus_3/gesture.png +0 -0
  20. package/drres/{icons/pointer-pink-60.png.meta → focus/focus_3/gesture.png.meta} +12 -12
  21. package/drres/focus/focus_3.meta +13 -0
  22. package/drres/focus/focus_4/gesture.atlas +20 -0
  23. package/drres/focus/focus_4/gesture.atlas.meta +6 -0
  24. package/drres/focus/focus_4/gesture.json +1 -0
  25. package/drres/focus/focus_4/gesture.json.meta +10 -0
  26. package/drres/focus/focus_4/gesture.png +0 -0
  27. package/drres/focus/focus_4/gesture.png.meta +38 -0
  28. package/drres/focus/focus_4.meta +13 -0
  29. package/drres/focus.meta +13 -0
  30. package/drres/prefab/ExitWin.prefab +2 -2
  31. package/drres/prefab/Toast.prefab +378 -378
  32. package/drscript/{ad/AdManager.ts.meta → BtnExtra.ts.meta} +1 -1
  33. package/drscript/Define.ts +17 -2
  34. package/drscript/{ad/AdSlot.ts.meta → Define.ts.meta} +1 -1
  35. package/drscript/{ad/AggregateAdProvider.ts.meta → ExitWin.ts.meta} +1 -1
  36. package/drscript/GameAnalytics.ts +37 -118
  37. package/drscript/{ad/IAdProvider.ts.meta → GameAnalytics.ts.meta} +1 -1
  38. package/drscript/HelperWin.ts.meta +10 -0
  39. package/drscript/Info.ts.meta +10 -0
  40. package/drscript/InternalUitl.ts.meta +10 -0
  41. package/drscript/KeyAdapter.ts +133 -0
  42. package/drscript/KeyAdapter.ts.meta +10 -0
  43. package/drscript/Loader.ts.meta +10 -0
  44. package/drscript/PlatformAdapter.ts +178 -192
  45. package/drscript/PlatformAdapter.ts.meta +10 -0
  46. package/drscript/README.md +91 -805
  47. package/drscript/README.md.meta +6 -0
  48. package/drscript/UIAdapter.ts +324 -0
  49. package/drscript/UIAdapter.ts.meta +10 -0
  50. package/drscript/WebBridge.ts +1 -1
  51. package/drscript/WebBridge.ts.meta +10 -0
  52. package/drscript/ad-sdk/reporters/superset-lewo.js +6 -0
  53. package/drscript/ad-sdk/types/enums.d.ts +1 -0
  54. package/drscript/ad-sdk.meta +13 -0
  55. package/drscript/ad.meta +13 -0
  56. package/drscript/component/AlertTip.ts +25 -0
  57. package/drscript/component/AlertTip.ts.meta +10 -0
  58. package/drscript/component/BtnExtra.ts +17 -0
  59. package/drscript/component/BtnExtra.ts.meta +10 -0
  60. package/drscript/{ExitWin.ts → component/ExitWin.ts} +6 -8
  61. package/drscript/component/ExitWin.ts.meta +10 -0
  62. package/drscript/{HelperWin.ts → component/HelperWin.ts} +5 -19
  63. package/drscript/component/HelperWin.ts.meta +10 -0
  64. package/drscript/component.meta +13 -0
  65. package/drscript/config.meta +13 -0
  66. package/drscript/core.meta +13 -0
  67. package/drscript/focus/FocusItem.ts +1 -19
  68. package/drscript/focus/FocusManager.ts +132 -170
  69. package/drscript/focus/FocusVisualLayer.ts +74 -163
  70. package/drscript/focus.meta +13 -0
  71. package/drscript/input/KeyboardMove.ts +40 -11
  72. package/drscript/input/KeyboardMove.ts.meta +10 -0
  73. package/drscript/input.meta +13 -0
  74. package/drscript/interface.meta +13 -0
  75. package/drscript/pinyin.meta +13 -0
  76. package/drscript/toast.meta +13 -0
  77. package/drscript/virtualgamepad.meta +13 -0
  78. package/package.json +1 -1
  79. package/drres/config/default.json +0 -108
  80. package/drres/config/default.json.meta +0 -6
  81. package/drres/icons/pointer-blue-60.png +0 -0
  82. package/drres/icons/pointer-pink-60.png +0 -0
  83. package/drres/icons/pointer-white-60.png +0 -0
  84. package/drres/icons/pointer-yellow-60.png +0 -0
  85. package/drscript/Info.ts +0 -78
  86. package/drscript/InternalUitl.ts +0 -65
  87. package/drscript/ad/AdManager.ts +0 -174
  88. package/drscript/ad/AdSlot.ts +0 -44
  89. package/drscript/ad/AggregateAdProvider.ts +0 -149
  90. package/drscript/ad/IAdProvider.ts +0 -39
  91. package/drscript/ad/MockAdProvider.ts +0 -36
  92. package/drscript/ad/MockAdProvider.ts.meta +0 -10
  93. package/drscript/ad/SelfAdProvider.ts +0 -41
  94. package/drscript/ad/SelfAdProvider.ts.meta +0 -10
  95. package/drscript/config/TvUiConfig.ts +0 -155
  96. package/drscript/config/TvUiConfig.ts.meta +0 -10
  97. package/drscript/config/TvUiConfigManager.ts +0 -133
  98. package/drscript/config/TvUiConfigManager.ts.meta +0 -10
  99. package/drscript/core/BackManager.ts +0 -38
  100. package/drscript/core/BackManager.ts.meta +0 -10
  101. package/drscript/core/NativeBridge.ts +0 -137
  102. package/drscript/core/NativeBridge.ts.meta +0 -10
  103. package/drscript/core/TvUi.ts +0 -69
  104. package/drscript/core/TvUi.ts.meta +0 -10
  105. package/drscript/core/TvUiRoot.ts +0 -137
  106. package/drscript/core/TvUiRoot.ts.meta +0 -10
  107. package/drscript/icon/IconConfig.ts +0 -7
  108. package/drscript/icon/IconConfig.ts.meta +0 -10
  109. package/drscript/icon/IconManager.ts +0 -97
  110. package/drscript/icon/IconManager.ts.meta +0 -10
  111. package/drscript/input/KeyCodeAdapter.ts +0 -23
  112. package/drscript/input/RemoteInputManager.ts +0 -105
  113. package/drscript/interface/ILobbyBridge.ts +0 -5
  114. package/drscript/toast/ToastComponent.ts +0 -35
  115. package/drscript/toast/ToastComponent.ts.meta +0 -10
  116. package/drscript/toast/ToastManager.ts +0 -66
  117. package/drscript/toast/ToastManager.ts.meta +0 -10
  118. package/drscript/toast/TvExitConfirmDialog.ts +0 -334
  119. package/drscript/toast/TvExitConfirmDialog.ts.meta +0 -10
  120. package/drscript/toast/UIFunctions.ts +0 -150
  121. package/drscript/toast/UIFunctions.ts.meta +0 -10
@@ -1,116 +1,73 @@
1
- import { TvUiDirection } from "../config/TvUiConfig";
2
- import KeyCodeAdapter, { TvRemoteKey } from "../input/KeyCodeAdapter";
3
- import FocusScope from "./FocusScope";
4
-
5
- export interface FocusOptions {
6
- root?: cc.Node;
7
- scopeId?: string;
8
- focusId?: string;
9
- iconKey?: string;
10
- up?: string;
11
- down?: string;
12
- left?: string;
13
- right?: string;
14
- onClick?: Function;
15
- onFocus?: Function;
16
- onBlur?: Function;
17
- focusScale?: number;
18
- showCursor?: boolean;
19
- }
20
-
21
- export interface FocusCursorOptions {
22
- basePath?: string;
23
- normalPath?: string;
24
- focusPath?: string;
25
- corner?: string;
26
- width?: number;
27
- height?: number;
28
- offsetX?: number;
29
- offsetY?: number;
30
- }
1
+ import { KeyCode } from "../ad-sdk/ad-sdk";
31
2
 
32
3
  export interface FocusRuntimeItem {
33
4
  node: cc.Node;
34
- root?: cc.Node;
35
- scopeId: string;
36
- focusId: string;
37
- iconKey?: string;
38
- up?: string;
39
- down?: string;
40
- left?: string;
41
- right?: string;
42
- onClick?: Function;
43
5
  onFocus?: Function;
44
6
  onBlur?: Function;
45
- component?: any;
46
- baseScaleX: number;
47
- baseScaleY: number;
7
+ onClick?: Function;
48
8
  focusScale: number;
49
9
  showCursor: boolean;
10
+ offset: cc.Vec2;
50
11
  }
51
12
 
52
13
  type FocusListener = (item: FocusRuntimeItem | null, previous?: FocusRuntimeItem | null) => void;
53
14
 
15
+ interface ScopeInfo {
16
+ scopeId: string;
17
+ lastNodeUuid: string;
18
+ /** 绑定的窗口/场景节点(用于返回键自动处理) */
19
+ node?: cc.Node;
20
+ /** 节点类型 */
21
+ nodeType?: "window" | "scene";
22
+ /** 自定义返回回调 */
23
+ onBack?: () => boolean | void;
24
+ }
25
+
54
26
  export default class FocusManager {
55
27
  private static readonly FOCUS_SCALE_ACTION_TAG: number = 9911;
56
28
  private static readonly BASE_SCALE_X_KEY: string = "__tvFocusBaseScaleX";
57
29
  private static readonly BASE_SCALE_Y_KEY: string = "__tvFocusBaseScaleY";
58
30
  private static items: FocusRuntimeItem[] = [];
59
- private static itemMap: { [focusId: string]: FocusRuntimeItem } = {};
60
- private static currentFocusId: string = "";
61
- private static scopes: FocusScope[] = [new FocusScope("page")];
31
+ private static itemMap: { [uuid: string]: FocusRuntimeItem } = {};
32
+ private static scopeMap: { [uuid: string]: string } = {};
33
+ private static currentNodeUuid: string = "";
34
+ private static scopes: ScopeInfo[] = [{ scopeId: "page", lastNodeUuid: "" }];
62
35
  private static activeScopeId: string = "page";
63
36
  private static listeners: FocusListener[] = [];
64
- private static cursorOptions: FocusCursorOptions | null = null;
65
37
 
66
- public static setCursor(options: FocusCursorOptions | null) {
67
- this.cursorOptions = options;
68
- }
69
-
70
- public static getCursor(): FocusCursorOptions | null {
71
- return this.cursorOptions;
72
- }
38
+ private static readonly DEFAULT_FOCUS_SCALE = 1.08;
73
39
 
74
- public static register(target: cc.Node | any, options: FocusOptions = {}) {
40
+ public static register(target: cc.Node | any, options: { scopeId?: string; onFocus?: Function; onBlur?: Function; onClick?: Function; focusScale?: number; showCursor?: boolean; offset?: cc.Vec2 } = {}): void {
75
41
  let node = this.resolveNode(target);
76
42
  if (!node) return;
77
43
  let component = this.resolveComponent(target, node);
78
- let previousItem = this.findItemByNode(node);
79
- let previousBaseScale = previousItem ? { x: previousItem.baseScaleX, y: previousItem.baseScaleY } : null;
80
44
  this.unregister(node);
81
45
 
82
- let focusId = options.focusId || (component && component.focusId) || node.uuid || this.createFocusId(node);
83
- this.unregisterById(focusId);
84
- let baseScale = this.getNodeBaseScale(node, previousBaseScale);
46
+ let uuid = node.uuid;
47
+ this.getNodeBaseScale(node);
48
+
49
+ let scopeId = options.scopeId || (component && component.scopeId) || this.activeScopeId || "page";
50
+ this.scopeMap[uuid] = scopeId;
85
51
 
86
52
  let item: FocusRuntimeItem = {
87
53
  node: node,
88
- root: options.root,
89
- scopeId: options.scopeId || (component && component.scopeId) || this.activeScopeId || "page",
90
- focusId: focusId,
91
- iconKey: options.iconKey || (component && component.iconKey) || "",
92
- up: options.up || (component && component.up) || "",
93
- down: options.down || (component && component.down) || "",
94
- left: options.left || (component && component.left) || "",
95
- right: options.right || (component && component.right) || "",
96
- onClick: options.onClick,
97
54
  onFocus: options.onFocus || (component && component.onFocus),
98
55
  onBlur: options.onBlur || (component && component.onBlur),
99
- component: component,
100
- baseScaleX: baseScale.x,
101
- baseScaleY: baseScale.y,
102
- focusScale: options.focusScale || (component && component.focusScale) || 1.08,
103
- showCursor: options.showCursor !== false
56
+ onClick: options.onClick,
57
+ focusScale: options.focusScale || (component && component.focusScale) || this.DEFAULT_FOCUS_SCALE,
58
+ showCursor: options.showCursor !== false,
59
+ offset: options.offset || cc.v2(0, 0)
104
60
  };
105
61
 
106
62
  this.items.push(item);
107
- this.itemMap[item.focusId] = item;
63
+ this.itemMap[uuid] = item;
108
64
  this.ensureFocus();
109
65
  }
110
66
 
111
67
  public static unregister(target: cc.Node | any) {
112
68
  let node = this.resolveNode(target);
113
69
  if (!node) return;
70
+ delete this.scopeMap[node.uuid];
114
71
  for (let i = this.items.length - 1; i >= 0; i--) {
115
72
  if (this.items[i].node == node) {
116
73
  this.removeItemAt(i);
@@ -123,21 +80,25 @@ export default class FocusManager {
123
80
  if (!root) return;
124
81
  for (let i = this.items.length - 1; i >= 0; i--) {
125
82
  let item = this.items[i];
126
- if (item.root == root || this.isChildOf(item.node, root)) {
83
+ if (this.isChildOf(item.node, root)) {
84
+ delete this.scopeMap[item.node.uuid];
127
85
  this.removeItemAt(i);
128
86
  }
129
87
  }
130
88
  this.ensureFocus();
131
89
  }
132
90
 
133
- public static unregisterById(focusId: string) {
134
- if (!focusId) return;
91
+ private static unregisterByUuid(uuid: string) {
92
+ if (!uuid) return;
135
93
  for (let i = this.items.length - 1; i >= 0; i--) {
136
- if (this.items[i].focusId == focusId) this.removeItemAt(i);
94
+ if (this.items[i].node.uuid == uuid) {
95
+ delete this.scopeMap[uuid];
96
+ this.removeItemAt(i);
97
+ }
137
98
  }
138
99
  }
139
100
 
140
- public static registerButtons(root: cc.Node, options: FocusOptions = {}) {
101
+ public static registerButtons(root: cc.Node) {
141
102
  if (!root) return;
142
103
  this.unregisterByRoot(root);
143
104
  this.walk(root, (node: cc.Node) => {
@@ -145,31 +106,36 @@ export default class FocusManager {
145
106
  let button = node.getComponent(cc.Button);
146
107
  if (!button) return;
147
108
  this.register(node, {
148
- root: root,
149
- scopeId: options.scopeId || this.activeScopeId,
150
- iconKey: options.iconKey,
151
- focusScale: options.focusScale || 1.08,
109
+ scopeId: this.activeScopeId,
110
+ focusScale: this.DEFAULT_FOCUS_SCALE,
152
111
  onClick: () => this.invokeButton(button)
153
112
  });
154
113
  });
114
+
155
115
  this.ensureFocus();
156
116
  }
157
117
 
158
- public static pushScope(scopeId: string, root?: cc.Node, firstFocus?: string | cc.Node) {
118
+ public static pushScope(scopeId: string, options?: { node?: cc.Node; nodeType?: "window" | "scene"; onBack?: () => boolean | void; skipEnsureFocus?: boolean }) {
159
119
  scopeId = scopeId || "modal";
160
120
  let currentScope = this.getActiveScope();
161
- if (currentScope) currentScope.lastFocusId = this.currentFocusId;
121
+ if (currentScope) currentScope.lastNodeUuid = this.currentNodeUuid;
162
122
  for (let i = 0; i < this.scopes.length; i++) {
163
123
  if (this.scopes[i].scopeId == scopeId) {
164
124
  this.scopes.splice(i, 1);
165
125
  break;
166
126
  }
167
127
  }
168
- this.scopes.push(new FocusScope(scopeId, root));
128
+ this.scopes.push({ scopeId, lastNodeUuid: "", node: options?.node, nodeType: options?.nodeType, onBack: options?.onBack });
169
129
  this.activeScopeId = scopeId;
170
- this.setFocusById("", true);
171
- if (firstFocus) this.focus(firstFocus);
172
- this.ensureFocus();
130
+ this.setFocusByUuid("", true);
131
+ if (!options?.skipEnsureFocus) this.ensureFocus();
132
+ }
133
+
134
+ /** 移除作用域并清理其下所有注册焦点 */
135
+ public static removeScope(node: cc.Node) {
136
+ if (!node) return;
137
+ this.popScope(node.uuid);
138
+ this.unregisterByRoot(node);
173
139
  }
174
140
 
175
141
  public static popScope(scopeId?: string) {
@@ -181,22 +147,42 @@ export default class FocusManager {
181
147
  break;
182
148
  }
183
149
  }
150
+ // 清理已弹出作用域下的所有条目
151
+ for (let i = this.items.length - 1; i >= 0; i--) {
152
+ if (this.scopeMap[this.items[i].node.uuid] == target) {
153
+ this.removeItemAt(i);
154
+ }
155
+ }
184
156
  let active = this.scopes[this.scopes.length - 1];
185
157
  this.activeScopeId = active ? active.scopeId : "page";
186
- let restored = active && active.lastFocusId ? this.focus(active.lastFocusId) : false;
158
+ let restored = active && active.lastNodeUuid ? this.focus(active.lastNodeUuid) : false;
187
159
  if (!restored) {
188
- this.setFocusById("", true);
160
+ this.setFocusByUuid("", true);
189
161
  this.ensureFocus();
190
162
  }
191
163
  }
192
164
 
165
+ /** 获取最顶层绑定了节点的作用域(排除 page) */
166
+ public static getTopNodeScope(): ScopeInfo | null {
167
+ for (let i = this.scopes.length - 1; i >= 1; i--) {
168
+ if (this.scopes[i].node) return this.scopes[i];
169
+ }
170
+ return null;
171
+ }
172
+
173
+ /** 获取所有作用域(只读副本) */
174
+ public static getScopes(): ReadonlyArray<ScopeInfo> {
175
+ return this.scopes;
176
+ }
177
+
193
178
  public static reset() {
194
179
  let old = this.getCurrentFocusItem();
195
180
  if (old) this.setFocusVisual(old, false, true);
196
181
  this.items = [];
197
182
  this.itemMap = {};
198
- this.currentFocusId = "";
199
- this.scopes = [new FocusScope("page")];
183
+ this.scopeMap = {};
184
+ this.currentNodeUuid = "";
185
+ this.scopes = [{ scopeId: "page", lastNodeUuid: "" }];
200
186
  this.activeScopeId = "page";
201
187
  this.emitFocusChanged(null, old);
202
188
  }
@@ -216,15 +202,11 @@ export default class FocusManager {
216
202
  }
217
203
  }
218
204
  if (!item || !this.isItemActive(item) || !this.isInActiveScope(item)) return false;
219
- this.setFocusById(item.focusId);
205
+ this.setFocusByUuid(item.node.uuid);
220
206
  return true;
221
207
  }
222
208
 
223
- public static focusNode(node: cc.Node): boolean {
224
- return this.focus(node);
225
- }
226
-
227
- public static move(direction: TvUiDirection): boolean {
209
+ public static move(direction: KeyCode): boolean {
228
210
  this.cleanup();
229
211
  let current = this.getCurrentFocusItem();
230
212
  if (!current) {
@@ -232,22 +214,19 @@ export default class FocusManager {
232
214
  return !!this.getCurrentFocusItem();
233
215
  }
234
216
 
235
- let explicitId = current[direction];
236
- if (explicitId && this.focus(explicitId)) return true;
237
-
238
217
  let vector = this.getDirectionVector(direction);
239
- let currentCenter = this.getWorldCenter(current.node);
218
+ let currentCenter = this.getWorldCenter(current.node).add(current.offset);
240
219
  let best: FocusRuntimeItem | null = null;
241
220
  let bestScore = Number.MAX_VALUE;
242
221
 
243
222
  for (let i = 0; i < this.items.length; i++) {
244
223
  let item = this.items[i];
245
224
  if (item == current || !this.isItemActive(item) || !this.isInActiveScope(item)) continue;
246
- let center = this.getWorldCenter(item.node);
225
+ let center = this.getWorldCenter(item.node).add(item.offset);
247
226
  let dx = center.x - currentCenter.x;
248
227
  let dy = center.y - currentCenter.y;
249
- if (vector.x != 0 && dx * vector.x <= 8) continue;
250
- if (vector.y != 0 && dy * vector.y <= 8) continue;
228
+ if (vector.x != 0 && dx * vector.x <= 0) continue;
229
+ if (vector.y != 0 && dy * vector.y <= 0) continue;
251
230
  let primary = vector.x != 0 ? Math.abs(dx) : Math.abs(dy);
252
231
  let secondary = vector.x != 0 ? Math.abs(dy) : Math.abs(dx);
253
232
  let score = primary + secondary * 2;
@@ -258,7 +237,7 @@ export default class FocusManager {
258
237
  }
259
238
 
260
239
  if (best) {
261
- this.setFocusById(best.focusId);
240
+ this.setFocusByUuid(best.node.uuid);
262
241
  return true;
263
242
  }
264
243
  return this.moveByOrder(direction == "left" || direction == "down" ? -1 : 1);
@@ -272,40 +251,18 @@ export default class FocusManager {
272
251
  item.onClick();
273
252
  return true;
274
253
  }
275
- if (item.component && item.component.onConfirm) {
276
- item.component.onConfirm();
254
+ let focusItem = item.node.getComponent("FocusItem") as any;
255
+ if (focusItem && focusItem.onConfirm) {
256
+ focusItem.onConfirm();
277
257
  return true;
278
258
  }
279
259
  this.invokeButton(item.node.getComponent(cc.Button));
280
260
  return true;
281
261
  }
282
262
 
283
- public static back(): boolean {
284
- return false;
285
- }
286
-
287
- public static handleKey(keyCode: number): boolean {
288
- let key = KeyCodeAdapter.toRemoteKey(keyCode);
289
- if (key == TvRemoteKey.Ok) return this.confirm();
290
- if (key == TvRemoteKey.Left) return this.move("left");
291
- if (key == TvRemoteKey.Right) return this.move("right");
292
- if (key == TvRemoteKey.Up) return this.move("up");
293
- if (key == TvRemoteKey.Down) return this.move("down");
294
- if (key == TvRemoteKey.Back) return this.back();
295
- return false;
296
- }
297
-
298
- public static clickCurrent(): boolean {
299
- return this.confirm();
300
- }
301
-
302
263
  public static getCurrentFocusItem(): FocusRuntimeItem | null {
303
- let item = this.itemMap[this.currentFocusId];
304
- return this.isItemActive(item) ? item : null;
305
- }
306
-
307
- public static getActiveScopeId(): string {
308
- return this.activeScopeId;
264
+ let item = this.itemMap[this.currentNodeUuid];
265
+ return (item && this.isItemActive(item) && this.isInActiveScope(item)) ? item : null;
309
266
  }
310
267
 
311
268
  public static onFocusChanged(listener: FocusListener) {
@@ -318,8 +275,8 @@ export default class FocusManager {
318
275
  if (index >= 0) this.listeners.splice(index, 1);
319
276
  }
320
277
 
321
- private static setFocusById(focusId: string, silent: boolean = false) {
322
- if (focusId == this.currentFocusId && focusId) {
278
+ private static setFocusByUuid(uuid: string, silent: boolean = false) {
279
+ if (uuid == this.currentNodeUuid && uuid) {
323
280
  let current = this.getCurrentFocusItem();
324
281
  if (current && !silent) {
325
282
  this.setFocusVisual(current, true);
@@ -329,11 +286,11 @@ export default class FocusManager {
329
286
  }
330
287
  let previous: FocusRuntimeItem | null = this.getCurrentFocusItem();
331
288
  if (previous) this.setFocusVisual(previous, false);
332
- this.currentFocusId = focusId || "";
289
+ this.currentNodeUuid = uuid || "";
333
290
  let current: FocusRuntimeItem | null = this.getCurrentFocusItem();
334
291
  if (current) {
335
292
  let scope = this.getActiveScope();
336
- if (scope) scope.lastFocusId = current.focusId;
293
+ if (scope) scope.lastNodeUuid = current.node.uuid;
337
294
  this.setFocusVisual(current, true);
338
295
  }
339
296
  if (!silent) this.emitFocusChanged(current, previous);
@@ -344,8 +301,9 @@ export default class FocusManager {
344
301
  if (!item || !item.node || !item.node.isValid) return;
345
302
  this.setButtonSpriteVisual(item, focused);
346
303
  item.node.stopActionByTag(this.FOCUS_SCALE_ACTION_TAG);
347
- let targetScaleX = focused ? item.baseScaleX * item.focusScale : item.baseScaleX;
348
- let targetScaleY = focused ? item.baseScaleY * item.focusScale : item.baseScaleY;
304
+ let baseScale = this.getNodeBaseScale(item.node);
305
+ let targetScaleX = focused ? baseScale.x * item.focusScale : baseScale.x;
306
+ let targetScaleY = focused ? baseScale.y * item.focusScale : baseScale.y;
349
307
  if (immediate) {
350
308
  item.node.setScale(targetScaleX, targetScaleY);
351
309
  } else {
@@ -353,8 +311,8 @@ export default class FocusManager {
353
311
  action.setTag(this.FOCUS_SCALE_ACTION_TAG);
354
312
  item.node.runAction(action);
355
313
  }
356
- if (focused && item.onFocus) item.onFocus.call(item.component || item.node);
357
- if (!focused && item.onBlur) item.onBlur.call(item.component || item.node);
314
+ if (focused && item.onFocus) item.onFocus();
315
+ if (!focused && item.onBlur) item.onBlur();
358
316
  }
359
317
 
360
318
  private static setButtonSpriteVisual(item: FocusRuntimeItem, focused: boolean) {
@@ -368,18 +326,33 @@ export default class FocusManager {
368
326
  if (spriteFrame) sprite.spriteFrame = spriteFrame;
369
327
  }
370
328
 
329
+ private static _ensureFocusPending: boolean = false;
330
+
371
331
  private static ensureFocus() {
372
332
  this.cleanup();
373
333
  if (this.getCurrentFocusItem()) return;
374
334
  let activeScope = this.getActiveScope();
375
- if (activeScope && activeScope.lastFocusId && this.focus(activeScope.lastFocusId)) return;
335
+ if (activeScope && activeScope.lastNodeUuid && this.focus(activeScope.lastNodeUuid)) return;
376
336
  for (let i = 0; i < this.items.length; i++) {
377
337
  let item = this.items[i];
378
338
  if (this.isItemActive(item) && this.isInActiveScope(item)) {
379
- this.setFocusById(item.focusId);
339
+ this.setFocusByUuid(item.node.uuid);
380
340
  return;
381
341
  }
382
342
  }
343
+ // 有注册的按钮但暂无活跃节点,可能是节点尚未完全初始化,延迟重试
344
+ if (this.items.length > 0 && !this._ensureFocusPending) {
345
+ this._ensureFocusPending = true;
346
+ setTimeout(() => {
347
+ this._ensureFocusPending = false;
348
+ this.ensureFocus();
349
+ }, 50);
350
+ }
351
+ // 既无注册项也无焦点,清除焦点状态
352
+ if (this.items.length === 0) {
353
+ this.currentNodeUuid = "";
354
+ this.emitFocusChanged(null, null);
355
+ }
383
356
  }
384
357
 
385
358
  private static moveByOrder(step: number): boolean {
@@ -390,13 +363,13 @@ export default class FocusManager {
390
363
  if (activeItems.length <= 0) return false;
391
364
  let index = 0;
392
365
  for (let i = 0; i < activeItems.length; i++) {
393
- if (activeItems[i].focusId == this.currentFocusId) {
366
+ if (activeItems[i].node.uuid == this.currentNodeUuid) {
394
367
  index = i;
395
368
  break;
396
369
  }
397
370
  }
398
371
  let next = (index + step + activeItems.length) % activeItems.length;
399
- this.setFocusById(activeItems[next].focusId);
372
+ this.setFocusByUuid(activeItems[next].node.uuid);
400
373
  return true;
401
374
  }
402
375
 
@@ -404,10 +377,11 @@ export default class FocusManager {
404
377
  let item = this.items[index];
405
378
  if (!item) return;
406
379
  this.setFocusVisual(item, false, true);
407
- delete this.itemMap[item.focusId];
380
+ delete this.itemMap[item.node.uuid];
381
+ delete this.scopeMap[item.node.uuid];
408
382
  this.items.splice(index, 1);
409
- if (this.currentFocusId == item.focusId) {
410
- this.currentFocusId = "";
383
+ if (this.currentNodeUuid == item.node.uuid) {
384
+ this.currentNodeUuid = "";
411
385
  this.emitFocusChanged(null, item);
412
386
  }
413
387
  }
@@ -426,10 +400,10 @@ export default class FocusManager {
426
400
  }
427
401
 
428
402
  private static isInActiveScope(item?: FocusRuntimeItem | null): boolean {
429
- return !!item && item.scopeId == this.activeScopeId;
403
+ return !!item && this.scopeMap[item.node.uuid] == this.activeScopeId;
430
404
  }
431
405
 
432
- private static getActiveScope(): FocusScope | null {
406
+ private static getActiveScope(): ScopeInfo | null {
433
407
  for (let i = this.scopes.length - 1; i >= 0; i--) {
434
408
  if (this.scopes[i].scopeId == this.activeScopeId) return this.scopes[i];
435
409
  }
@@ -441,10 +415,10 @@ export default class FocusManager {
441
415
  return cc.v2(box.x + box.width / 2, box.y + box.height / 2);
442
416
  }
443
417
 
444
- private static getDirectionVector(direction: TvUiDirection): cc.Vec2 {
445
- if (direction == "left") return cc.v2(-1, 0);
446
- if (direction == "right") return cc.v2(1, 0);
447
- if (direction == "up") return cc.v2(0, 1);
418
+ private static getDirectionVector(direction: KeyCode): cc.Vec2 {
419
+ if (direction == KeyCode.Left) return cc.v2(-1, 0);
420
+ if (direction == KeyCode.Right) return cc.v2(1, 0);
421
+ if (direction == KeyCode.Up) return cc.v2(0, 1);
448
422
  return cc.v2(0, -1);
449
423
  }
450
424
 
@@ -479,15 +453,7 @@ export default class FocusManager {
479
453
  return target;
480
454
  }
481
455
 
482
- private static findItemByNode(node: cc.Node): FocusRuntimeItem | null {
483
- if (!node) return null;
484
- for (let i = 0; i < this.items.length; i++) {
485
- if (this.items[i].node == node) return this.items[i];
486
- }
487
- return null;
488
- }
489
-
490
- private static getNodeBaseScale(node: cc.Node, preferred?: { x: number, y: number } | null): { x: number, y: number } {
456
+ private static getNodeBaseScale(node: cc.Node): { x: number, y: number } {
491
457
  let nodeAny: any = node as any;
492
458
  let storedX = nodeAny[this.BASE_SCALE_X_KEY];
493
459
  let storedY = nodeAny[this.BASE_SCALE_Y_KEY];
@@ -495,7 +461,7 @@ export default class FocusManager {
495
461
  return { x: storedX, y: storedY };
496
462
  }
497
463
 
498
- let baseScale = preferred || {
464
+ let baseScale = {
499
465
  x: this.readScale(node, "scaleX"),
500
466
  y: this.readScale(node, "scaleY")
501
467
  };
@@ -516,10 +482,6 @@ export default class FocusManager {
516
482
  return typeof value == "number" && isFinite(value);
517
483
  }
518
484
 
519
- private static createFocusId(node: cc.Node): string {
520
- return "focus_" + (node.name || "node") + "_" + this.items.length;
521
- }
522
-
523
485
  private static walk(node: cc.Node, visitor: (node: cc.Node) => void) {
524
486
  visitor(node);
525
487
  for (let i = 0; i < node.childrenCount; i++) {
@@ -535,4 +497,4 @@ export default class FocusManager {
535
497
  }
536
498
  return false;
537
499
  }
538
- }
500
+ }