2023-04-03 11:04:31 +08:00

819 lines
28 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<EmojiInfo> emojiInfos = new List<EmojiInfo>();
//计算定点信息的缓存数组
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<int, int, List<float>> luaGetEmojiUVs;
#if !UNITY_2018_4_23
//lua获取顶点计算需要屏蔽的符号
private Func<List<string>> luaGetNotCalVertice;
//符号缓存
private List<string> notCalVertice = new List<string>();
#endif
private Func<Texture2D> luaSetMainTexture;
#region
//点击事件监听
public BFEvent<string, int> OnHrefClick = new BFEvent<string, int>();
// 超链接信息列表
private readonly List<HrefInfo> hrefInfos = new List<HrefInfo>();
#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<EmojiGraphic>();
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<string> GetTranslateStr(out string format)
{
var sb = new StringBuilder();
var list = new List<string>();
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<int, int, List<float>> callback)
{
luaGetEmojiUVs = callback;
}
public void AddLuaSetMainTexture(Func<Texture2D> callback)
{
luaSetMainTexture = callback;
}
#if !UNITY_2018_4_23
public void AddLuaGetNotCalVerticeStr(Func<List<string>> 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("<color=blue>");
int startIndex = textBuilder.Length * 4;
textBuilder.Append("[" + match.Groups[2].Value + "]");
int endIndex = textBuilder.Length * 4 - 1;
textBuilder.Append("</color>");
var hrefInfo = BFMain.Instance.PoolMgr.GetClassPool<HrefInfo>().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<EmojiInfo>().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 = @"<quad size=" + emojiSize + " width=" + 1 + " />";
richTextLength = richTextLength + str.Length - 1;
#else
var tempIndex = textBuilder.Length * 4;
emojiInfo.StartIndex = tempIndex;
emojiInfo.StartStrIndex = textBuilder.Length - startStrIndexOffset + 1;
var str = @"<quad size=" + emojiSize + " width=" + 1 + " />";
#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<TextGenerator>().Get();
underlineText.Populate("_", settings);
IList<UIVertex> 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<TextGenerator>().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<EmojiInfo>().Release(emojiInfos[i]);
}
emojiInfos.Clear();
}
//回收超链接的信息
private void ReleaseHrefInfos()
{
for (var i = 0; i < hrefInfos.Count; i++)
{
BFMain.Instance.PoolMgr.GetClassPool<HrefInfo>().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
/// <summary>
/// 表情的信息
/// </summary>
public class EmojiInfo
{
/// <summary>
/// 顶点索引id
/// </summary>
public int StartIndex;
/// <summary>
/// 开始自定义自负索引
/// </summary>
public int StartStrIndex;
/// <summary>
/// 所占字符数量
/// </summary>
public int Length;
/// <summary>
/// 表情corner
/// </summary>
public Vector3[] Corner;
/// <summary>
/// 表情uv
/// </summary>
public Vector2[] UVs;
#if !UNITY_2018_4_23
/// <summary>
/// 字符串开始真实索引
/// </summary>
public int StartTrueStrIndex;
#endif
public EmojiInfo()
{
Corner = new Vector3[4];
UVs = new Vector2[4];
}
}
/// <summary>
/// 超链接信息类
/// </summary>
public class HrefInfo
{
/// <summary>
/// 超链接id
/// </summary>
public int Id;
/// <summary>
/// 顶点开始索引值
/// </summary>
public int StartIndex;
/// <summary>
/// 顶点结束索引值
/// </summary>
public int EndIndex;
/// <summary>
/// 名称
/// </summary>
public string Name;
/// <summary>
/// 超链接的值
/// </summary>
public string HrefValue;
/// <summary>
/// 碰撞盒范围
/// </summary>
public readonly List<Rect> Boxes = new List<Rect>();
}
#endregion
}