4.6 uGUI select next InputField with Enter/tab

I’ve been trying to make a login screen. ( just for practice ). But I dont know how to ‘select’ the password InputField when you press enter or tab at the username InputField.

I did however add a listener. This listener actually gets the value of the InputField and puts it in a variable. Thats all fine, but after that it should go to the Password InputField. But it doesn’t do that.

How can I do that?

Also, once you’ve entered your password, and deselect the InputField, it shows the value of the password. No ***** whatsoever. Is this a bug or am I doing something wrong?

*C# is preferred.

Thanks in advance!

I wasn’t happy with the specified answers, so I’d like to offer another solution.

Add the following script to any global singleton game object. (I think that’s a common pattern in Unity projects – an empty gameobject at the root level which has no renderer, but contains any scripts intended to be loaded just once, for the whole game/level.)

When tab is pressed, and there is an object already selected, the “next” Selectable GameObject is selected. Now, this is where things get tricky: Unity only gives us the ability to map a “Right”, “Left”, “Up” and “Down” selectable. This is intended to be used with keyboard arrow keys or gamepad arrows, presumably. However, “Tab” behaves a bit differently, generally speaking. It’s USUALLY either “Right” or “Down”, but in certain situations, the next element is something different. For example, in a spreadsheet, “Tab” would usually select the next right element, except when there is no right element, in which case it should select the left-most element on the next row.

Anyways, we don’t handle that complexity in the script below. Fully supporting tab is probably something that Unity should build in, as an additional option to “Right”, “Left”, “Up”, “Down”. We can get 90% of the way there with the heuristic of the “next” tab being “Right”, except when right doesn’t exist, in which case it’s “Down”.

You can set up the Navigation of a Selectable such that the next right/down, or next left/up points to the appropriate element, if you think your users will mostly be using tab and not arrow keys. For my project that makes sense, but it might not make sense for you.

Finally, if there is no object selected when you press Tab, we just select the first selectable object.

Script is here–

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

public class TabSelect : MonoBehaviour {

    private EventSystem eventSystem;

	// Use this for initialization
	void Start () {
        this.eventSystem = EventSystem.current;
	}
	
	// Update is called once per frame
	void Update () {
        // When TAB is pressed, we should select the next selectable UI element
        if (Input.GetKeyDown(KeyCode.Tab)) {
            Selectable next = null;
            Selectable current = null;

            // Figure out if we have a valid current selected gameobject
            if (eventSystem.currentSelectedGameObject != null) {
                // Unity doesn't seem to "deselect" an object that is made inactive
                if (eventSystem.currentSelectedGameObject.activeInHierarchy) {
                    current = eventSystem.currentSelectedGameObject.GetComponent<Selectable>();
                }
            }
            
            if (current != null) {
                // When SHIFT is held along with tab, go backwards instead of forwards
                if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) {
                    next = current.FindSelectableOnLeft();
                    if (next == null) {
                        next = current.FindSelectableOnUp();
                    }
                } else {
                    next = current.FindSelectableOnRight();
                    if (next == null) {
                        next = current.FindSelectableOnDown();
                    }
                }
            } else {
                // If there is no current selected gameobject, select the first one
                if (Selectable.allSelectables.Count > 0) {
                    next = Selectable.allSelectables[0];
                }
            }
            
            if (next != null)  {
                next.Select();
            }
        }
	}
}

As of 4.6.1:
Attach this script to your Input Field. Also to show ‘*’ you have to set the “Content Type” to password. To navigate using ‘Enter’ set EndEdit handler (in editor) to the next input fields Select method.

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class InputFieldNavigation : MonoBehaviour {

	public enum NavigationDirection { Down, Up, };

	public NavigationDirection Direction;

	private EventSystem system;

	void Start ()
	{
		system = EventSystem.current;
	}
	
	public void Update()
	{
		if (Input.GetKeyDown(KeyCode.Tab) && system.currentSelectedGameObject == gameObject)
		{

			Selectable next = null;

			if(Direction == NavigationDirection.Down)
			{
				next = system.currentSelectedGameObject.GetComponent<Selectable>().FindSelectableOnDown();
			}
			else 
			{
				next = system.currentSelectedGameObject.GetComponent<Selectable>().FindSelectableOnUp();
			}

			if (next!= null) 
			{
				system.SetSelectedGameObject(next.gameObject);
				next.Select();
				var field = next as InputField;
				if(field != null) field.OnPointerClick(new PointerEventData(system));
			}
			//else Debug.Log("next nagivation element not found");
			
		}
	}
}

I expanded on the idea presented here: http://forum.unity3d.com/threads/tab-between-input-fields.263779/

Okay. So, NONE of this worked for me so I scripted my own.

Setup Instructions: Drag script on any game object (For me I did the “Login Window” in my Canvas. Once the script is in your scene, set NavFields to the size of the fields you want and drag in your GameObjects. This allows tab navigation between any game objects, I personally use this to tab between Username, password, and other buttons I have.

Enjoy!

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class SLINavigateInputFields : MonoBehaviour
{
    public GameObject[] navFields;
    private int selected;
    private int maxArray;
    private GameObject currentSelected;

    void Start()
    {
        selected = 0;
        maxArray = navFields.Length -1;
        currentSelected = navFields[selected].gameObject;
    }

    void Update()
    {
        if (Input.GetKey(KeyCode.LeftShift) && Input.GetKeyDown(KeyCode.Tab))
        {
            if (selected > 0)
            {
                selected--;
                currentSelected = navFields[selected].gameObject;

                if (EventSystem.current == null)
                    return;
                EventSystem.current.SetSelectedGameObject(currentSelected);
                if (GetComponent<InputField>() != null)
                {
                    GetComponent<InputField>().Select();
                    GetComponent<InputField>().ActivateInputField();
                }
            }
        }
        else if (Input.GetKeyDown(KeyCode.Tab))
        {
            if (selected < maxArray)
            {
                selected++;
                currentSelected = navFields[selected].gameObject;

                if (EventSystem.current == null)
                    return;
                EventSystem.current.SetSelectedGameObject(currentSelected);
                if (GetComponent<InputField>() != null)
                {
                    GetComponent<InputField>().Select();
                    GetComponent<InputField>().ActivateInputField();
                }
            }
        }
    }
}

For select Next filed use SetNextControlName .

 GUI.SetNextControlName("loginUsername");
        		GUILayout.BeginArea (new Rect (100, 186, 199, 63));
        		loginUsername = GUILayout.TextField(loginUsername, login_txt.customStyles[0],GUILayout.Height(30));//,GUILayout.MaxWidth(200),GUILayout.MaxHeight(23));	
        		GUILayout.EndArea ();
        		GUI.SetNextControlName("loginPassword ");
        		if (GUI.GetNameOfFocusedControl () == string.Empty) {
        				GUI.FocusControl ("loginUsername");
        		} 
    GUILayout.BeginArea (new Rect (100, 230, 199, 63));
    		loginPassword = GUILayout.PasswordField(loginPassword, '*',UIManager.instance.login_txt.customStyles[0],GUILayout.Height(30));
    		GUILayout.EndArea ();

I hate to add to the implementations already provided but I found:

  • Selectable.allSelectables[0] does NOT always provide the top-most selection
  • Getting the next selectable for a complex layout isn’t straight-forward
  • None of the above include navigation with the Enter key

I’ve implemented:

  • A simple sorting method that orders all active selectables based on their X and Y positioning (left-to-right followed by top-to-bottom)
  • A simple, reliable way to navigate forward/backward; includes optional wrap-around to first/last, respectively
  • Navigation with Tab, Enter and Numpad Enter
    (Enter is setup to only work from input fields, where it will also only select the next selection if it is an input field or button)
  • If nothing is selected, the first selectable is selected (Enter is setup to only work if it is an input field or button)

NOTE: The sorting of selectables is done each time (as this is a simple implementation), you could do this elsewhere when UI elements are enabled/disabled instead.

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class UIHotkeySelect : MonoBehaviour
{
    private List<Selectable> m_orderedSelectables;

    private void Awake()
    {
        m_orderedSelectables = new List<Selectable>();
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Tab))
        {
            HandleHotkeySelect(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift), true, false); // Navigate backward when holding shift, else navigate forward.
        }

        if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter))
        {
            HandleHotkeySelect(false, false, true);
        }
    }

    private void HandleHotkeySelect(bool _isNavigateBackward, bool _isWrapAround, bool _isEnterSelect)
    {
        SortSelectables();

        GameObject selectedObject = EventSystem.current.currentSelectedGameObject;
        if (selectedObject != null && selectedObject.activeInHierarchy) // Ensure a selection exists and is not an inactive object.
        {
            Selectable currentSelection = selectedObject.GetComponent<Selectable>();
            if (currentSelection != null)
            {
                if (_isEnterSelect)
                {
                    if (currentSelection.GetComponent<InputField>() != null)
                    {
                        ApplyEnterSelect(FindNextSelectable(m_orderedSelectables.IndexOf(currentSelection), _isNavigateBackward, _isWrapAround));
                    }
                }
                else // Tab select.
                {
                    Selectable nextSelection = FindNextSelectable(m_orderedSelectables.IndexOf(currentSelection), _isNavigateBackward, _isWrapAround);
                    if (nextSelection != null)
                    {
                        nextSelection.Select();
                    }
                }
            }
            else
            {
                SelectFirstSelectable(_isEnterSelect);
            }
        }
        else
        {
            SelectFirstSelectable(_isEnterSelect);
        }
    }

    ///<summary> Selects an input field or button, activating the button if one is found. </summary>
    private void ApplyEnterSelect(Selectable _selectionToApply)
    {
        if (_selectionToApply != null)
        {
            if (_selectionToApply.GetComponent<InputField>() != null)
            {
                _selectionToApply.Select();
            }
            else
            {
                Button selectedButton = _selectionToApply.GetComponent<Button>();
                if (selectedButton != null)
                {
                    _selectionToApply.Select();
                    selectedButton.OnPointerClick(new PointerEventData(EventSystem.current));
                }
            }
        }
    }

    private void SelectFirstSelectable(bool _isEnterSelect)
    {
        if (m_orderedSelectables.Count > 0)
        {
            Selectable firstSelectable = m_orderedSelectables[0];
            if (_isEnterSelect)
            {
                ApplyEnterSelect(firstSelectable);
            }
            else
            {
                firstSelectable.Select();
            }
        }
    }

    private Selectable FindNextSelectable(int _currentSelectableIndex, bool _isNavigateBackward, bool _isWrapAround)
    {
        Selectable nextSelection = null;

        int totalSelectables = m_orderedSelectables.Count;
        if (totalSelectables > 1)
        {
            if (_isNavigateBackward)
            {
                if (_currentSelectableIndex == 0)
                {
                    nextSelection = (_isWrapAround) ? m_orderedSelectables[totalSelectables - 1] : null;
                }
                else
                {
                    nextSelection = m_orderedSelectables[_currentSelectableIndex - 1];
                }
            }
            else // Navigate forward.
            {
                if (_currentSelectableIndex == (totalSelectables - 1))
                {
                    nextSelection = (_isWrapAround) ? m_orderedSelectables[0] : null;
                }
                else
                {
                    nextSelection = m_orderedSelectables[_currentSelectableIndex + 1];
                }
            }
        }

        return (nextSelection);
    }

    private void SortSelectables()
    {
        List<Selectable> originalSelectables = Selectable.allSelectables;
        int totalSelectables = originalSelectables.Count;
        m_orderedSelectables = new List<Selectable>(totalSelectables);
        for (int index = 0; index < totalSelectables; ++index)
        {
            Selectable selectable = originalSelectables[index];
            m_orderedSelectables.Insert(FindSortedIndexForSelectable(index, selectable), selectable);
        }
    }

    ///<summary> Recursively finds the sorted index by positional order within m_orderedSelectables (positional order is determined from left-to-right followed by top-to-bottom). </summary>
    private int FindSortedIndexForSelectable(int _selectableIndex, Selectable _selectableToSort)
    {
        int sortedIndex = _selectableIndex;
        if (_selectableIndex > 0)
        {
            int previousIndex = _selectableIndex - 1;
            Vector3 previousSelectablePosition = m_orderedSelectables[previousIndex].transform.position;
            Vector3 selectablePositionToSort = _selectableToSort.transform.position;

            if (previousSelectablePosition.y == selectablePositionToSort.y)
            {
                if (previousSelectablePosition.x > selectablePositionToSort.x)
                {
                    // Previous selectable is in front, try the previous index:
                    sortedIndex = FindSortedIndexForSelectable(previousIndex, _selectableToSort);
                }
            }
            else if (previousSelectablePosition.y < selectablePositionToSort.y)
            {
                // Previous selectable is in front, try the previous index:
                sortedIndex = FindSortedIndexForSelectable(previousIndex, _selectableToSort);
            }
        }

        return (sortedIndex);
    }
}