Search Unity

Get correct cursor location when no text is in the textfield

Discussion in 'UGUI & TextMesh Pro' started by Trivium_Dev, Aug 7, 2019.

  1. Trivium_Dev

    Trivium_Dev

    Joined:
    Aug 1, 2017
    Posts:
    78
    I have a Cursor class that works similarly to the cursor found in the TMP_InputField except that you can toggle it manually (the input field doesn't have to be in focus). I have mine attached to just a regular TextMeshProUGUI textfield with text coming in from an on-screen keyboard that I've built.

    I've managed to get the cursor to show in the correct location for when there is text in the field using the following code:
    Code (CSharp):
    1. private void LateUpdate()
    2.         {
    3.             Debug.Log("Text bounds: " + this.text.textBounds + ", " + this.text.text.Length);
    4.             if(this.text.text.Length == 0)
    5.             {
    6.                 // place cursor at beginning position????
    7.             }
    8.             else if(this.cursorIndex == 0)
    9.             {
    10.                 // place cursor at bounds position
    11.                 this.cursor.rectTransform.anchoredPosition = this.text.textBounds.center - new Vector3(this.text.textBounds.extents.x, 0, 0);
    12.             }
    13.             else
    14.             {
    15.                 TMP_LineInfo lineInfo = this.text.textInfo.lineInfo[this.text.textInfo.characterInfo[this.cursorIndex - 1].lineNumber];
    16.                 Vector2 pos = new Vector2(this.text.textInfo.characterInfo[this.cursorIndex - 1].xAdvance,
    17.                                             lineInfo.lineExtents.min.y + (lineInfo.lineExtents.max.y - lineInfo.lineExtents.min.y) / 2);
    18.                 this.cursor.rectTransform.anchoredPosition = pos;
    19.             }
    20.         }
    this.text is the TextMeshProUGUI component. The cursorIndex is the desired position of the cursor - it can range from 0 (e.g., the very beginning of the text, before the first character), to this.text.text.Lenght (e.g., after the last character). This works for all situations and text alignments, the only thing that I have to do in Awake is set the Anchor Position of the Cursor RectTransform to match the Pivot of the TextMeshProUGUI RectTransform (so that the values returned by the textfield variables line up without having to do any extra calculations).

    The issue I've run into is the text.textBounds does not return relevant information - it returns Text bounds: Center: (0.0, 0.0, 0.0), Extents: (-2147484000.0, -2147484000.0, 0.0) instead of returning something that would work (for example, Center: (-325.1, 179.9, 0.0), Extents: (0, 20.1, 0.0) would make sense and would work with my layout). The TextInfo information wouldn't work either because there's no text.

    I assume I could always just calculate where it needs to be based on the alignments and this.text.bounds information, but I feel like there should be a better way?

    P.S. I also realize this cursor probably wouldn't work for right-to-left languages. I'll tackle that problem when I need to though....
     
  2. Trivium_Dev

    Trivium_Dev

    Joined:
    Aug 1, 2017
    Posts:
    78
    This seems to do the trick, not sure if there's a better way. The "GetTextInfo" seems to cause the text bounds to be set to something useful:

    if(this.text.text.Length == 0)
    {
    // place cursor at beginning position????
    TMP_TextInfo t = this.text.GetTextInfo("a");
    this.cursor.rectTransform.anchoredPosition = this.text.textBounds.center - new Vector3(this.text.textBounds.extents.x, 0, 0);
    }
     
  3. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Once a text object has been processed / rendered, the TextInfo will be populated with the relevant data as well as the bounds.

    Now if the text is null or empty then it won't get processed but you could set the text with a Zero Width Space \u200B which would then cause the TextInfo to get populated with valid data and bounds. At least it should ...

    You might need to call ForceMeshUpdate() depending on when you are calling this if you need the text object to be processed right away instead of waiting until the end of the frame before it is normally updated.