-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathRedDotManager.cs
More file actions
309 lines (268 loc) · 10 KB
/
RedDotManager.cs
File metadata and controls
309 lines (268 loc) · 10 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
using UnityEngine;
using System;
using System.Collections.Generic;
namespace RedDotSystem
{
/// <summary>
/// Manages the entire red dot system. It's a singleton.
/// It builds the Trie from a ScriptableObject config and handles state changes.
/// </summary>
public class RedDotManager
{
private static readonly Lazy<RedDotManager> _instance = new Lazy<RedDotManager>(() => new RedDotManager());
public static RedDotManager Instance => _instance.Value;
private RedDotNode _root;
private Dictionary<string, Action<bool>> _eventHandlers = new Dictionary<string, Action<bool>>();
private RedDotSetting _setting;
private RedDotManager()
{
_root = new RedDotNode("Root");
}
/// <summary>
/// Initializes the manager by building the Trie from a ScriptableObject configuration.
/// </summary>
/// <param name="setting">The RedDotSetting ScriptableObject defining the red dot tree.</param>
public void Initialize(RedDotSetting setting)
{
if (setting == null)
{
HLogger.LogError("[RedDotManager] RedDotSetting is null!");
return;
}
_setting = setting;
// 验证配置
List<string> errors;
if (!_setting.ValidateConfiguration(out errors))
{
HLogger.LogError($"[RedDotManager] Configuration validation failed:\n{string.Join("\n", errors)}");
return;
}
// 清空现有树
_root = new RedDotNode("Root");
// 按层级深度排序,确保父节点先创建
var sortedPaths = _setting.GetAllPaths();
sortedPaths.Sort((a, b) => a.GetDepth().CompareTo(b.GetDepth()));
// 构建树结构
foreach (var pathData in sortedPaths)
{
CreateNodeFromPath(pathData);
}
HLogger.Log($"[RedDotManager] Red Dot System Initialized with {_setting.paths.Count} paths.");
}
/// <summary>
/// 从路径数据创建节点
/// </summary>
private void CreateNodeFromPath(RedDotPathData pathData)
{
if (string.IsNullOrEmpty(pathData.fullPath)) return;
var pathParts = pathData.fullPath.Split('/');
RedDotNode currentNode = _root;
// 逐级创建或获取节点
for (int i = 0; i < pathParts.Length; i++)
{
currentNode = currentNode.AddChild(pathParts[i]);
}
// 设置节点描述
currentNode.description = pathData.description;
}
/// <summary>
/// 从Resources文件夹加载配置并初始化
/// </summary>
/// <param name="resourcePath">Resources文件夹中的路径(不包含扩展名)</param>
public void InitializeFromResources(string resourcePath = "RedDotSetting")
{
var setting = Resources.Load<RedDotSetting>(resourcePath);
if (setting == null)
{
HLogger.LogError($"[RedDotManager] Could not load RedDotSetting from Resources/{resourcePath}");
return;
}
Initialize(setting);
}
/// <summary>
/// Sets the red dot state for a specific leaf node.
/// This is the primary way to trigger a red dot.
/// </summary>
/// <param name="path">The full path to the node (e.g., "Mail/System").</param>
/// <param name="show">True to show/increment, false to hide/decrement.</param>
public void SetRedDotState(string path, bool show)
{
RedDotNode node = GetNode(path);
if (node == null)
{
HLogger.LogWarning($"[RedDotManager] Path not found: {path}");
return;
}
if (node.HasChildren)
{
HLogger.LogWarning(
$"[RedDotManager] Cannot set state on a non-leaf node directly: {path}. State is determined by children.");
return;
}
node.ChangeCount(show ? 1 : -1);
}
/// <summary>
/// Gets the current red dot count for a given path.
/// </summary>
/// <param name="path">The full path to the node.</param>
/// <returns>The number of active red dots at or below this path.</returns>
public int GetRedDotCount(string path)
{
RedDotNode node = GetNode(path);
return node?.RedDotCount ?? 0;
}
/// <summary>
/// 检查指定路径是否有红点
/// </summary>
/// <param name="path">路径</param>
/// <returns>是否有红点</returns>
public bool HasRedDot(string path)
{
return GetRedDotCount(path) > 0;
}
/// <summary>
/// 获取所有有红点的叶子节点路径
/// </summary>
/// <returns>有红点的叶子节点路径列表</returns>
public List<string> GetActiveLeafPaths()
{
List<string> activePaths = new List<string>();
if (_setting != null)
{
foreach (string leafPath in _setting.GetAllLeafPaths())
{
if (HasRedDot(leafPath))
{
activePaths.Add(leafPath);
}
}
}
return activePaths;
}
/// <summary>
/// 清除指定路径的所有红点
/// </summary>
/// <param name="path">路径</param>
public void ClearRedDots(string path)
{
RedDotNode node = GetNode(path);
if (node == null) return;
// 如果是叶子节点,直接清除
if (!node.HasChildren)
{
if (node.RedDotCount > 0)
{
node.ChangeCount(-node.RedDotCount);
}
return;
}
// 如果是父节点,清除所有子叶子节点
if (_setting != null)
{
foreach (string leafPath in _setting.GetAllLeafPaths())
{
if (leafPath.StartsWith(path + "/") || leafPath == path)
{
var leafNode = GetNode(leafPath);
if (leafNode != null && leafNode.RedDotCount > 0)
{
leafNode.ChangeCount(-leafNode.RedDotCount);
}
}
}
}
}
/// <summary>
/// Registers a callback to be invoked when a node's red dot state changes.
/// </summary>
/// <param name="path">The path to listen to.</param>
/// <param name="callback">The action to execute. The boolean parameter is true if a red dot is active.</param>
public void Register(string path, Action<bool> callback)
{
if (!_eventHandlers.ContainsKey(path))
{
_eventHandlers[path] = delegate { };
}
_eventHandlers[path] += callback;
// Immediately invoke with current state
RedDotNode node = GetNode(path);
callback?.Invoke(node != null && node.RedDotCount > 0);
}
/// <summary>
/// Unregisters a callback.
/// </summary>
/// <param name="path">The path to stop listening to.</param>
/// <param name="callback">The specific action to remove.</param>
public void Unregister(string path, Action<bool> callback)
{
if (_eventHandlers.ContainsKey(path))
{
_eventHandlers[path] -= callback;
}
}
/// <summary>
/// 获取配置信息(只读)
/// </summary>
public RedDotSetting GetSetting()
{
return _setting;
}
/// <summary>
/// 重新加载配置
/// </summary>
public void ReloadConfiguration()
{
if (_setting != null)
{
Initialize(_setting);
}
}
internal void Notify(string path, bool isActive)
{
if (_eventHandlers.TryGetValue(path, out var handler))
{
handler?.Invoke(isActive);
}
}
private RedDotNode GetNode(string path)
{
if (string.IsNullOrEmpty(path)) return _root;
var keys = path.Split('/');
RedDotNode currentNode = _root;
foreach (var key in keys)
{
currentNode = currentNode.GetChild(key);
if (currentNode == null)
{
return null;
}
}
return currentNode;
}
/// <summary>
/// 调试用:打印当前红点树状态
/// </summary>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public void DebugPrintTree()
{
if (_setting == null)
{
HLogger.Log("[RedDotManager] No configuration loaded.");
return;
}
var sb = new System.Text.StringBuilder();
sb.AppendLine("=== Red Dot Tree Status ===");
var sortedPaths = _setting.GetAllPaths();
sortedPaths.Sort((a, b) => a.GetDepth().CompareTo(b.GetDepth()));
foreach (var pathData in sortedPaths)
{
string indent = new string(' ', pathData.GetDepth() * 2);
int count = GetRedDotCount(pathData.fullPath);
string status = count > 0 ? $"[{count}]" : "";
string leafMark = pathData.isLeaf ? "(叶子)" : "";
sb.AppendLine($"{indent}- {pathData.fullPath} {status} {leafMark}");
}
HLogger.Log(sb.ToString());
}
}
}