- Home /
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!
Answer by aeasterling · May 31, 2015 at 11:44 AM
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();
}
}
}
}
[1]: /storage/temp/47344-screen-shot-2015-05-30-at-31648-pm.png
Thank you for your great code! And, I think you should add this code at 42 line to back to the first selectable object.
if (next == null)
{
next = Selectable.allSelectables[0];
}
Answer by sed · Dec 11, 2014 at 03:46 PM
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/
In Unity 5, this portion doesn't seem to be necessary --
if (field != null) field.OnPointerClick(new PointerEventData(eventSystem));
Focus is properly passed to text fields with SetSelectedGameObject(). Perhaps this was needed in 4.6, maybe? For Unity 5, SetSelectedGameObject() works just fine.
I'm not super happy with the top comment here. You have to add it every single UI element in your game, which to me seems super error prone. (What if you forget to add it on a UI element? Suddenly tab won't work.)
Furthermore, I'm a little confused about the "direction" property -- it doesn't seem to be useful. Why would you want tab to go backwards ins$$anonymous$$d of forward?
Also, there's no support for SHIFT, which seems like something you'd want. I often use Shift+Tab to move between elements.
Also, you have to actually give initial focus to an element for this script to take over. That is, if you load up a UI and don't give initial focus to something, the user would have to click on something to let tab take over. However, one would expect that pressing tab with no focus would go ahead and select the next selectable element. (This is how browsers work for example.)
I've offered another solution below--
Answer by jessejarvis · Oct 14, 2016 at 06:42 PM
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();
}
}
}
}
}
Thank you! I was thinking on doing something like this
Answer by JITENDRA-RAJ · Sep 04, 2014 at 07:02 AM
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 believe the OP was about the new Unity GUI system.
Answer by ConflictedDev · Jul 26, 2017 at 09:01 PM
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);
}
}