using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using System.Text.RegularExpressions; using System.Text; using UnityEngine.EventSystems; using System; namespace BF { public class InlineText : Text, IPointerClickHandler { #region 属性 // 用正则取 [*#emoji*_*#] *值为0 ,表示为超链接 没有值则为表情 private static readonly Regex inputTagRegex = new Regex(@"\[(\d{0,})#emoji(\d{1,})_(\d{1,})#\]", RegexOptions.Singleline); //记录表情匹配信息 private List emojiInfos = new List(); //计算定点信息的缓存数组 private readonly UIVertex[] m_TempVerts = new UIVertex[4]; //stringbuilder private StringBuilder textBuilder = new StringBuilder(); //tmp uivertext UIVertex _tempVertex = UIVertex.simpleVert; //tmp isvisible private bool isVisible = false; //tmp vector3 private Vector3 tmpVector3 = Vector3.zero; //offset private Vector3 offset = Vector3.zero; //表情大小 private float emojiSize; //自定义表情绘制 private EmojiGraphic emojiGraphic; //luaGetEmojiUVs private Func> luaGetEmojiUVs; #if !UNITY_2018_4_23 //lua获取顶点计算需要屏蔽的符号 private Func> luaGetNotCalVertice; //符号缓存 private List notCalVertice = new List(); #endif private Func luaSetMainTexture; #region 超链接 //点击事件监听 public BFEvent OnHrefClick = new BFEvent(); // 超链接信息列表 private readonly List hrefInfos = new List(); #endregion #endregion #region 重写函数 [TextArea(3, 10)] [SerializeField] protected string _text = string.Empty; public override string text { get { return m_Text; } set { if (String.IsNullOrEmpty(value)) { m_Text = ""; m_Text = GetOutputText(value); SetVerticesDirty(); SetLayoutDirty(); } else if (_text != value) { m_Text = GetOutputText(value); SetVerticesDirty(); SetLayoutDirty(); } #if UNITY_EDITOR //编辑器赋值 如果是一样的 也可以刷新一下 else { m_Text = GetOutputText(value); SetVerticesDirty(); SetLayoutDirty(); } #endif //输入字符备份 _text = value; } } protected override void OnEnable() { base.OnEnable(); supportRichText = true; alignByGeometry = false; //alignByGeometry = false; isVisible = true; if (Application.isPlaying) { BFMain.Instance.OneShotManager.AddOneShot(UpdateDrawSprite); } } protected override void OnDisable() { base.OnDisable(); isVisible = false; if (Application.isPlaying) { BFMain.Instance.OneShotManager.AddOneShot(UpdateDrawSprite); } } protected override void Awake() { base.Start(); if (!Application.isPlaying) { return; } emojiSize = fontSize; var gameObj = new GameObject(); gameObj.transform.SetParent(transform); gameObj.transform.localPosition = Vector3.zero; gameObj.transform.localScale = Vector3.one; emojiGraphic = gameObj.AddComponent(); var rectTran = emojiGraphic.rectTransform; rectTran.anchorMin = Vector2.zero; rectTran.anchorMax = Vector2.one; rectTran.sizeDelta = Vector2.zero; rectTran.pivot = rectTransform.pivot; } protected override void OnRectTransformDimensionsChange() { if (emojiGraphic != null) { emojiGraphic.rectTransform.pivot = rectTransform.pivot; } base.OnRectTransformDimensionsChange(); } protected override void Start() { //base.Start(); if (!Application.isPlaying) { return; } m_Text = GetOutputText(_text); SetVerticesDirty(); SetLayoutDirty(); } protected override void OnPopulateMesh(VertexHelper toFill) { if (font == null) return; base.OnPopulateMesh(toFill); m_DisableFontTextureRebuiltCallback = true; //更新顶点位置&去掉乱码uv DealSpriteTagInfo(toFill); //处理超链接的信息 DealHrefInfo(toFill); m_DisableFontTextureRebuiltCallback = false; isVisible = true; BFMain.Instance.OneShotManager.AddOneShot(UpdateDrawSprite); } // 重写文本所占的长宽 //文本的宽度计算好像有bug,超过sizeDelta就取sizeDelta public override float preferredWidth { get { var settings = GetGenerationSettings(Vector2.zero); float width = cachedTextGeneratorForLayout.GetPreferredWidth(m_Text, settings) / pixelsPerUnit; //Debug.LogWarning("width " + width); //return width; return width < rectTransform.sizeDelta.x || horizontalOverflow == HorizontalWrapMode.Overflow ? width : rectTransform.sizeDelta.x; } } public override float preferredHeight { get { var settings = GetGenerationSettings(new Vector2(rectTransform.rect.size.x, 0.0f)); float height = cachedTextGeneratorForLayout.GetPreferredHeight(m_Text, settings) / pixelsPerUnit; //Debug.LogWarning("height " + height); return height; //return height < rectTransform.sizeDelta.y || verticalOverflow == VerticalWrapMode.Overflow ? height : rectTransform.sizeDelta.y; } } protected override void OnDestroy() { base.OnDestroy(); } #endregion #region 事件回调 //响应点击事件-->检测是否在超链接的范围内 public void OnPointerClick(PointerEventData eventData) { Vector2 lp; RectTransformUtility.ScreenPointToLocalPointInRectangle( rectTransform, eventData.position, eventData.pressEventCamera, out lp); foreach (var hrefInfo in hrefInfos) { var boxes = hrefInfo.Boxes; for (var i = 0; i < boxes.Count; ++i) { if (boxes[i].Contains(lp)) { Debug.LogWarningFormat("点击超链接 id {0}", hrefInfo.Id); OnHrefClick.Invoke(hrefInfo.HrefValue, hrefInfo.Id); return; } } } } #endregion #region 内部函数 public int GetTrueLength() { var countDown = 0; foreach (var emojiInfo in emojiInfos) { countDown += emojiInfo.Length - 1; } return _text.Length - countDown; } public void SetEmojiSize(int size) { emojiSize = size; } public string FilterStr(int length) { if (length >= _text.Length) { return _text; } EmojiInfo tmp = new EmojiInfo(); var countDown = 0; var emojiCount = 0; foreach (var emojiInfo in emojiInfos) { if (emojiInfo.StartStrIndex <= length) { //取出最后一个emojiInfo countDown += emojiInfo.Length - 1; tmp = emojiInfo; emojiCount++; } } var str = ""; if (tmp.Length != 0) { var end = tmp.StartStrIndex + countDown; str = _text.Substring(0, end); if (tmp.StartStrIndex < length && _text.Length - end > 0) { str += _text.Substring(end, Mathf.Min(length - tmp.StartStrIndex,_text.Length - end)); } } else { str = _text.Substring(0, length); } if (_text.Length - countDown > length) { str += "..."; } return str; } public bool IsOnlyEmoji() { var total = emojiInfos.Count; var length = 0; if (total <= 0) { return false; } emojiInfos.Sort((x, y) => { return x.StartStrIndex.CompareTo(y.StartStrIndex); }); for (var i = 0; i < total - 1; i++) { if (emojiInfos[i].StartStrIndex + 1 != emojiInfos[i + 1].StartStrIndex) { return false; } length += emojiInfos[i].Length; } if (length + emojiInfos[total - 1].Length != _text.Length) { return false; } return true; } #if !UNITY_2018_4_23 public List GetTranslateStr(out string format) { var sb = new StringBuilder(); var list = new List(); var lastIndex = 0; var endIndex = 0; if (emojiInfos.Count == 0) { list.Add(_text); format = "#&&&#"; } else { foreach (var emojiInfo in emojiInfos) { if (lastIndex < emojiInfo.StartTrueStrIndex) { list.Add(_text.Substring(lastIndex, emojiInfo.StartTrueStrIndex - lastIndex)); sb.Append("#&&&#"); sb.Append(_text.Substring(emojiInfo.StartTrueStrIndex, emojiInfo.Length)); lastIndex = emojiInfo.StartTrueStrIndex + emojiInfo.Length; } else if (lastIndex == emojiInfo.StartTrueStrIndex) { sb.Append(_text.Substring(emojiInfo.StartTrueStrIndex, emojiInfo.Length)); lastIndex = emojiInfo.StartTrueStrIndex + emojiInfo.Length; } endIndex = emojiInfo.StartTrueStrIndex + emojiInfo.Length; } if (endIndex < _text.Length) { var tmp = _text.Substring(endIndex, _text.Length - endIndex); sb.Append("#&&&#"); list.Add(tmp); } format = sb.ToString(); } return list; } #endif public void AddLuaGetEmojiUVs(Func> callback) { luaGetEmojiUVs = callback; } public void AddLuaSetMainTexture(Func callback) { luaSetMainTexture = callback; } #if !UNITY_2018_4_23 public void AddLuaGetNotCalVerticeStr(Func> callback) { luaGetNotCalVertice = callback; } #endif public void SetMainTexture(Texture2D texture) { if (emojiGraphic) { emojiGraphic.MainTexture = texture; } } //根据正则规则更新文本 private string GetOutputText(string inputText) { //回收表情对象和超链接对象 ReleaseEmojiInfo(); ReleaseHrefInfos(); if (string.IsNullOrEmpty(inputText)) return ""; textBuilder.Clear(); var textIndex = 0; var emojiTpye = 0; var emojiIndex = 0; #if !UNITY_2018_4_23 var richTextLength = 0; var notCalVerticeLength = 0; if (notCalVertice.Count <= 0) { notCalVertice = luaGetNotCalVertice(); } #endif var startStrIndexOffset = 0; foreach (Match match in inputTagRegex.Matches(inputText)) { int tempId = -1; if (!string.IsNullOrEmpty(match.Groups[1].Value) && !match.Groups[1].Value.Equals("-")) tempId = int.Parse(match.Groups[1].Value); var fronStr = inputText.Substring(textIndex, match.Index - textIndex); #if !UNITY_2018_4_23 //先加入之前的匹配位置字符串 foreach (var str in notCalVertice) { var m = Regex.Matches(fronStr, str); foreach (Match _match in m) { notCalVerticeLength += _match.Groups[0].Value.Length; } } #endif textBuilder.Append(fronStr); //更新超链接 if (tempId == 0) { textBuilder.Append(""); int startIndex = textBuilder.Length * 4; textBuilder.Append("[" + match.Groups[2].Value + "]"); int endIndex = textBuilder.Length * 4 - 1; textBuilder.Append(""); var hrefInfo = BFMain.Instance.PoolMgr.GetClassPool().Get(); hrefInfo.Id = Mathf.Abs(tempId); hrefInfo.StartIndex = startIndex;// 超链接里的文本起始顶点索引 hrefInfo.EndIndex = endIndex; hrefInfo.Name = match.Groups[2].Value; hrefInfo.HrefValue = match.Groups[3].Value; hrefInfos.Add(hrefInfo); } //更新表情 else { //先判断是否有这个图 emojiTpye = int.Parse(match.Groups[2].Value); emojiIndex = int.Parse(match.Groups[3].Value); var info = luaGetEmojiUVs?.Invoke(emojiTpye, emojiIndex); if (info != null) { EmojiInfo emojiInfo = BFMain.Instance.PoolMgr.GetClassPool().Get(); #if !UNITY_2018_4_23 emojiInfo.StartTrueStrIndex = match.Index; #endif //获取uv for (var i = 0; i < 4; i++) { var index = i * 2; emojiInfo.UVs[i] = new Vector2(info[index], info[index + 1]); } //设置乱码开始index #if !UNITY_2018_4_23 var tempIndex = (textBuilder.Length - richTextLength - notCalVerticeLength) * 4; emojiInfo.StartIndex = tempIndex; emojiInfo.StartStrIndex = textBuilder.Length - startStrIndexOffset + 1; var str = @""; richTextLength = richTextLength + str.Length - 1; #else var tempIndex = textBuilder.Length * 4; emojiInfo.StartIndex = tempIndex; emojiInfo.StartStrIndex = textBuilder.Length - startStrIndexOffset + 1; var str = @""; #endif textBuilder.Append(str); startStrIndexOffset += str.Length - 1; emojiInfo.Length = match.Groups[0].Value.Length; emojiInfos.Add(emojiInfo); } else { Debug.LogError("没有图片"); textBuilder.Append(match.Groups[0].Value); textIndex = match.Index + match.Length; continue; } } textIndex = match.Index + match.Length; } textBuilder.Append(inputText.Substring(textIndex, inputText.Length - textIndex)); return textBuilder.ToString(); } //处理表情信息 设置顶点信息 private void DealSpriteTagInfo(VertexHelper toFill) { int index = -1; //emoji for (int i = 0; i < emojiInfos.Count; i++) { index = emojiInfos[i].StartIndex; #if !UNITY_2018_4_23 if ((index + 4) <= toFill.currentVertCount) #else if ((index + 4) < toFill.currentVertCount) #endif { for (int j = index; j < index + 4; j++) { toFill.PopulateUIVertex(ref _tempVertex, j); //清理多余的乱码uv _tempVertex.uv0 = Vector2.zero; toFill.SetUIVertex(_tempVertex, j); //获取quad的位置 --> 转为世界坐标 //emojiInfos[i].Corner[j - index] = Utils.TransformPoint2World(transform, _tempVertex.position); emojiInfos[i].Corner[j - index] = _tempVertex.position; } } } } //处理超链接的信息 private void DealHrefInfo(VertexHelper toFill) { if (hrefInfos.Count > 0) { // 处理超链接包围框 for (int i = 0; i < hrefInfos.Count; i++) { hrefInfos[i].Boxes.Clear(); if (hrefInfos[i].StartIndex >= toFill.currentVertCount) continue; toFill.PopulateUIVertex(ref _tempVertex, hrefInfos[i].StartIndex); // 将超链接里面的文本顶点索引坐标加入到包围框 var pos = _tempVertex.position; var bounds = new Bounds(pos, Vector3.zero); for (int j = hrefInfos[i].StartIndex + 1; j < hrefInfos[i].EndIndex; j++) { if (j >= toFill.currentVertCount) { break; } toFill.PopulateUIVertex(ref _tempVertex, j); pos = _tempVertex.position; if (pos.x < bounds.min.x) { // 换行重新添加包围框 hrefInfos[i].Boxes.Add(new Rect(bounds.min, bounds.size)); bounds = new Bounds(pos, Vector3.zero); } else { bounds.Encapsulate(pos); // 扩展包围框 } } //添加包围盒 hrefInfos[i].Boxes.Add(new Rect(bounds.min, bounds.size)); } //添加下划线 Vector2 extents = rectTransform.rect.size; var settings = GetGenerationSettings(extents); TextGenerator underlineText = BFMain.Instance.PoolMgr.GetClassPool().Get(); underlineText.Populate("_", settings); IList tut = underlineText.verts; for (int m = 0; m < hrefInfos.Count; m++) { for (int i = 0; i < hrefInfos[m].Boxes.Count; i++) { //计算下划线的位置 Vector3[] ulPos = new Vector3[4]; ulPos[0] = hrefInfos[m].Boxes[i].position + new Vector2(0.0f, fontSize * 0.2f); ulPos[1] = ulPos[0] + new Vector3(hrefInfos[m].Boxes[i].width, 0.0f); ulPos[2] = hrefInfos[m].Boxes[i].position + new Vector2(hrefInfos[m].Boxes[i].width, 0.0f); ulPos[3] = hrefInfos[m].Boxes[i].position; //绘制下划线 for (int j = 0; j < 4; j++) { m_TempVerts[j] = tut[j]; m_TempVerts[j].color = Color.blue; m_TempVerts[j].position = ulPos[j]; if (j == 3) toFill.AddUIVertexQuad(m_TempVerts); } } } //回收下划线的对象 BFMain.Instance.PoolMgr.GetClassPool().Release(underlineText); } } //表情绘制 private void UpdateDrawSprite() { if (this == null || emojiGraphic == null) { return; } if (emojiGraphic.MainTexture == null) { emojiGraphic.MainTexture = luaSetMainTexture?.Invoke(); } if (emojiGraphic.MainTexture != null) { emojiGraphic.SetMeshInfos(emojiInfos); } } //回收EmojiInfo private void ReleaseEmojiInfo() { //记录之前的信息 for (var i = 0; i < emojiInfos.Count; i++) { //回收信息到对象池 BFMain.Instance.PoolMgr.GetClassPool().Release(emojiInfos[i]); } emojiInfos.Clear(); } //回收超链接的信息 private void ReleaseHrefInfos() { for (var i = 0; i < hrefInfos.Count; i++) { BFMain.Instance.PoolMgr.GetClassPool().Release(hrefInfos[i]); } hrefInfos.Clear(); } #endregion #region UNITY_EDITOR #if UNITY_EDITOR // protected override void OnValidate() // { // base.OnValidate(); // m_Text = GetOutputText(_text); // SetVerticesDirty(); // SetLayoutDirty(); // } //辅助线框 Vector3[] _textWolrdVertexs = new Vector3[4]; private void OnDrawGizmos() { //text rectTransform.GetWorldCorners(_textWolrdVertexs); GizmosDrawLine(Color.white, _textWolrdVertexs); //preferred size Vector2 pivot = GetTextAnchorPivot(alignment); Rect rect = new Rect(); Vector2 size = rectTransform.sizeDelta - new Vector2(preferredWidth, preferredHeight); rect.position = new Vector2(pivot.x * size.x, pivot.y * size.y) - new Vector2(rectTransform.sizeDelta.x * rectTransform.pivot.x, rectTransform.sizeDelta.y * rectTransform.pivot.y); rect.width = preferredWidth; rect.height = preferredHeight; _textWolrdVertexs[0] = Utils.TransformPoint2World(transform, new Vector3(rect.x, rect.y)); _textWolrdVertexs[1] = Utils.TransformPoint2World(transform, new Vector3(rect.x + rect.width, rect.y)); _textWolrdVertexs[2] = Utils.TransformPoint2World(transform, new Vector3(rect.x + rect.width, rect.y + rect.height)); _textWolrdVertexs[3] = Utils.TransformPoint2World(transform, new Vector3(rect.x, rect.y + rect.height)); GizmosDrawLine(Color.blue, _textWolrdVertexs); //href for (int i = 0; i < hrefInfos.Count; i++) { for (int j = 0; j < hrefInfos[i].Boxes.Count; j++) { rect = hrefInfos[i].Boxes[j]; _textWolrdVertexs[0] = Utils.TransformPoint2World(transform, rect.position); _textWolrdVertexs[1] = Utils.TransformPoint2World(transform, new Vector3(rect.x + rect.width, rect.y)); _textWolrdVertexs[2] = Utils.TransformPoint2World(transform, new Vector3(rect.x + rect.width, rect.y + rect.height)); _textWolrdVertexs[3] = Utils.TransformPoint2World(transform, new Vector3(rect.x, rect.y + rect.height)); GizmosDrawLine(Color.green, _textWolrdVertexs); } } //sprite for (int i = 0; i < emojiInfos.Count; i++) { GizmosDrawLine(Color.yellow, emojiInfos[i].Corner); } } //划线 private void GizmosDrawLine(Color color, Vector3[] pos) { Gizmos.color = color; Gizmos.DrawLine(Utils.TransformPoint2World(transform, pos[0]), Utils.TransformPoint2World(transform, pos[1])); Gizmos.DrawLine(Utils.TransformPoint2World(transform, pos[1]), Utils.TransformPoint2World(transform, pos[2])); Gizmos.DrawLine(Utils.TransformPoint2World(transform, pos[2]), Utils.TransformPoint2World(transform, pos[3])); Gizmos.DrawLine(Utils.TransformPoint2World(transform, pos[3]), Utils.TransformPoint2World(transform, pos[0])); } #endif #endregion } #region Struct /// /// 表情的信息 /// public class EmojiInfo { /// /// 顶点索引id /// public int StartIndex; /// /// 开始自定义自负索引 /// public int StartStrIndex; /// /// 所占字符数量 /// public int Length; /// /// 表情corner /// public Vector3[] Corner; /// /// 表情uv /// public Vector2[] UVs; #if !UNITY_2018_4_23 /// /// 字符串开始真实索引 /// public int StartTrueStrIndex; #endif public EmojiInfo() { Corner = new Vector3[4]; UVs = new Vector2[4]; } } /// /// 超链接信息类 /// public class HrefInfo { /// /// 超链接id /// public int Id; /// /// 顶点开始索引值 /// public int StartIndex; /// /// 顶点结束索引值 /// public int EndIndex; /// /// 名称 /// public string Name; /// /// 超链接的值 /// public string HrefValue; /// /// 碰撞盒范围 /// public readonly List Boxes = new List(); } #endregion }