using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using Sirenix.OdinInspector; using TMPro; using UnityEngine; using UnityEngine.Events; using XericLibrary.Runtime.MacroLibrary; using XericLibrary.Runtime.Type; namespace Deconstruction.UI.TmpText { /// /// TMP超链接点击管理组件 /// public class TMPHyperlinkManager : MonoBehaviour { /// /// 画布组件 /// #if ODIN_INSPECTOR [LabelText("画布*")] #endif [Tooltip("组件可为空,将自动向上查找")] [SerializeField] public Canvas canvas; #if ODIN_INSPECTOR [LabelText("点击超链接时回调")] #endif [SerializeField] public UnityEvent onClickLink = new UnityEvent(); /// /// 当前摄像机 /// private Camera _camera; /// /// 存储链接ID与对应回调函数的映射 /// private Dictionary _linkCallbacks = new Dictionary(); #if UNITY_EDITOR protected void OnValidate() { canvas = GetComponentInParent(); } #endif protected void Awake() { if (canvas == null) canvas = transform.GetParents().Startup(transform).GetComponent(); if (canvas == null) { Debug.LogError("TMP文本对象必须在Canvas下才能使用超链接功能"); return; } if (canvas.renderMode == RenderMode.ScreenSpaceOverlay) _camera = null; else _camera = canvas.worldCamera; } /// /// 注册链接回调函数 /// public void RegisterLinkCallback(string linkID, Action callback) { if (_linkCallbacks.ContainsKey(linkID)) { Debug.LogWarning($"链接ID '{linkID}' 已存在,将覆盖之前的回调函数"); _linkCallbacks[linkID] = callback; } else { _linkCallbacks.Add(linkID, callback); } } /// /// 注销链接回调函数 /// public void UnregisterLinkCallback(string linkID) { if (_linkCallbacks.ContainsKey(linkID)) { _linkCallbacks.Remove(linkID); } } /// /// 处理链接点击事件 /// public void HandleLinkClick(string linkID) { if (_linkCallbacks.TryGetValue(linkID, out Action callback)) { callback?.Invoke(); Debug.Log($"执行链接 '{linkID}' 的回调函数"); } else { Debug.LogWarning($"未找到链接ID '{linkID}' 的回调函数"); } } /// /// 检查坐标下的超链接索引 /// /// /// /// internal int CheckLinkIndex(TMP_Text text, Vector3 position) { return TMP_TextUtilities.FindIntersectingLink(text, position, _camera); } /// /// 检查坐标下的超链接id信息 /// /// /// /// /// internal bool CheckLinkInfo(TMP_Text text, Vector3 position, out TMP_LinkInfo linkInfo) { var index = CheckLinkIndex(text, position); if (index != -1) { linkInfo = text.textInfo.linkInfo[index]; return true; } linkInfo = default; return false; } /// /// 检查坐标下的超链接id /// /// /// /// /// internal bool CheckLinkID(TMP_Text text, Vector3 position, out string linkID) { if (CheckLinkInfo(text, position, out var info)) { linkID = info.GetLinkID(); return true; } linkID = string.Empty; return false; } /// /// 当任意tmp对象超链接点击时调用,从这里发送事件 /// /// internal void OnAnyLinkClick(string linkID) { onClickLink?.Invoke(linkID); // Debug.Log($"click link{linkID}"); // 这里不需要处理报错,应当由发送消息的成员处理 } // todo 主动查找所有的tmp组件,或是等组件唤醒后区管理它们,用来获得所有的带有标记的超链接组件 /// /// 主动查找所有具有link属性的tmp组件 /// /// 自动查找 /// public void FindAllID(bool requirementComponent = true, bool forceRefesh = false) { transform.GetChildrenBFS().GetComponentsOTON(); } private List _tempGetChildrenHyperlink = null; /// /// 获取所有链接子项 /// /// 强制刷新,如果刷新过一次,之后不论设定如何都将直接从缓存中返回对象,置位将跳过这个缓存 /// 包括重复标记(也就是是否包含另一个超链接管理器作用域下的超链接对象) public List GetChildrenHyperlink(bool forceRefesh = false, bool includeRepeatedMarking = false) { if (!forceRefesh && _tempGetChildrenHyperlink != null && _tempGetChildrenHyperlink.Count > 0) return _tempGetChildrenHyperlink; _tempGetChildrenHyperlink = transform.GetChildrenBFS() .GetComponentsOTON().ToList(); return _tempGetChildrenHyperlink; } private List _tempGetChildrenTmpText2Hyperlink = null; /// /// 获取所有tmp_text,然后根据规则返回链接 /// /// 强制刷新,如果刷新过一次,之后不论设定如何都将直接从缓存中返回对象,置位将跳过这个缓存 /// 强制给带有link标记的对象加上超链接组件 /// 包括重复标记(也就是是否包含另一个超链接管理器作用域下的超链接对象) /// public List GetChildrenTmpText2Hyperlink( bool forceRefesh = false, bool requirementHyperlink = true, bool includeRepeatedMarking = false) { if (!forceRefesh && _tempGetChildrenTmpText2Hyperlink != null && _tempGetChildrenTmpText2Hyperlink.Count > 0) return _tempGetChildrenTmpText2Hyperlink; var children = includeRepeatedMarking ? transform.GetChildrenBFS() : transform.GetChildrenBFS(); var result = new List(); var hyperlinkType = typeof(TMPHyperlinkReceiver); var tmpTextType = typeof(TMP_Text); // 预先分配足够的容量,减少扩容 result.Capacity = Mathf.Min(children.Count(), 10); foreach (var child in children) { // 尝试从缓存获取组件 var hyperlink = child.GetComponent(hyperlinkType) as TMPHyperlinkReceiver; if (hyperlink != null) { result.Add(hyperlink); continue; } if (!requirementHyperlink) continue; // 尝试获取TMP_Text组件 var tmp = child.GetComponent(tmpTextType) as TMP_Text; if (tmp != null && tmp.text.MatchRichTextLinkID()) { result.Add(child.gameObject.AddComponent()); } } return result; } } }