Unity TMP Inputfield 输入框 框选 富文本 获取真实定位

一、带富文本标签的框选是什么

UGUI的InputField提供了selectionAnchorPosition和selectionFocusPosition,开始选择时的光标下标和当前光标下标

对于未添加富文本标签时,直接通过以上两个值,判断一下框选方向(前向后/后向前),进而对inputfield的text内容进行字符串拆分即可

相关基础内容可以看一些参考博客

https://blog.51cto.com/u_15296378/7884559

但是对于有富文本标签的inputfield,这两个值,返回的只是表面看起来的索引,并没有包含富文本标签。

举个栗子:

对于一段普通文本(未添加富文本):今天没有下雨

你想选择“没有下”,那么索引应该是,2和4

但是对于一段富文本内容:今天<b>没有</b>下雨

你想选择“没有下”,那么真实索引应该是,5和12,

那么根据实际索引,拆分后的字符串是这样“没有</b>下

然而你再使用selectionAnchorPosition和selectionFocusPosition尝试获取时,依然得到的是2和4,

那么根据原索引,拆分后的字符串是这样“<b>”,就完全被富文本标签干扰了

截止目前还没有或者我没找到官方直接可以使用的,支持带有富文本标签的,框选或选中内容定位获取接口。因此只能自己计算

二、获取真实富文本标签定位的大致思路

为了获取真实定位,这里我们先不考虑框选,先只考虑一个字符的位置。

还是以今天<b>没有</b>下雨为例

我想获取“没”的真实定位,只需要在原索引加上其前面富文本标签“<b>”的长度即可

那么2就变成了5

进而扩展一下,今天<b><i><color=yellow>没有</color></i></b>下雨 ,对于这个文本

想获取“没”的真实定位,就需要将“没”前面所有富文本标签的长度都加上

那么写一个方法,去除目标索引前的第一个富文本标签,并记录去除后的字符串和被去除标签的字符数量,然后递归调用自己,直到没有富文本标签之后结束

计算框选时第二个字的定位时,也是同理(这里后面框选时,计算的调节稍有不同,下文讲)

如果想要返回的内容中,不包含富文本标签,类似上图中的打印结果,只要“车10辆”三个字,那么使用一个正则字符串替换即可

//移除选定部分,所有富文本标签
    public string RemoveRichTextTags(string text) {
        return System.Text.RegularExpressions.Regex.Replace(text, "<.*?>", string.Empty);
    }

三、代码实现

在unity创建RichTextTagHandler类

这里先假定,只有一个InputField被编辑,无切换

先创建一个全局变量,用来保存某次计算的真实索引

private int total = 0;

创建RemoveFrontRichTag方法,用于移除,指定字符串,目标索引前,第一个富文本标签

/// <summary>
/// 从前往后,移除第一个富文本标签,计数标签字符数量,并将移除后的字符串返回
/// </summary>
/// <param name="aimString">待移除富文本标签的目标字符串</param>
/// <param name="surfaceIndex">原定位</param>
/// <param name="count">用来计数用的, 标记已经移除的富文本标签字符长度</param>
/// <returns>阶段性返回当前移除的富文本标签字符数量,递归后最终返回所有符合要求的富文本标签字符总数量</returns>
private string RemoveFrontRichTag(string aimString,int surfaceIndex, ref int count) {
    //先尝试定位'<'
    int meet = aimString.IndexOf('<');

    //如果定位不到,或者定位超过了原支付长度,说明标签在我们所选字符后面或者无富文本标签了,方法直接返回,并将数量count设置为0
    if (meet == -1 || meet > surfaceIndex) {
        count = 0;
        return aimString;
    }

    //成功定位到‘<’后,继续定位‘>’
    int leave = aimString.IndexOf('>');

    //将字符串拆分成去掉<>及其内部内容
    string newString = aimString.Substring(0, meet) + aimString.Substring(leave + 1, aimString.Length - leave - 1);

    //计算去掉部分的字符数量
    int length = leave - meet + 1;
    count += length;

    //Debug.Log(newString + " meet=" + meet + " leave=" + leave + " length=" + length);

    //把去掉已计数的标签后的字符串,返回
    return newString;
}

创建TryGetRealIndex方法,用于递归调用,移除目标索引前的所有富文本标签

/// <summary>
/// 尝试获取真实索引,递归方法,
/// </summary>
/// <param name="richText"></param>
/// <param name="surfaceIndex"></param>
/// <param name="isEndPoint"></param>
/// <returns></returns>
private int TryGetRealIndex(string richText, int surfaceIndex,bool isEndPoint) {

    string newString = richText;
    int count = 0;

    //先尝试定位‘<’,并获取它的定位
    int mark = newString.IndexOf('<');

    //-1是没找到,没找到或大于,原本的定位,那么代表,原本定位之前已经没有'<'了,直接返回结束

    if ((isEndPoint && (mark == -1 || mark >= surfaceIndex)) || //如果是结尾的点,那么判断mark时添加等号,防止后侧遗留标签整体
        (!isEndPoint && (mark == -1 || mark > surfaceIndex))) {//如果不是结尾的点,那么判断mark时不加等号,判断前侧遗留标签整体
        return total;
    }
    //如果在原定位前,找到了'<',那么走一遍清除最前的富文本标签并计数的方法
    else {
        //清除一次标签,并将清除后的字符串保存
        newString = RemoveFrontRichTag(newString, surfaceIndex, ref count);
        //加入计数
        total += count;
        //回调,继续判断,原本定位前,是否有富文本标记
        TryGetRealIndex(newString, surfaceIndex, isEndPoint);
    }

    //递归完成后,返回最终的富文本标签字符总数
    return total;
}

最后创建GetRealIndex,用于获取最终索引,并返回

/// <summary>
/// 获取选定部分,排除富文本标签后(富文本标签也算作数量),字符的真实位置
/// </summary>
/// <param name="inputField"></param>
/// <param name="surfaceIndex"></param>
/// <param name="isEndPoint">
/// 默认为false,从选定位置往前,所有富文本标签<>全部排除(通常为选定部分的“前面”点)
/// 传入true后,从选定位置开始,不仅往前,而且往后,所有富文本标签<>全部排除(通常为选定部分“后面”点)
/// 注意:选定时,从前往后选和从后往前选,“前面”和“后面”的点要判断一下
/// 扩展:如果“前面”点传入true,而“后面”点传入false,那么可以获取选定区域开始,前后所有的富文本标签都会被选定(默认是清除前后所有富文本标签)
/// </param>
/// <returns></returns>
public int GetRealIndex(TMP_InputField inputField, int surfaceIndex,bool isEndPoint=false) {
    //
    string richText = inputField.text;

    //所有,surfaceIndex前,富文本标签所占的字符总数
    int allRichTagCharCount = 0;
    total = 0;//每次开始前,重置total为0

    //递归获取富文本标签所占的字符总数
    allRichTagCharCount = TryGetRealIndex(richText, surfaceIndex,isEndPoint);

    //最终实际的定位是,富文本字符总数与surfaceIndex表定位的合
    return allRichTagCharCount + surfaceIndex;
}

完整的脚本长这样

using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class RichTextTagHandler {

    private int total = 0;

    /// <summary>
    /// 获取选定部分,排除富文本标签后(富文本标签也算作数量),字符的真实位置
    /// </summary>
    /// <param name="inputField"></param>
    /// <param name="surfaceIndex"></param>
    /// <param name="isEndPoint">
    /// 默认为false,从选定位置往前,所有富文本标签<>全部排除(通常为选定部分的“前面”点)
    /// 传入true后,从选定位置开始,不仅往前,而且往后,所有富文本标签<>全部排除(通常为选定部分“后面”点)
    /// 注意:选定时,从前往后选和从后往前选,“前面”和“后面”的点要判断一下
    /// 扩展:如果“前面”点传入true,而“后面”点传入false,那么可以获取选定区域开始,前后所有的富文本标签都会被选定(默认是清除前后所有富文本标签)
    /// </param>
    /// <returns></returns>
    public int GetRealIndex(TMP_InputField inputField, int surfaceIndex,bool isEndPoint=false) {
        //
        string richText = inputField.text;

        //所有,surfaceIndex前,富文本标签所占的字符总数
        int allRichTagCharCount = 0;
        total = 0;//每次开始前,重置total为0

        //递归获取富文本标签所占的字符总数
        allRichTagCharCount = TryGetRealIndex(richText, surfaceIndex,isEndPoint);

        //最终实际的定位是,富文本字符总数与surfaceIndex表定位的合
        return allRichTagCharCount + surfaceIndex;
    }

    /// <summary>
    /// 尝试获取真实索引,递归方法,
    /// </summary>
    /// <param name="richText"></param>
    /// <param name="surfaceIndex"></param>
    /// <param name="isEndPoint"></param>
    /// <returns></returns>
    private int TryGetRealIndex(string richText, int surfaceIndex,bool isEndPoint) {

        string newString = richText;
        int count = 0;

        //先尝试定位‘<’,并获取它的定位
        int mark = newString.IndexOf('<');

        //-1是没找到,没找到或大于,原本的定位,那么代表,原本定位之前已经没有'<'了,直接返回结束

        if ((isEndPoint && (mark == -1 || mark >= surfaceIndex)) || //如果是结尾的点,那么判断mark时添加等号,防止后侧遗留标签整体
            (!isEndPoint && (mark == -1 || mark > surfaceIndex))) {//如果不是结尾的点,那么判断mark时不加等号,判断前侧遗留标签整体
            return total;
        }
        //如果在原定位前,找到了'<',那么走一遍清除最前的富文本标签并计数的方法
        else {
            //清除一次标签,并将清除后的字符串保存
            newString = RemoveFrontRichTag(newString, surfaceIndex, ref count);
            //加入计数
            total += count;
            //回调,继续判断,原本定位前,是否有富文本标记
            TryGetRealIndex(newString, surfaceIndex, isEndPoint);
        }

        //递归完成后,返回最终的富文本标签字符总数
        return total;
    }

    /// <summary>
    /// 从前往后,移除第一个富文本标签,计数标签字符数量,并将移除后的字符串返回
    /// </summary>
    /// <param name="aimString">待移除富文本标签的目标字符串</param>
    /// <param name="surfaceIndex">原定位</param>
    /// <param name="count">用来计数用的, 标记已经移除的富文本标签字符长度</param>
    /// <returns>阶段性返回当前移除的富文本标签字符数量,递归后最终返回所有符合要求的富文本标签字符总数量</returns>
    private string RemoveFrontRichTag(string aimString,int surfaceIndex, ref int count) {
        //先尝试定位'<'
        int meet = aimString.IndexOf('<');

        //如果定位不到,或者定位超过了原支付长度,说明标签在我们所选字符后面或者无富文本标签了,方法直接返回,并将数量count设置为0
        if (meet == -1 || meet > surfaceIndex) {
            count = 0;
            return aimString;
        }

        //成功定位到‘<’后,继续定位‘>’
        int leave = aimString.IndexOf('>');

        //将字符串拆分成去掉<>及其内部内容
        string newString = aimString.Substring(0, meet) + aimString.Substring(leave + 1, aimString.Length - leave - 1);

        //计算去掉部分的字符数量
        int length = leave - meet + 1;
        count += length;

        //Debug.Log(newString + " meet=" + meet + " leave=" + leave + " length=" + length);

        //把去掉已计数的标签后的字符串,返回
        return newString;
    }

    #region Utility

    //移除选定部分,所有富文本标签
    public string RemoveRichTextTags(string text) {
        return System.Text.RegularExpressions.Regex.Replace(text, "<.*?>", string.Empty);
    }

    //移除选定部分,所有颜色标签
    public string RemoveRichTextTags_color(string text) {
        return System.Text.RegularExpressions.Regex.Replace(text, "<color=.*?>|</color>", string.Empty);
    }

    //移除选定部分,所有加粗标签
    public string RemoveRichTextTags_B(string text) {
        return System.Text.RegularExpressions.Regex.Replace(text, "<b>|</b>", string.Empty);
    }

    //移除选定部分,所有Size标签
    public string RemoveRichTextTags_Size(string text) {
        return System.Text.RegularExpressions.Regex.Replace(text, "<size=.*?>|</size>", string.Empty);
    }

    #endregion
}

使用时,类似这样

private void Update() {
    if (Input.GetMouseButtonUp(0)) {
        OnMouseUpFromIpf();
    }

}

#region 主要代码

private void OnMouseUpFromIpf() {
    if (EventSystem.current.currentSelectedGameObject == ipf.gameObject) {

        int startPos = ipf.selectionAnchorPosition;//起始选择的位置
        int endPos = ipf.selectionFocusPosition;//结束时的位置

        if (startPos == endPos) return;//如果起始结束相等,那么相当于就点了一下,没选择内容

        string selectedStr = "";

        if (startPos < endPos) {//正常从前往后选的情况

            // 获取可见文本的起始位置,从选定Index开始,排除前面所有富文本标签
            currentInsideBeginIndex = handler.GetRealIndex(ipf, startPos);
            //获取可见文本的结束位置,从选定Index开始,后面的所有富文本标签,也要排除
            currentInsideEndIndex = handler.GetRealIndex(ipf, endPos, true);//此处传入true,即可以排除index后面的所有富文本标签
        }
        else if (startPos > endPos) {//从后往前选的情况

            currentInsideBeginIndex = handler.GetRealIndex(ipf, endPos);//反选,endPos是“前面的点”
            currentInsideEndIndex = handler.GetRealIndex(ipf, startPos, true);//反选,startPos是“后面的点”
        }

        //获取选定部分的字符串,排除选定区域开始,前后所有的富文本标签
        //注意:如果需要,以选定区域为基准,获取区域前后部分的所有富文本标签(而不是排除),那么需要将BeginIndex的GetRealIndex中IsEndPoint传入true,而EndIndex的GetRealIndex中IsEndPoint传入false
        //类似一个加粗区域,<b>A</b>,目前选定A,那么获取到的是“A”,若是以下面的写法反向设置IsEndPoint,那么获取到的是“<b>A</b>”。后面这么获取,可以方便对标签进行判断和修改
        selectedStr = ipf.text.Substring(currentInsideBeginIndex, currentInsideEndIndex - currentInsideBeginIndex);

        Debug.Log(currentInsideBeginIndex + " " + currentInsideEndIndex + " " + selectedStr+" "+startPos+" "+endPos);
        ipf_console.text = selectedStr;
    }
}

演示

其他扩展功能,只需要对字符串进行编辑即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/546226.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【热门话题】PyTorch:深度学习领域的强大工具

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 PyTorch&#xff1a;深度学习领域的强大工具一、PyTorch概述二、PyTorch核心特性…

C语言洛谷题目分享(9)奇怪的电梯

目录 1.前言 2.题目&#xff1a;奇怪的电梯 1.题目描述 2.输入格式 3.输出格式 4.输入输出样例 5.说明 6.题解 3.小结 1.前言 哈喽大家好啊&#xff0c;前一段时间小编去备战蓝桥杯所以博客的更新就暂停了几天&#xff0c;今天继续为大家带来题解分享&#xff0c;希望大…

性能再升级!UNet+注意力机制,新SOTA分割准确率高达99%

UNet结合注意力机制能够有效提升图像分割任务的性能。 具体来说&#xff0c;通过将注意力模块集成到UNet的架构中&#xff0c;动态地重新分配网络的焦点&#xff0c;让其更集中在图像中对于分割任务关键的部分。这样UNet可以更有效地利用其跳跃连接特性&#xff0c;以精细的局…

2024-4-15-ARM作业

实现字符串数据收发函数的封装 源代码&#xff1a; main.c #include "gpio.h"#include "uart4.h"int main(){uart4_config();while (1){// char agetchar();// putchar(a1);char s[20];gets(s);puts(s);//putchar(\n);putchar(\r);}return 0;}uart4.c …

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之十二 简单把视频的水印去掉效果

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之十二 简单把视频的水印去掉效果 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之十二 简单把视频的水印去掉效果 一、简单介绍 二、简单把视频的水印去掉效果实现原理 …

MVVM架构模式

目录 MVVM 数据绑定方式 实现方式 Model View ViewModel 数据绑定方式 vue&#xff1a;&#xff1a; 数据劫持和发布-订阅模式&#xff1a; Object.defineProperty() 方法来劫持&#xff08;监控&#xff09;各属性的 getter 、setter &#xff0c;并在数据&#xff08;对…

centos7上安装python3.10

1 安装依赖 使用yum程序安装所需依赖。打开终端并运行以下命令&#xff1a; yum install wget zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make zlib zlib-devel libffi-devel -y 这些依赖项是编译和安装Python 3.10所必…

结合文本的目标检测:Open-GroundingDino训练自己的数据集

1、简单介绍 Open-GroundingDino是GroundingDino的第三方实现训练流程的代码&#xff0c;因为官方GroundingDino没有提供训练代码&#xff0c;只提供了demo推理代码。 关于GroundingDino的介绍可以看论文&#xff1a;https://arxiv.org/pdf/2303.05499.pdf GroundingDino的G…

没有算法大佬,都是草台班子

没有算法大佬&#xff0c;都是草台班子。 最近除了工作之外&#xff0c;还有一些时间在和加我微信的小伙伴沟通&#xff0c;聊的内容大部分集中在如何快速有效的学习人工智能、入门人工智能的技巧。 其中&#xff0c;一个知乎过来加我微信的小伙伴的经历更是让我感触很深。 …

openGauss学习笔记-261 openGauss性能调优-使用Plan Hint进行调优-将部分Error降级为Warning的Hint

文章目录 openGauss学习笔记-261 openGauss性能调优-使用Plan Hint进行调优-将部分Error降级为Warning的Hint261.1 功能描述261.2 语法格式261.3 示例261.3.1 忽略非空约束261.3.2 忽略唯一约束261.3.3 忽略分区表无法匹配到合法分区261.3.4 更新/插入值向目标列类型转换失败 o…

AI 编程助手汇总

文章目录 AI编程助手~~GitHub学生认证申请&#xff08;无效&#xff0c;申请不了了&#xff09;~~GitHub双重身份验证 AI编程助手 Baidu Comate &#xff08;强推✔&#xff09; 阿里通义灵码 清华CodeGeeX Amazon CodeWhisperer &#xff08;需要注册账号&#xff0c;绑定信…

秋招算法刷题7

20240410 1.接雨水 方法一&#xff0c;动态规划&#xff0c;时间复杂度O&#xff08;n^2&#xff09;&#xff0c;空间复杂度O&#xff08;n&#xff09; public int trap(int[] height) { int nheight.length; if(n0){ return 0; } …

python 海龟画图tutle螺旋线

目录 初识turtle模块 基本绘图概念 示例&#xff1a;绘制一个正方形 示例&#xff1a;绘制彩色螺旋线 附录 常用命令 其它命令 在Python编程中&#xff0c;使用turtle模块进行图形绘制是一种非常有趣和富有教育意义的活动。通过控制一个小海龟&#xff08;Turtle&#x…

RabbitMQ消息模型之Direct消息模型

Direct消息模型 * 路由模型&#xff1a; * 一个交换机可以绑定多个队列 * 生产者给交换机发送消息时&#xff0c;需要指定消息的路由键 * 消费者绑定队列到交换机时&#xff0c;需要指定所需要消费的信息的路由键 * 交换机会根据消息的路由键将消息转发到对应的队…

ModuleNotFoundError: No module named ‘llama_index.readers“解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

“We Need Structured Output”: 以用户为中心的大模型输出

发表机构&#xff1a;Google Research 这篇论文的核心是设计了一种系统&#xff0c;可以让开发者和用户对大型语言模型的输出施加结构性约束。系统的主要部分包括&#xff1a; 1. 用户界面&#xff08;GUI&#xff09;&#xff1a;允许用户通过图形界面来定义他们希望LLM遵守…

Redis中的BigKey

Redis中的BigKey 文章目录 Redis中的BigKey什么是BigKey&#xff1f;BigKey的危害找到Bigkey删除BigKey优化BigKeyBigKey对持久化的影响对AOF日志的影响对AOF重写和RDB的影响 什么是BigKey&#xff1f; 大 key 并不是指 key 的值很大&#xff0c;而是 key 对应的 value 很大。…

最新版IntelliJ IDEA 2024.1安装和配置教程 详细图文解说版安装教程

IntelliJ IDEA 2024.1 最新版如何快速入门体验?IntelliJ IDEA 2024.1 安装和配置教程 图文解说版 文章目录 IntelliJ IDEA 2024.1 最新版如何快速入门体验?IntelliJ IDEA 2024.1 安装和配置教程 图文解说版前言 第一步&#xff1a; IntelliJ IDEA 2024.1安装教程第 0 步&…

python数据结构与算法之线性表

1、线性表 是一种由n个元素&#xff08;n> 0 &#xff09;数据元素组成的有限序列&#xff0c;所包含的元素数量通常被称为表的长度 n 0 的表被称为空表&#xff0c;线性表的数据元素可以单一也可以复杂&#xff0c;可以是整数&#xff0c;字符串&#xff0c;也可以是由几…

H.265视频直播点播录像EasyPlayer.js流媒体播放器用户常见问题及解答

EasyPlayer属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;无须安装任何插件&#xff0c;起播快、延迟低、兼容性强&#xff0c;使用非常便捷。 今天我们来汇总下用户常见的几个问题及解答。 1、EasyPlayer.js播放多路H.265视…