using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;
using System.Linq;
using System.Text;
public class RichText : Text, IPointerClickHandler
{
///
/// 最终显示文本
///
private string m_OutputText;
private string m_RichText = string.Empty;
public Action OnClick;
#region 对外参数
[SerializeField]
private float m_ImgDeltay = 0;
public float ImgDeltay
{
get
{
return m_ImgDeltay;
}
set
{
m_ImgDeltay = value;
SetVerticesDirty();
}
}
[SerializeField]
private float m_unlineDeltay = 0;
public float UnlineDeltay
{
get
{
return m_unlineDeltay;
}
set
{
m_unlineDeltay = value;
}
}
[SerializeField]
private float m_unlineHeight = 1;
public float UnlineHeight
{
get
{
return m_unlineHeight;
}
set
{
m_unlineHeight = value;
}
}
[SerializeField]
private float m_faceSize = 32;
public float FaceSize
{
get
{
return m_faceSize;
}
set
{
m_faceSize = value;
}
}
[SerializeField]
private bool m_unline = true;
public bool Unline
{
get { return m_unline; }
set { m_unline = value; }
}
[SerializeField]
private bool m_HrefClick = true;
public bool HrefClick
{
get { return m_HrefClick; }
set { m_HrefClick = value; }
}
[SerializeField]
private string m_EnableDisplay;
public string enableDisplay
{
get { return m_EnableDisplay; }
set { m_EnableDisplay = value; }
}
[SerializeField]
private bool m_Language = false;
public bool language
{
get { return m_Language; }
set { m_Language = value; }
}
[SerializeField]
private bool m_AutoNewLine = false;
public bool AutoNewLine
{
get
{
return m_AutoNewLine;
}
set
{
m_AutoNewLine = value;
SetRichTextDirty();
}
}
[SerializeField]
private bool m_LockImgSize = false;
public bool LockImgSize
{
get
{
return m_LockImgSize;
}
set
{
m_LockImgSize = value;
}
}
[SerializeField]
private bool m_ModifyImgSize = false;
public bool ModifyImgSiez
{
get { return m_ModifyImgSize; }
set { m_ModifyImgSize = value; }
}
[SerializeField]
private float m_ModifyImgWidth = 0;
public float ModifyImgWidth
{
get { return m_ModifyImgWidth; }
set { m_ModifyImgWidth = value; }
}
[SerializeField]
private float m_ModifyImgHeight = 0;
public float ModifyImgHeight
{
get { return m_ModifyImgHeight; }
set { m_ModifyImgHeight = value; }
}
[SerializeField]
private ColorType m_ColorType = ColorType.Dark;
public ColorType colorType
{
get { return m_ColorType; }
set { m_ColorType = value; }
}
public enum ColorType
{
Dark,
Bright,
}
#endregion
protected override void Awake()
{
#if UNITY_EDITOR
if (UnityEditor.PrefabUtility.GetPrefabType(this) == UnityEditor.PrefabType.Prefab)
{
return;
}
#endif
unline = transform.GetComponentInChildren();
if (unline == null)
{
GameObject obj = UILoader.LoadPrefab("TextUnline") as GameObject;
obj = Instantiate(obj);
obj.transform.SetParent(transform);
obj.transform.localScale = Vector3.one;
unline = obj.GetComponent();
unline.raycastTarget = false;
}
}
protected override void OnEnable()
{
base.OnEnable();
#if UNITY_EDITOR
if (UnityEditor.PrefabUtility.GetPrefabType(this) == UnityEditor.PrefabType.Prefab)
{
return;
}
#endif
if (language && !string.IsNullOrEmpty(enableDisplay) && Application.isPlaying && ConfigInitiator.done)
{
text = Language.Get(enableDisplay);
}
}
#region 绘制
private UIVertex vert = new UIVertex();
protected override void OnPopulateMesh(VertexHelper toFill)
{
if (font == null) return;
toFill.Clear();
base.OnPopulateMesh(toFill);
CalcBounds(toFill);
#region 修改图片位置
if (m_ImgList == null) return;
for (int i = 0; i < m_ImgList.Count; i++)
{
int _imgEnd = m_ImgList[i].end;
RectTransform rt = m_ImgPool[i].rectTransform;
Vector2 _imgSize = rt.sizeDelta;
if (_imgEnd < toFill.currentIndexCount)
{
try
{
toFill.PopulateUIVertex(ref vert, _imgEnd);
rt.localPosition = new Vector2(vert.position.x + _imgSize.x / 2, vert.position.y + _imgSize.y / 2 - m_ImgDeltay);
toFill.PopulateUIVertex(ref vert, _imgEnd - 3);
Vector3 pos = vert.position;
for (int j = _imgEnd, k = _imgEnd - 3; j >= k; j--)
{
toFill.PopulateUIVertex(ref vert, j);
pos.y = Mathf.Min(pos.y, vert.position.y - ImgDeltay);
vert.position = pos;
toFill.SetUIVertex(vert, j);
}
}
catch (System.Exception)
{
continue;
}
}
}
#endregion
}
public override void SetVerticesDirty()
{
base.SetVerticesDirty();
#if UNITY_EDITOR
if (UnityEditor.PrefabUtility.GetPrefabType(this) == UnityEditor.PrefabType.Prefab)
{
return;
}
#endif
//m_OutputText = GetOutputText(m_RichText);
GenerateImg();
}
#endregion
#region 解析
private 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);
return result;
}
#endregion
#region 图片
private List m_ImgList;
private List m_ImgPool = new List();
private void GenerateImg()
{
if (m_ImgList == null) return;
m_ImgPool.Clear();
GetComponentsInChildren(true, m_ImgPool);
for (int i = 0; i < m_ImgList.Count; i++)
{
RichTextMgr.ImgInfo imgInfo = m_ImgList[i];
Image img;
if (i >= m_ImgPool.Count)
{
img = CreateImg();
m_ImgPool.Add(img);
}
else
{
img = m_ImgPool[i];
}
img.SetActive(true);
img.raycastTarget = HrefClick;
img.sprite = imgInfo.sprite;
Vector2 _imgSize = new Vector2(imgInfo.width, imgInfo.height);
img.rectTransform.sizeDelta = _imgSize;
UIFrame frame = img.GetComponent();
if (imgInfo.IsFace)
{
if (frame == null) frame = img.gameObject.AddComponent();
img.raycastTarget = false;
frame.ResetFrame(imgInfo.spriteName);
frame.enabled = true;
}
else
{
if (frame != null) frame.enabled = false;
}
}
for (int i = m_ImgPool.Count - 1; i >= m_ImgList.Count; i--)
{
m_ImgPool[i].SetActive(false);
}
}
private Image CreateImg()
{
DefaultControls.Resources res = new DefaultControls.Resources();
GameObject go = DefaultControls.CreateImage(res);
go.layer = gameObject.layer;
RectTransform rt = go.transform as RectTransform;
if (rt != null)
{
rt.SetParent(rectTransform);
rt.localPosition = Vector3.zero;
rt.localRotation = Quaternion.identity;
rt.localScale = Vector3.one;
}
Image img = go.GetComponent();
return img;
}
#endregion
#region 下划线||超链接
[SerializeField]
private TextUnline unline;
private List m_HrefList;
private Dictionary m_UnlineBottomDict = new Dictionary();
private float GetUnlineHeight(int _line)
{
foreach (var _key in m_UnlineBottomDict.Keys)
{
if (_line <= _key)
{
return m_UnlineBottomDict[_key];
}
}
return m_UnlineBottomDict.Values.Last();
}
public string GetHrefMessage(string _key)
{
if (m_HrefList != null)
{
for (int i = 0; i < m_HrefList.Count; i++)
{
if (m_HrefList[i].mSplits.ContainsKey(_key))
{
return m_HrefList[i].mSplits[_key];
}
}
}
return string.Empty;
}
private void CalcBounds(VertexHelper toFill)
{
if (unline != null) unline.ClearVert();
if (m_HrefList == null) return;
float minBottom = 0;
if (!Unline)
{
return;
}
m_UnlineBottomDict.Clear();
int _line = 0;
bool _note = false;
foreach (RichTextMgr.HrefInfo hrefInfo in m_HrefList)
{
CalcUnline(toFill, hrefInfo);
if (!hrefInfo.unline)
{
continue;
}
hrefInfo.lines.Clear();
for (int i = 0; i < hrefInfo.boxs.Count; i++)
{
Vector3 start = hrefInfo.boxs[i].position;
if (minBottom == 0 && !_note)
{
minBottom = start.y;
_note = true;
}
if (Math.Abs(minBottom - start.y) >= fontSize / 2)
{
m_UnlineBottomDict.Add(_line, minBottom);
minBottom = start.y;
_line++;
}
else
{
minBottom = Mathf.Min(start.y, minBottom);
}
hrefInfo.lines.Add(_line);
}
}
m_UnlineBottomDict.Add(_line, minBottom);
foreach (RichTextMgr.HrefInfo hrefInfo in m_HrefList)
{
if (!hrefInfo.unline)
{
continue;
}
for (int i = 0; i < hrefInfo.boxs.Count; i++)
{
Vector3 start = hrefInfo.boxs[i].position;
var _unlineline = i < hrefInfo.lines.Count ? hrefInfo.lines[i] : 0;
start.y = GetUnlineHeight(_unlineline);
Vector3 end = start;
end.x += hrefInfo.boxs[i].width;
unline.SetUIVertex(start, end, -UnlineDeltay, m_unlineHeight, hrefInfo.unlineColor);
}
}
}
private void CalcUnline(VertexHelper toFill, RichTextMgr.HrefInfo hrefInfo)
{
hrefInfo.boxs.Clear();
if (hrefInfo.start >= toFill.currentIndexCount) return;
try
{
toFill.PopulateUIVertex(ref vert, hrefInfo.start);
}
catch (System.Exception)
{
return;
}
Vector3 pos = vert.position;
var realStart = hrefInfo.start;
///-----为了过滤颜色导致多次添加包围盒
while (realStart <= hrefInfo.end)
{
try
{
toFill.PopulateUIVertex(ref vert, realStart);
if (pos != vert.position)
{
if (realStart <= hrefInfo.start + 1)
{
realStart = hrefInfo.start;
}
else
{
pos = vert.position;
}
break;
}
}
catch (Exception)
{
return;
}
realStart++;
}
Bounds bounds = new Bounds(pos, Vector3.zero);
int _Cnt = 0;
for (int i = realStart, k = hrefInfo.end; i <= k; i++)
{
if (i >= toFill.currentIndexCount) break;
try
{
toFill.PopulateUIVertex(ref vert, i);
}
catch (System.Exception)
{
break;
}
pos = vert.position;
if (pos.x - 0.1 <= bounds.min.x && _Cnt > 3)
{
hrefInfo.boxs.Add(new Rect(bounds.min, bounds.size));
bounds = new Bounds(pos, Vector3.zero);
_Cnt = 0;
}
bounds.Encapsulate(pos);
_Cnt++;
}
hrefInfo.boxs.Add(new Rect(bounds.min, bounds.size));
}
#endregion
#region 自适应
public override float preferredWidth
{
get
{
if (font == null && Application.isPlaying)
{
font = FontUtility.preferred;
}
var settings = GetGenerationSettings(Vector2.zero);
return cachedTextGeneratorForLayout.GetPreferredWidth(m_OutputText, settings) / pixelsPerUnit;
}
}
public override float preferredHeight
{
get
{
if (font == null && Application.isPlaying)
{
font = FontUtility.preferred;
}
var settings = GetGenerationSettings(new Vector2(rectTransform.rect.size.x, 0.0f));
float _height = cachedTextGeneratorForLayout.GetPreferredHeight(m_OutputText, settings) / pixelsPerUnit;
return _height;
}
}
#endregion
#region 外部数据
private static readonly string FORMAT_PATTERN = @"\%s([0-9]+)";
private ArrayList extenalData = new ArrayList();
public void SetExtenalData(params object[] msg)
{
extenalData.Clear();
if (msg != null && msg.Length > 0)
{
extenalData.AddRange(msg);
}
}
public void SetExtenalData(ICollection collection)
{
extenalData.Clear();
if (collection != null && collection.Count > 0)
{
extenalData.AddRange(collection);
collection = null;
}
}
private string GetExtenalData(string val)
{
textBuilder.Length = 0;
int index = 0;
if (Regex.IsMatch(val, FORMAT_PATTERN))
{
foreach (Match match in Regex.Matches(val, FORMAT_PATTERN))
{
textBuilder.Append(val.Substring(index, match.Index - index));
int infoIndex = 0;
int.TryParse(match.Groups[1].Value, out infoIndex);
if (extenalData != null && infoIndex < extenalData.Count)
{
textBuilder.Append(extenalData[infoIndex]);
}
else
{
textBuilder.Append(0);
}
index = match.Index + match.Length;
}
textBuilder.Append(val.Substring(index, val.Length - index));
return textBuilder.ToString();
}
else
{
return val;
}
}
#region 任务数据
private Dictionary extenalDataDic = null;
private const string Info_Pattern = "{([a-zA-Z0-9_]+)}";
public void SetReplaceInfo(Dictionary _infoDic)
{
extenalDataDic = _infoDic;
SetRichTextDirty();
}
private string GetTaskInfo(string val)
{
textBuilder.Length = 0;
int index = 0;
if (Regex.IsMatch(val, Info_Pattern))
{
foreach (Match match in Regex.Matches(val, Info_Pattern))
{
textBuilder.Append(val.Substring(index, match.Index - index));
if (extenalDataDic != null && extenalDataDic.ContainsKey(match.Groups[1].Value))
{
textBuilder.Append(extenalDataDic[match.Groups[1].Value]);
}
else
{
textBuilder.Append(0);
}
index = match.Index + match.Length;
}
textBuilder.Append(val.Substring(index, val.Length - index));
return textBuilder.ToString();
}
else
{
return val;
}
}
#endregion
#endregion
#region 执行事件
public void OnPointerClick(PointerEventData eventData)
{
if (HrefClick)
{
Vector2 lp;
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform,
eventData.position, eventData.pressEventCamera, out lp);
if (m_HrefList != null)
{
foreach (var hrefInfo in m_HrefList)
{
List boxs = hrefInfo.boxs;
for (int i = 0; i < boxs.Count; i++)
{
if (boxs[i].Contains(lp))
{
hrefInfo.Execute();
return;
}
}
}
}
}
if (OnClick != null) OnClick();
}
public void OnImgClick()
{
}
public void ExcuteHref(int eventIndex = 0, int hrefIndex = 0)
{
if (m_HrefList == null || m_HrefList.Count <= 0
|| hrefIndex < 0) return;
if (hrefIndex < m_HrefList.Count)
{
m_HrefList[hrefIndex].Execute(eventIndex);
}
}
#endregion
#region 自适应2
///
/// 牺牲了主动换行
///
private static StringBuilder textBuilder = new StringBuilder();
private Dictionary matchDics = new Dictionary();
private bool IsModifySize(int _index,out int _size)
{
_size = 0;
int _end = 0;
int _before = -1;
foreach (var _key in matchDics.Keys)
{
if (_index < _key)
{
var _match = matchDics[_key];
if (WordAnalysis.Size_Start_Regex.IsMatch(_match.Value))
{
break;
}
else if (WordAnalysis.Size_End_Regex.IsMatch(_match.Value))
{
_end = _key;
break;
}
}
}
if (_end != 0)
{
foreach (var _key in matchDics.Keys)
{
if (_key < _end)
{
var _match = matchDics[_key];
if (WordAnalysis.Size_Start_Regex.IsMatch(_match.Value))
{
_before = _key;
}
}
else
{
break;
}
}
}
if (_before != -1)
{
_size = int.Parse(matchDics[_before].Groups[1].Value);
return true;
}
return false;
}
public void SetFitterSize()
{
string fitterText = m_OutputText;
fitterText = fitterText.Replace("\n", "");
float width = 0;
textBuilder.Length = 0;
if (null == font && Application.isPlaying)
{
font = FontUtility.preferred;
}
var settings = GetGenerationSettings(Vector2.zero);
float cache = 0;
string part = string.Empty;
float ratio = GetResolutionRatio();
matchDics.Clear();
foreach (Match match in ImgAnalysis.Unity_Img_Regex.Matches(fitterText))
{
if (!matchDics.ContainsKey(match.Index))
{
matchDics.Add(match.Index, match);
}
}
foreach (Match match in WordAnalysis.Color_Start_Regex.Matches(fitterText))
{
if (!matchDics.ContainsKey(match.Index))
{
matchDics.Add(match.Index, match);
}
}
foreach (Match match in WordAnalysis.Color_End_Regex.Matches(fitterText))
{
if (!matchDics.ContainsKey(match.Index))
{
matchDics.Add(match.Index, match);
}
}
bool _modifySize = false;
foreach (Match match in WordAnalysis.Size_Start_Regex.Matches(fitterText))
{
if (!matchDics.ContainsKey(match.Index))
{
_modifySize = true;
matchDics.Add(match.Index, match);
}
}
foreach (Match match in WordAnalysis.Size_End_Regex.Matches(fitterText))
{
if (!matchDics.ContainsKey(match.Index))
{
matchDics.Add(match.Index, match);
}
}
for (int i = 0; i < fitterText.Length; i++)
{
if (matchDics.ContainsKey(i))
{
Match match = matchDics[i];
if (ImgAnalysis.Unity_Img_Regex.IsMatch(match.Value))
{
width += match.Length;
}
if (WordAnalysis.Color_Start_Regex.IsMatch(match.Value))
{
textBuilder.Append(match.Value);
}
else if (WordAnalysis.Color_End_Regex.IsMatch(match.Value))
{
textBuilder.Append(match.Value);
}
else if (WordAnalysis.Size_Start_Regex.IsMatch(match.Value))
{
textBuilder.Append(match.Value);
}
else if (WordAnalysis.Size_End_Regex.IsMatch(match.Value))
{
textBuilder.Append(match.Value);
}
else
{
cache = cachedTextGeneratorForLayout.GetPreferredWidth(match.Value, settings) * ratio;
if (width + cache > (rectTransform.rect.width - 5))
{
CacluHrefAndImgIndex(Mathf.Max(0, i - 1));
textBuilder.Append("\n");
width = cache;
}
else
{
width += cache;
}
textBuilder.Append(match.Value);
}
i += (match.Length - 1);
}
else
{
var _size = 0;
var _cacheFontSize = fontSize;
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);
}
else
{
cache = cachedTextGeneratorForLayout.GetPreferredWidth(fitterText[i].ToString(), settings) * ratio;
}
if (width + cache > (rectTransform.rect.width - 5))
{
CacluHrefAndImgIndex(Mathf.Max(0, i - 1));
textBuilder.Append("\n");
width = cache;
}
else
{
width += cache;
}
textBuilder.Append(fitterText[i]);
}
}
CacluHrefAndImgIndex();
m_OutputText = textBuilder.ToString();
}
private void CacluHrefAndImgIndex(int index)
{
for (int i = 0; i < m_ImgList.Count; i++)
{
if (index * 4 < m_ImgList[i].end)
{
m_ImgList[i].cacheCnt += 1;
}
}
for (int m = 0; m < m_HrefList.Count; m++)
{
RichTextMgr.HrefInfo hrefInfo = m_HrefList[m];
if (index * 4 < hrefInfo.end)
{
hrefInfo.end_cache_cnt += 1;
}
if (index * 4 < hrefInfo.start)
{
hrefInfo.start_cache_cnt += 1;
}
}
}
private void CacluHrefAndImgIndex()
{
for (int i = 0; i < m_ImgList.Count; i++)
{
m_ImgList[i].end += m_ImgList[i].cacheCnt * 4;
}
for (int m = 0; m < m_HrefList.Count; m++)
{
RichTextMgr.HrefInfo hrefInfo = m_HrefList[m];
hrefInfo.start += hrefInfo.start_cache_cnt * 4;
hrefInfo.end += hrefInfo.end_cache_cnt * 4;
}
}
private float GetResolutionRatio()
{
return 1 / pixelsPerUnit;
}
public override string text
{
get
{
return m_Text;
}
set
{
if (string.IsNullOrEmpty(value))
{
value = string.Empty;
}
m_RichText = value;
SetRichTextDirty();
}
}
#if UNITY_EDITOR
protected override void OnValidate()
{
text = m_Text;
}
#endif
private void SetRichTextDirty()
{
m_RichText = UIHelper.ReplaceNewLine(m_RichText);
m_OutputText = GetOutputText(m_RichText);
if (AutoNewLine)
{
SetFitterSize();
}
m_Text = m_OutputText;
SetVerticesDirty();
SetLayoutDirty();
}
#endregion
}