ugui学习 - 自己实现InputField的文本选中

发布时间 2023-07-31 23:16:33作者: yanghui01

效果

 

代码把光标闪烁拿掉了

using UnityEngine;
using UnityEngine.UI;


[DisallowMultipleComponent]
[RequireComponent(typeof(CanvasRenderer))]
[RequireComponent(typeof(RectTransform))]
public partial class MyInputField_TextSelect : MonoBehaviour, ICanvasElement
{
    private VertexHelper m_CaretVh;
    private Mesh m_CaretMesh;

    public Text m_Text;

    [Range(1, 5)]
    public int m_CaretWidth = 1; //光标宽度
    public Color m_CaretColor = Color.black; //光标颜色

    private int m_CaretPos; //光标在哪个字符处, 光标处字符指示的是光标后面那个字符
    /// <summary>
    /// 选择到哪个字符处.
    /// 从光标处往后选时, [m_CaretPos, m_SelectPos]为选中区域;
    /// 从光标处往前选时, [m_SelectPos, m_CaretPos]为选中区域;
    /// m_CaretPos==m_SelectPos时表示无选择
    /// </summary>
    private int m_SelectPos;
    public Color m_SelectColor = new Color(168f / 255f, 206f / 255f, 255f / 255f, 192f / 255f);


    private CanvasRenderer m_CaretRenderer;
    public CanvasRenderer caretRenderer
    {
        get
        {
            if (null == m_CaretRenderer)
                m_CaretRenderer = GetComponent<CanvasRenderer>();
            return m_CaretRenderer;
        }
    }

    void Start()
    {
        m_CaretVh = new VertexHelper();
        m_CaretMesh = new Mesh();

        var renderer = this.caretRenderer;

        //设置材质和贴图
        renderer.materialCount = 1;
        var caretMat = Graphic.defaultGraphicMaterial;
        renderer.SetMaterial(caretMat, 0);

        var caretTexture = Texture2D.whiteTexture;
        renderer.SetTexture(caretTexture);
    }


    public virtual void Rebuild(CanvasUpdate update)
    {
        switch (update)
        {
        case CanvasUpdate.LatePreRender:
            UpdateCaretVerts();
            break;
        }
    }

    public void LayoutComplete() { }
    public void GraphicUpdateComplete() { }
    public bool IsDestroyed() { return (null == this); }


    private void UpdateCaretVerts()
    {
        var renderer = this.caretRenderer;

        m_CaretVh.Clear();

        if (hasSelection)
            PopulateSelectAreaMesh(m_CaretVh);
        else
            PopulateCaretMesh(m_CaretVh);

        m_CaretVh.FillMesh(m_CaretMesh);
        renderer.SetMesh(m_CaretMesh);
    }

    private void PopulateCaretMesh(VertexHelper vh)
    {
        var textGen = m_Text.cachedTextGenerator;
        if (textGen.characterCount <= 0)
            return;

        //光标显示在字符位置处
        float minX = 0;
        if (m_CaretPos < textGen.characters.Count)
        {
            var chInfo = textGen.characters[m_CaretPos];
            minX = chInfo.cursorPos.x;
            minX /= m_Text.pixelsPerUnit;
        }

        //找出光标处字符在哪一行上
        var caretLine = textGen.lineCount - 1;
        for (int i = 1; i < textGen.lineCount; ++i)
        {
            var tLineInfo = textGen.lines[i];
            if (tLineInfo.startCharIdx > m_CaretPos)
            {
                caretLine = i - 1;
                break;
            }
        }

        var lineInfo = textGen.lines[caretLine];
        float maxY = lineInfo.topY / m_Text.pixelsPerUnit;
        float minY = maxY - lineInfo.height / m_Text.pixelsPerUnit;

        //这边添加一个四边形
        vh.AddVert(new Vector3(minX, maxY), m_CaretColor, new Vector2(0f, 1f)); //左上
        vh.AddVert(new Vector3(minX + m_CaretWidth, maxY), m_CaretColor, new Vector2(1f, 1f)); //右上
        vh.AddVert(new Vector3(minX + m_CaretWidth, minY), m_CaretColor, new Vector2(1f, 0f)); //右下
        vh.AddVert(new Vector3(minX, minY), m_CaretColor, new Vector2(0f, 0f)); //左下

        //顺时针
        vh.AddTriangle(0, 1, 2);
        vh.AddTriangle(0, 2, 3);
    }

    private int GetCharLine(int charIndex, TextGenerator textGen)
    {
        int line = textGen.lineCount - 1;
        for (int i = 1; i < textGen.lineCount; ++i)
        {
            var lineInfo = textGen.lines[i];
            if (lineInfo.startCharIdx > charIndex)
            {
                return i - 1;
            }
        }
        return line;
    }

    private bool hasSelection
    {
        get { return m_CaretPos != m_SelectPos; }
    }

    void PopulateSelectAreaMesh(VertexHelper vh)
    {
        var textGen = m_Text.cachedTextGenerator;
        if (textGen.characterCount <= 0)
            return;

        int selectStartPos = m_CaretPos;
        int selectEndPos = m_SelectPos - 1;
        if (m_SelectPos < m_CaretPos)
        {
            selectStartPos = m_SelectPos;
            selectEndPos = m_CaretPos - 1;
        }

        int startLine = GetCharLine(selectStartPos, textGen);
        int endLine = GetCharLine(selectEndPos, textGen);
        for (int i = startLine, vertStartIndex = 0; i <= endLine; i++, vertStartIndex += 4)
        {
            var lineInfo = textGen.lines[i];
            UICharInfo lineStartCharInfo;
            if (i == startLine)
                lineStartCharInfo = textGen.characters[selectStartPos];
            else
                lineStartCharInfo = textGen.characters[lineInfo.startCharIdx];

            UICharInfo lineEndCharInfo;
            if (i == endLine)
            {
                lineEndCharInfo = textGen.characters[selectEndPos];
            }
            else if (i == textGen.lineCount - 1) //最后一行
            {
                lineEndCharInfo = textGen.characters[textGen.characterCount - 1];
            }
            else
            {
                var nextLineInfo = textGen.lines[i + 1];
                lineEndCharInfo = textGen.characters[nextLineInfo.startCharIdx - 1];
            }

            float minX = lineStartCharInfo.cursorPos.x / m_Text.pixelsPerUnit;
            float maxX = (lineEndCharInfo.cursorPos.x + lineEndCharInfo.charWidth) / m_Text.pixelsPerUnit;
            float maxY = lineInfo.topY / m_Text.pixelsPerUnit;
            float minY = maxY - lineInfo.height / m_Text.pixelsPerUnit;

            //这边添加一个四边形
            vh.AddVert(new Vector3(minX, maxY), m_SelectColor, new Vector2(0f, 1f)); //左上
            vh.AddVert(new Vector3(maxX, maxY), m_SelectColor, new Vector2(1f, 1f)); //右上
            vh.AddVert(new Vector3(maxX, minY), m_SelectColor, new Vector2(1f, 0f)); //右下
            vh.AddVert(new Vector3(minX, minY), m_SelectColor, new Vector2(0f, 0f)); //左下

            //顺时针
            vh.AddTriangle(vertStartIndex + 0, vertStartIndex + 1, vertStartIndex + 2);
            vh.AddTriangle(vertStartIndex + 0, vertStartIndex + 2, vertStartIndex + 3);
        }
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.LeftArrow))
        {
            if (Input.GetKey(KeyCode.LeftShift)) //改变选择区域
            {
                if (m_SelectPos > 0)
                {
                    m_SelectPos--;
                    CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); //下一帧统一更新
                }
            }
            else
            {
                if (m_CaretPos > 0)
                {
                    m_CaretPos--;
                    m_SelectPos = m_CaretPos;
                    CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
                }
                else if (m_CaretPos != m_SelectPos)
                {
                    m_SelectPos = m_CaretPos;
                    CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
                }
            }
        }
        if (Input.GetKeyDown(KeyCode.RightArrow))
        {
            if (Input.GetKey(KeyCode.LeftShift)) //改变选择区域
            {
                if (m_SelectPos < m_Text.text.Length)
                {
                    m_SelectPos++;
                    Debug.Log($"{m_CaretPos}, {m_SelectPos}");
                    CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
                }
            }
            else
            {
                if (m_CaretPos < m_Text.text.Length)
                {
                    m_CaretPos++;
                    m_SelectPos = m_CaretPos;
                    CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
                }
                else if (m_CaretPos != m_SelectPos)
                {
                    m_SelectPos = m_CaretPos;
                    CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
                }
            }
        }
    }

}

 

ugui的InputFiled做了,这边简化掉了的:

(ctrl+)shift+下 选中到下一行的同一列处

(ctrl+)shift+上 选中到上一行的同一列处

ctrl+shift+右 选中到行尾

ctrl+shift+左 选中到行首