4

The UI InputField when it gets focus highlights all text inside. I would like to move the caret to the end of the text so the user can keep writing the text within. Currently I have a hack solution, which works, however there it is still a short moment when the text is highlighted. Here is my hack:

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class TextFieldBehaviour : MonoBehaviour, ISelectHandler
{
    private InputField inputField;
    private bool isCaretPositionReset = false;

    void Start()
    {
        inputField = gameObject.GetComponent<InputField>();
    }

    public void OnSelect (BaseEventData eventData) 
    {
        isCaretPositionReset = false;
    }

    void Update()
    {
        if(inputField.isFocused == true && isCaretPositionReset == false)
        {
            inputField.caretPosition = inputField.text.Length;
            isCaretPositionReset = true;
        }
    }
}

I was also checking out the source code of InputField. But I have trouble trouble creating a custom one without the SelectAll() function. I am getting a bunch of errors due to the protection level of the UnityEngine.UI.SetPropertyUtility.

rjth
  • 95
  • 1
  • 3
  • 12

3 Answers3

6

There is a trick to disable the short moment when the text is highlighted. I managed to redo this without the Update() function.

1.Get the Color of the InputField.selectionColor. Set its alpha to 0.

2.Apply the new color from #1 to the InputField.

3.Wait for one frame. You must because Unity caret waits for one frame to appear.

4.Change the InputField caret position.

5.Change the InputField.selectionColor alpha back to 1.

public class TextFieldBehaviour : MonoBehaviour, ISelectHandler
{
    private InputField inputField;
    private bool isCaretPositionReset = false;

    void Start()
    {
        inputField = gameObject.GetComponent<InputField>();
    }

    public void OnSelect(BaseEventData eventData)
    {
        StartCoroutine(disableHighlight());
    }

    IEnumerator disableHighlight()
    {
        Debug.Log("Selected!");

        //Get original selection color
        Color originalTextColor = inputField.selectionColor;
        //Remove alpha
        originalTextColor.a = 0f;

        //Apply new selection color without alpha
        inputField.selectionColor = originalTextColor;

        //Wait one Frame(MUST DO THIS!)
        yield return null;

        //Change the caret pos to the end of the text
        inputField.caretPosition = inputField.text.Length;

        //Return alpha
        originalTextColor.a = 1f;

        //Apply new selection color with alpha
        inputField.selectionColor = originalTextColor;
    }
}

NOTE:

The best way to move caret to the end of the text is with the MoveTextEnd function instead of inputField.caretPosition. You will notice a bug with inputField.caretPosition if your text is long.

If you care about this, replace inputField.caretPosition = inputField.text.Length; with inputField.MoveTextEnd(false); in the code above.

Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Thank you for the solution. I've managed to come up with a working solution myself. If you have a moment I would appreciate some feedback on it. – rjth Jan 05 '17 at 15:18
  • Sure, I will take a look. – Programmer Jan 05 '17 at 15:27
  • Hi @programmer. Great code by the way! Worked a treat for me on PC! I tested the app on a tablet but the whole text was still being highlighted :/ do you know how to get your code to work for mobiles devices too? – Lloyd Sep 09 '19 at 16:08
  • Sorry, is anyone able to help? :/ – Lloyd Sep 12 '19 at 12:24
1

Okay so I've figured it out. I needed to inherit from the original InputField and extend it with the necessary functionality. Here is the working script:

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class CustomInputField : InputField
{
    new public bool Focused = false;
    new public bool Deactivated = false;

    new public void ActivateInputField()
    {
        Focused = true;
        base.ActivateInputField();
    }

    public override void OnDeselect(BaseEventData eventData)
    {
        Deactivated = true;
        DeactivateInputField();
        base.OnDeselect(eventData);
    }

    public override void OnPointerClick(PointerEventData eventData)
    {
        if(Deactivated)
        {
            MoveTextEnd(true);
            Deactivated = false;
        }
        base.OnPointerClick(eventData);
    }

    protected override void LateUpdate()
    {
        base.LateUpdate();
        if(Focused)
        {
            MoveTextEnd(true);
            Focused = false;
        }
    }
}
rjth
  • 95
  • 1
  • 3
  • 12
  • 1
    Interesting solution. Few problems though. 1.You should [not](http://stackoverflow.com/a/1853908/3785314) override a non virtual function(`ActivateInputField`). 2.I tried this with Unity 5.5 and it does not work.It simply does not work. You can't even click on the InputField. – Programmer Jan 05 '17 at 15:38
  • Thanks for the response. It works on my side though with Unity 5.5.0f3. Yeah shadowing or hiding a non-virtual function is never a good idea. I might try a little bit different approach later. – rjth Jan 05 '17 at 16:13
  • 1
    Hi, I just realized that your script removed the Text component of my InputField. I attached it back and was able to click on it. The code does not fix the problem. The short moment when the text is highlighted is **still** there. The first time you click it, the problem is not there. Other times, the problem is there. Happy coding! – Programmer Jan 05 '17 at 16:19
  • Well I will be sticking with your solution for now. It is way more elegant. Thanks! – rjth Jan 05 '17 at 16:33
0

Thanks for your solutions, they were really helpful, I think the following adaption of your methods eradicates the fraction of time the selection is highlighted. It works in my use case, have not robustly tested it. (Actually this is for a slightly different use case where you can both select with mouse and using event system, but will leave in case helpful to someone looking for this, as I was).

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class SourceCodeTextStopFocusSelection : InputField
{
    bool justSelected = false;
    int selectedPosition;

    public override void OnSelect(BaseEventData eventData)
    {
        base.OnSelect(eventData);
        if (eventData is PointerEventData)
        {
            Vector2 mousePos;
            PointerEventData pointerData = eventData as PointerEventData;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(textComponent.rectTransform, pointerData.position, pointerData.pressEventCamera, out mousePos);
            selectedPosition = GetCharacterIndexFromPosition(mousePos);
        }
        else
        {
            selectedPosition = text.Length;
        }
        justSelected = true;
    }

    protected override void LateUpdate()
    {
        base.LateUpdate();
        if (justSelected)
        {
            caretPosition = selectedPosition;

            ForceLabelUpdate();
            justSelected = false;
        }
    }
}