| | |
| | | using System; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using Cysharp.Threading.Tasks; |
| | | public class RichText : Text, IPointerClickHandler |
| | | { |
| | | /// <summary> |
| | |
| | | set |
| | | { |
| | | m_AutoNewLine = value; |
| | | SetRichTextDirty(); |
| | | SetRichTextDirty().Forget(); |
| | | } |
| | | } |
| | | |
| | |
| | | unline = transform.GetComponentInChildren<TextUnline>(); |
| | | if (unline == null) |
| | | { |
| | | GameObject obj = Resources.Load("UIComp/TextUnline") as GameObject; |
| | | // GameObject obj = UILoader.LoadPrefab("TextUnline") as GameObject; |
| | | // GameObject obj = await BuiltInLoader.LoadPrefabAsync("TextUnline"); |
| | | // if (this == null) return; |
| | | // obj = Instantiate(obj); |
| | | // obj.transform.SetParent(transform); |
| | | // obj.transform.localScale = Vector3.one; |
| | | // unline = obj.GetComponent<TextUnline>(); |
| | | // unline.raycastTarget = false; |
| | | |
| | | BuiltInLoader.LoadPrefabAsync("TextUnline").ContinueWith(prefab => |
| | | { |
| | | if (this == null) return; |
| | | GameObject obj = Instantiate(prefab); |
| | | obj.transform.SetParent(transform); |
| | | obj.transform.localScale = Vector3.one; |
| | | unline = obj.GetComponent<TextUnline>(); |
| | | unline.raycastTarget = false; |
| | | }).Forget(); |
| | | } |
| | | } |
| | | |
| | | public async UniTask AwakeAsync() |
| | | { |
| | | #if UNITY_EDITOR |
| | | if (UnityEditor.PrefabUtility.GetPrefabType(this) == UnityEditor.PrefabType.Prefab) |
| | | { |
| | | return; |
| | | } |
| | | #endif |
| | | unline = transform.GetComponentInChildren<TextUnline>(); |
| | | if (unline == null) |
| | | { |
| | | GameObject obj = await BuiltInLoader.LoadPrefabAsync("TextUnline"); |
| | | if (this == null) return; |
| | | obj = Instantiate(obj); |
| | | obj.transform.SetParent(transform); |
| | | obj.transform.localScale = Vector3.one; |
| | |
| | | #endregion |
| | | |
| | | #region 解析 |
| | | private string GetOutputText(string _text) |
| | | private async UniTask<string> GetOutputText(string _text) |
| | | { |
| | | string result = _text; |
| | | result = GetExtenalData(result); |
| | | result = GetTaskInfo(result); |
| | | result = RichTextMgr.Inst.Analysis(result, out m_ImgList, out m_HrefList, this); |
| | | result = await RichTextMgr.Inst.Analysis(result, this); |
| | | m_ImgList = RichTextMgr.Inst.GetImgList(); |
| | | m_HrefList = RichTextMgr.Inst.GetHrefList(); |
| | | return result; |
| | | } |
| | | #endregion |
| | |
| | | { |
| | | font = FontUtility.preferred; |
| | | } |
| | | |
| | | // 添加 null 检查 |
| | | if (font == null || rectTransform == null || cachedTextGeneratorForLayout == null || string.IsNullOrEmpty(m_OutputText)) |
| | | { |
| | | return 0f; |
| | | } |
| | | |
| | | var settings = GetGenerationSettings(Vector2.zero); |
| | | return cachedTextGeneratorForLayout.GetPreferredWidth(m_OutputText, settings) / pixelsPerUnit; |
| | | |
| | | float width = 0f; |
| | | try |
| | | { |
| | | width = cachedTextGeneratorForLayout.GetPreferredWidth(m_OutputText, settings) / pixelsPerUnit; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError($"GetPreferredWidth failed: {ex.Message}"); |
| | | width = 0f; |
| | | } |
| | | return width; |
| | | } |
| | | } |
| | | |
| | |
| | | { |
| | | font = FontUtility.preferred; |
| | | } |
| | | |
| | | // 添加 null 检查 |
| | | if (font == null || rectTransform == null || cachedTextGeneratorForLayout == null || string.IsNullOrEmpty(m_OutputText)) |
| | | { |
| | | return 0f; |
| | | } |
| | | |
| | | var settings = GetGenerationSettings(new Vector2(rectTransform.rect.size.x, 0.0f)); |
| | | float _height = cachedTextGeneratorForLayout.GetPreferredHeight(m_OutputText, settings) / pixelsPerUnit; |
| | | return _height; |
| | | |
| | | float height = 0f; |
| | | try |
| | | { |
| | | height = cachedTextGeneratorForLayout.GetPreferredHeight(m_OutputText, settings) / pixelsPerUnit; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError($"GetPreferredHeight failed: {ex.Message}"); |
| | | height = 0f; |
| | | } |
| | | return height; |
| | | } |
| | | } |
| | | #endregion |
| | |
| | | public void SetReplaceInfo(Dictionary<string, string> _infoDic) |
| | | { |
| | | extenalDataDic = _infoDic; |
| | | SetRichTextDirty(); |
| | | SetRichTextDirty().Forget(); |
| | | } |
| | | |
| | | private string GetTaskInfo(string val) |
| | |
| | | #region 执行事件 |
| | | public void OnPointerClick(PointerEventData eventData) |
| | | { |
| | | // 检查组件是否已禁用或销毁,避免空指针解引用 |
| | | if (!this.isActiveAndEnabled) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | OnClick?.Invoke(); |
| | | if (HrefClick) |
| | | { |
| | | Vector2 lp; |
| | |
| | | } |
| | | } |
| | | } |
| | | if (OnClick != null) OnClick(); |
| | | } |
| | | |
| | | public void OnImgClick() |
| | |
| | | private static StringBuilder textBuilder = new StringBuilder(); |
| | | |
| | | private Dictionary<int, Match> matchDics = new Dictionary<int, Match>(); |
| | | |
| | | // 字符宽度缓存,避免重复计算相同字符 |
| | | private Dictionary<string, float> charWidthCache = new Dictionary<string, float>(); |
| | | |
| | | protected override void OnDisable() |
| | | { |
| | | base.OnDisable(); |
| | | // 组件禁用时清空缓存,防止内存泄漏 |
| | | charWidthCache.Clear(); |
| | | } |
| | | |
| | | private bool IsModifySize(int _index,out int _size) |
| | | { |
| | |
| | | { |
| | | font = FontUtility.preferred; |
| | | } |
| | | |
| | | // 添加 null 检查 |
| | | if (font == null || rectTransform == null || cachedTextGeneratorForLayout == null) |
| | | { |
| | | Debug.LogWarning("SetFitterSize: font, rectTransform or cachedTextGeneratorForLayout is null"); |
| | | return; |
| | | } |
| | | |
| | | var settings = GetGenerationSettings(Vector2.zero); |
| | | |
| | | float cache = 0; |
| | |
| | | float ratio = GetResolutionRatio(); |
| | | |
| | | matchDics.Clear(); |
| | | charWidthCache.Clear(); |
| | | |
| | | foreach (Match match in ImgAnalysis.Unity_Img_Regex.Matches(fitterText)) |
| | | { |
| | |
| | | } |
| | | else |
| | | { |
| | | cache = cachedTextGeneratorForLayout.GetPreferredWidth(match.Value, settings) * ratio; |
| | | cache = GetCharWidthCached(match.Value, settings, ratio); |
| | | |
| | | if (width + cache > (rectTransform.rect.width - 5)) |
| | | { |
| | | CacluHrefAndImgIndex(Mathf.Max(0, i - 1)); |
| | |
| | | else |
| | | { |
| | | var _size = 0; |
| | | var _cacheFontSize = fontSize; |
| | | // 不再修改 fontSize,使用临时 TextGenerator 计算不同字号的宽度 |
| | | if (_modifySize && IsModifySize(i, out _size)) |
| | | { |
| | | fontSize = _size; |
| | | settings = GetGenerationSettings(Vector2.zero); |
| | | cache = cachedTextGeneratorForLayout.GetPreferredWidth(fitterText[i].ToString(), settings) * ratio; |
| | | fontSize = _cacheFontSize; |
| | | settings = GetGenerationSettings(Vector2.zero); |
| | | cache = GetCharWidthWithCustomSize(fitterText[i], _size, ratio); |
| | | } |
| | | else |
| | | { |
| | | cache = cachedTextGeneratorForLayout.GetPreferredWidth(fitterText[i].ToString(), settings) * ratio; |
| | | cache = GetCharWidthCached(fitterText[i].ToString(), settings, ratio); |
| | | } |
| | | |
| | | if (width + cache > (rectTransform.rect.width - 5)) |
| | | { |
| | | CacluHrefAndImgIndex(Mathf.Max(0, i - 1)); |
| | |
| | | } |
| | | CacluHrefAndImgIndex(); |
| | | m_OutputText = textBuilder.ToString(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取字符宽度(带缓存) |
| | | /// </summary> |
| | | private float GetCharWidthCached(string charStr, TextGenerationSettings settings, float ratio) |
| | | { |
| | | string cacheKey = $"{fontSize}_{charStr}"; |
| | | |
| | | if (charWidthCache.ContainsKey(cacheKey)) |
| | | { |
| | | return charWidthCache[cacheKey]; |
| | | } |
| | | |
| | | float width = 0; |
| | | try |
| | | { |
| | | width = cachedTextGeneratorForLayout.GetPreferredWidth(charStr, settings) * ratio; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError($"GetPreferredWidth failed for '{charStr}': {ex.Message}"); |
| | | width = 0; |
| | | } |
| | | |
| | | charWidthCache[cacheKey] = width; |
| | | return width; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 使用自定义字号计算字符宽度(不修改原始 Text 组件的 fontSize) |
| | | /// </summary> |
| | | private float GetCharWidthWithCustomSize(char c, int customFontSize, float ratio) |
| | | { |
| | | string charStr = c.ToString(); |
| | | string cacheKey = $"{customFontSize}_{charStr}"; |
| | | |
| | | if (charWidthCache.ContainsKey(cacheKey)) |
| | | { |
| | | return charWidthCache[cacheKey]; |
| | | } |
| | | |
| | | float width = 0; |
| | | try |
| | | { |
| | | // 创建临时的 TextGenerationSettings,使用自定义字号 |
| | | TextGenerationSettings tempSettings = GetGenerationSettings(Vector2.zero); |
| | | tempSettings.fontSize = customFontSize; |
| | | |
| | | width = cachedTextGeneratorForLayout.GetPreferredWidth(charStr, tempSettings) * ratio; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError($"GetPreferredWidth failed for '{charStr}' with fontSize {customFontSize}: {ex.Message}"); |
| | | width = 0; |
| | | } |
| | | |
| | | charWidthCache[cacheKey] = width; |
| | | return width; |
| | | } |
| | | |
| | | private void CacluHrefAndImgIndex(int index) |
| | |
| | | value = string.Empty; |
| | | } |
| | | m_RichText = value; |
| | | SetRichTextDirty(); |
| | | SetRichTextDirty().Forget(); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | #endif |
| | | |
| | | private void SetRichTextDirty() |
| | | private async UniTask SetRichTextDirty() |
| | | { |
| | | m_RichText = UIHelper.ReplaceNewLine(m_RichText); |
| | | m_OutputText = GetOutputText(m_RichText); |
| | | m_OutputText = await GetOutputText(m_RichText); |
| | | if (AutoNewLine) |
| | | { |
| | | SetFitterSize(); |