using System; using System.Collections.Generic; using UnityEngine; namespace YIUIFramework { /// /// 每个具体红点的 运行时数据 /// public partial class RedDotData { /// /// Key /// public int Key => Config.Key; /// /// 配置 /// public RedDotConfigData Config { get; private set; } /// /// 操作堆栈 /// public List StackList { get; private set; } /// /// 我的所有父级 /// public HashSet ParentList { get; private set; } /// /// 我的所有子级 /// public HashSet ChildList { get; private set; } /// /// 红点改变通知 /// private Action m_OnChangedAction; /// /// 当前是否提示 /// public bool Tips { get; private set; } /// /// 当前红点的真实数量 /// public int RealCount { get; private set; } /// /// 当前红点数量 如果关闭了红点永远=0 /// public int Count => Tips ? RealCount : 0; /// /// 本地存储的唯一K /// private string m_PlayerTipsKey; /// /// 本地存储的值 /// private BoolPrefs m_PlayerTipsKeyBoolPrefs; private RedDotData() { } internal RedDotData(RedDotConfigData config) { Config = config; ParentList = new HashSet(); ChildList = new HashSet(); InitTips(); #if UNITY_EDITOR || YIUIMACRO_REDDOT_STACK StackList = new List(); #endif } /// /// 初始化 当前红点是否可显示 /// private void InitTips() { //如果配置档中不允许开关提示 那么这个提示永远 = true 且不可修改 if (!Config.SwitchTips) { Tips = true; return; } m_PlayerTipsKey = $"PlayerRedDotTips_{Config.Key}"; m_PlayerTipsKeyBoolPrefs = new BoolPrefs(m_PlayerTipsKey, null, Config.SwitchTips); Tips = m_PlayerTipsKeyBoolPrefs.Value; } internal void DeletePlayerTipsPrefs() { m_PlayerTipsKeyBoolPrefs.Delete(); } /// /// 添加父级对象 /// internal bool AddParent(RedDotData data) { //所有父子关联关系 都在编辑器中检查完成 //要保证不会出现循环引用关系 ParentList.Add(data); //目标设定为我的父级 return data.AddChild(this); //因为他是我的父级所以我为他的子级 } /// /// 添加子对象 /// private bool AddChild(RedDotData data) { return ChildList.Add(data); } /// /// 这个是实时同步修改 /// 目前使用脏标修改 防止有人同步过快 /// 没有子级 说明是最后一级 最后一级才可以设置 /// internal bool TrySetCount(int count) { if (ChildList.Count <= 0) { return SetCount(count); } Debug.LogError($"{Config.Key} 配置 不是最后一级红点 请不要直接修改"); return false; } /// /// 设置当前红点数量 /// 返回值 false = 没有变化 /// private bool SetCount(int count, RedDotStack stack = null) { if (RealCount == count) { return false; } #if UNITY_EDITOR || YIUIMACRO_REDDOT_STACK if (stack == null) { var firstData = new FirstRedDotChangeData(this, RealCount, count, Tips); stack = AddNewStack(ERedDotOSType.Count, RealCount, count, Tips, firstData); } else { AddStack(stack); } #endif RealCount = count; NotifyChange(stack); return true; } /// /// 通知父级 自己变化了 /// 此处会递归 直到没有父级 /// private void NotifyChange(RedDotStack stack) { InvokeOnChanged(); foreach (var parent in ParentList) { parent.ChildChanged(stack); } } /// /// 子对象变化了 /// 然后自己变化 然后又通知递归 /// private void ChildChanged(RedDotStack stack) { var count = 0; foreach (var child in ChildList) { count += child.Count; } SetCount(count, stack); } /// /// 设置当前是否提示 /// internal bool SetTips(bool tips, RedDotStack stack = null) { if (!Config.SwitchTips) { Debug.LogError($"{Config.Key} 配置 关闭了红点的开关提示 目前永久提示 禁止修改 "); return false; } if (Tips == tips) { return false; } Tips = tips; #if UNITY_EDITOR || YIUIMACRO_REDDOT_STACK if (stack == null) { var firstData = new FirstRedDotChangeData(this, RealCount, Count, Tips); stack = AddNewStack(ERedDotOSType.Tips, RealCount, Count, Tips, firstData); } else { AddStack(stack); } #endif m_PlayerTipsKeyBoolPrefs.Value = tips; //存储到本地 NotifyChange(stack); //提示改变后 通知 监听改变 return true; } /// /// 添加一个回调 /// int = 当前红点数量 (非真实数量) /// 并马上设置相关数据 /// (因为界面初始化时都是需要刷新界面的 所以默认调用一次) /// internal void AddOnChanged(Action action) { m_OnChangedAction += action; InvokeOnChanged(); } /// /// 移除回调 /// /// internal void RemoveChanged(Action action) { m_OnChangedAction -= action; } //try回调 private void InvokeOnChanged() { try { //回调不会使用真实数量 m_OnChangedAction?.Invoke(Count); } catch (Exception e) { Debug.LogError($"红点改变回调 try报错 请检查原因 {Key} {e}"); } } #if UNITY_EDITOR || YIUIMACRO_REDDOT_STACK //因为有堆栈操作的需求 所以还是有可能 同一时间刷新2个节点 他们都有相同的路线时 会被刷新2次 //因为需要这2次的堆栈操作信息 如果去掉堆栈 可以合并 TODO /// /// 添加操作堆栈 /// 目前只有在编辑器下执行 以后可能修改成一个可开关的 这样在手机上也可以查看 debug模式 /// private RedDotStack AddNewStack( ERedDotOSType osType, int originalCount, int changeCount, bool changeTips, FirstRedDotChangeData firstData) { var stack = new RedDotStack { Id = StackList.Count + 1, DataTime = DateTime.Now, StackTrace = new System.Diagnostics.StackTrace(true), RedDotOSType = osType, OriginalCount = originalCount, ChangeCount = changeCount, ChangeTips = changeTips, FirstData = firstData, }; //新的在前 其他地方就不用翻转数据了 StackList.Insert(0, stack); return stack; } private void AddStack(RedDotStack stack) { StackList.Insert(0, stack); } #endif } }