有些游戏聊天时会要发送个坐标或者道具信息之类的,一般都是用超链接做的,超链接也属于富文本,当我们按退格键时,如果删除到了超链接的内容,一般是希望能把超链接的部分作为整体一起删除的,否则看起来会有点奇怪,可以用正则表达式来匹配文本中出现的富文本,然后判断当前光标处于哪个富文本内,退格的时候直接将那一块富文本的内容全部删掉,代码如下
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using TMPro;
public class InputTest : MonoBehaviour
{
private TMP_InputField m_ti_chat;
private int selectionStartStringPosition = -1;
private int selectionEndStringPosition = -1;
private string oldValue;
private string curValue;
public Vector3 pos;
public void Start()
{
//当选中输入框时不要高亮其中所有的内容
m_ti_chat.onFocusSelectAll = false;
m_ti_chat.onValueChanged.AddListener(OnValueChanged);
m_ti_chat.onTextSelection.AddListener(OnTextSelection);
m_ti_chat.onEndTextSelection.AddListener(OnEndTextSelection);
}
private void OnValueChanged(string arg0)
{
oldValue = oldValue == null ? arg0 : curValue;
curValue = arg0;
}
private void OnEndTextSelection(string arg0, int arg1, int arg2)
{
selectionStartStringPosition = -1;
selectionEndStringPosition = -1;
}
private void OnTextSelection(string arg0, int arg1, int arg2)
{
selectionStartStringPosition = arg1;
selectionEndStringPosition = arg2;
}
public void Update()
{
// 检测是否按下了退格键
if (Input.GetKeyDown(KeyCode.Backspace))
{
if (m_ti_chat.isFocused)
{
DeleteNearestColorTag(m_ti_chat);
}
}
}
/// <summary>
/// 在当前输入框中插入坐标信息,默认是插在最右边
/// </summary>
private void AddCurPosInfo()
{
StringBuilder stb = new();
stb.Append(m_ti_chat.text);
string linkId = $"坐标_{pos.x},{pos.y},{pos.z}";
string linkText = $"[X{pos.x},Y{pos.y},Z{pos.z}]";
string replaceText = $"<#FF0000><link=\"{linkId}\">{linkText}</link></color> ";
stb.Append(replaceText);
m_ti_chat.text = stb.ToString();
//选中输入框
m_ti_chat.Select();
//光标移到最右边
m_ti_chat.MoveTextEnd(false);
}
/// <summary>
/// 删除富文本中的内容时使其一整块整体删除
/// </summary>
/// <param name="inputField"></param>
void DeleteNearestColorTag(TMP_InputField inputField)
{
int stringPosition = inputField.stringPosition;
string text = curValue;
if (selectionEndStringPosition != -1)
{
stringPosition = selectionEndStringPosition + 1;
text = oldValue;
}
else
{
stringPosition += 1;
}
// 正则表达式匹配颜色标签及其内部的文本
string pattern = @"<#[A-Fa-f0-9]{6}>(.*?)<\/color>";
MatchCollection matches = Regex.Matches(text, pattern);
// 从光标位置开始向前查找最近的匹配项
int closestIndex = -1;
int minDistance = int.MaxValue;
int startIndex = 0;
for (var index = 0; index < matches.Count; index++)
{
var matchTemp = matches[index];
int distance = stringPosition - matchTemp.Index;
if (distance >= 0 && distance < minDistance)
{
minDistance = distance;
closestIndex = matchTemp.Index;
startIndex = index;
}
}
RemoveMatchText(inputField, closestIndex, matches, startIndex, stringPosition, text);
}
/// <summary>
/// 删除匹配的文本
/// </summary>
/// <param name="inputField"></param>
/// <param name="closestIndex"></param>
/// <param name="matches"></param>
/// <param name="startIndex"></param>
/// <param name="stringPosition"></param>
/// <param name="text"></param>
private void RemoveMatchText(TMP_InputField inputField, int closestIndex, MatchCollection matches, int startIndex,
int stringPosition, string text)
{
// 如果找到了匹配项,删除它
if (closestIndex == -1)
{
return;
}
var match = matches[startIndex];
// 判断光标是否在标签内
if (stringPosition < match.Index || stringPosition > match.Index + match.Length + 1)
{
return;
}
int length = match.Length;
int selectionLength = selectionStartStringPosition - selectionEndStringPosition;
if (selectionStartStringPosition == -1)
{
inputField.text = text.Remove(closestIndex, length);
inputField.caretPosition = closestIndex; // 移动光标到删除标签后的位置
}
else
{
if (selectionLength > 0)
{
length = selectionStartStringPosition - closestIndex;
inputField.text = text.Remove(closestIndex, length);
inputField.caretPosition = closestIndex; // 移动光标到删除标签后的位置
}
else
{
inputField.text = text.Remove(selectionStartStringPosition, match.Index + match.Length + 1 - selectionStartStringPosition);
inputField.caretPosition = selectionStartStringPosition; // 移动光标到删除标签后的位置
}
}
}
}