Search Unity

Tab Between Input Fields & Button

Discussion in 'Scripting' started by mholmes, Oct 13, 2018.

  1. mholmes

    mholmes

    Joined:
    Dec 8, 2012
    Posts:
    414
    Working on a create account scene and I'm trying to setup navigation. I'm getting an error system object null. Aka it can't find my object. Not sure what I've done wrong:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.UI;
    4. using UnityEngine.EventSystems;
    5.  
    6. public class InputNavigator : MonoBehaviour
    7. {
    8.     public InputField _tbEmail;
    9.     public InputField _tbPassword;
    10.     public InputField _tbBirthMM;
    11.     public InputField _tbBirthDD;
    12.     public InputField _tbBirthYYYY;
    13.     public InputField _tbChatHandle;
    14.     public InputField _tbFirstName;
    15.     public InputField _tbLastName;
    16.     public InputField _tbAddress;
    17.     public InputField _tbCity;
    18.     public InputField _tbState;
    19.     public InputField _tbZip;
    20.     public InputField _tbRecoveryQuestion;
    21.     public InputField _tbRecoveryAnswer;
    22.     public Button _btnCreateAccount;
    23.  
    24.     private EventSystem myEventSystem;
    25.  
    26.     void Start()
    27.     {
    28.         myEventSystem = EventSystem.current;// EventSystemManager.currentSystem;
    29.  
    30.     }
    31.     // Update is called once per frame
    32.     void Update()
    33.     {
    34.         if (Input.GetKeyDown(KeyCode.Tab))
    35.         {
    36.             string current = myEventSystem.currentSelectedGameObject.GetComponent<Selectable>().name;
    37.             myEventSystem = GetComponent<EventSystem>();
    38.  
    39.             switch (current)
    40.             {
    41.                 case "tbEmail":
    42.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbPassword"), new BaseEventData(myEventSystem));
    43.                     break;
    44.                 case "tbPassword":
    45.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbBirthDate_MM"), new BaseEventData(myEventSystem));
    46.                     break;
    47.                 case "tbBirthDate_MM":
    48.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbBirthDate_DD"), new BaseEventData(myEventSystem));
    49.                     break;
    50.                 case "tbBirthDate_DD":
    51.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbBirthDate_YYYY"), new BaseEventData(myEventSystem));
    52.                     break;
    53.                 case "tbBirthDate_YYYY":
    54.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbChatHandle"), new BaseEventData(myEventSystem));
    55.                     break;
    56.                 case "tbChatHandle":
    57.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbFirstName"), new BaseEventData(myEventSystem));
    58.                     break;
    59.                 case "tbFirstName":
    60.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbLastName"), new BaseEventData(myEventSystem));
    61.                     break;
    62.                 case "tbLastName":
    63.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbAddress"), new BaseEventData(myEventSystem));
    64.                     break;
    65.                 case "tbAddress":
    66.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbCity"), new BaseEventData(myEventSystem));
    67.                     break;
    68.                 case "tbCity":
    69.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbState"), new BaseEventData(myEventSystem));
    70.                     break;
    71.                 case "tbState":
    72.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbZipCode"), new BaseEventData(myEventSystem));
    73.                     break;
    74.                 case "tbZipCode":
    75.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbRecoveryQuestion"), new BaseEventData(myEventSystem));
    76.                     break;
    77.                 case "tbRecoveryQuestion":
    78.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbRecoveryAnswer"), new BaseEventData(myEventSystem));
    79.                     break;
    80.                 case "tbRecoveryAnswer":
    81.                     myEventSystem.SetSelectedGameObject(GameObject.Find("btnCreateAccount"), new BaseEventData(myEventSystem));
    82.                     break;
    83.                 case "btnCreateAccount":
    84.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbEmail"), new BaseEventData(myEventSystem));
    85.                     break;
    86.                 default:
    87.                     myEventSystem.SetSelectedGameObject(GameObject.Find("tbEmail"), new BaseEventData(myEventSystem));
    88.                     break;
    89.             }
    90.         }
    91.     }
    92. }
    93.  
     
  2. Cyber-Dog

    Cyber-Dog

    Joined:
    Sep 12, 2018
    Posts:
    352
    That's allot of code for me to see whats happening without testing, although if I may add a suggestion to your current tabbing logic.

    When writing desktop applications, tabbing is controlled by each object having a bool tabStop and tabIndex.
    So tabStop says if that object is tabbable, and tabIndex says what order it is in.

    So I would recommend an implementation of something like this.. Its hacky but I threw it together real quick.
    Just drag your list of objects to the list in order you want the to cycle, and set there tabstop to true.

    Code (CSharp):
    1. public class TabNavigator : MonoBehaviour
    2. {
    3.     public int startIndex = 0;
    4.     private int targetIndex;
    5.    
    6.     public List<ObjectTab> objectTabs = new ist<ObjectTab>();
    7.     private EventSystem myEventSystem;
    8.    
    9.     class ObjectTab
    10.     {
    11.         public bool tabStop;
    12.         public GameObject tabObject;
    13.     }
    14.    
    15.     void Start()
    16.     {
    17.         myEventSystem = EventSystem.current;
    18.         targetIndex = startIndex - 1;
    19.         SetCurrentTabObject(targetIndex);
    20.     }
    21.    
    22.     void Update()
    23.     {
    24.         if (Input.GetKeyDown(KeyCode.Tab))
    25.             SetCurrentTabObject();
    26.     }
    27.    
    28.     void SetCurrentTabObject()
    29.     {
    30.         targetIndex ++;
    31.         if (targetIndex > objectTabs.Count)
    32.             targetIndex = 0;
    33.         if (!objectTabs[targetIndex].tabStop || !objectTabs[targetIndex].tabObject.activeSelf)
    34.         {
    35.             SetCurrentTabObject()
    36.             return;
    37.         }
    38.         myEventSystem.SetSelectedGameObject(objectTabs[targetIndex].tabObject);  
    39.     }
    40. }
    Alternatively, add a tabIndex to the ObjectTab class and do a search to find the the index number or next highest. That way you don't have to worry about the order in which they are in the list. But I'll leave that up to you :)
     
  3. bgprocks

    bgprocks

    Joined:
    Oct 28, 2012
    Posts:
    8
    Cyber Dog, That is a nice piece of code, Thanks. I made some minor mods, plus anyone who use this should make sure the input you are referencing is active at the time. In my case, I keep some field hidden until the user select certain options. I am implementing this in my game tonight.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.EventSystems;
    3. using System.Collections.Generic;
    4.  
    5. public class TabNavigator : MonoBehaviour
    6. {
    7.     public int startIndex = 0;
    8.     private int targetIndex;
    9.  
    10.     public List<ObjectTab> objectTabs = new List<ObjectTab>();
    11.     private EventSystem myEventSystem;
    12.  
    13.  
    14.  
    15.     void Start()
    16.     {
    17.         myEventSystem = EventSystem.current;
    18.         targetIndex = startIndex - 1;
    19.         SetCurrentTabObject();
    20.     }
    21.  
    22.     void Update()
    23.     {
    24.         if (Input.GetKeyDown(KeyCode.Tab))
    25.             SetCurrentTabObject();
    26.     }
    27.  
    28.     void SetCurrentTabObject()
    29.     {
    30.      
    31.         targetIndex++;
    32.         if (targetIndex >= objectTabs.Count)
    33.             targetIndex = 0;
    34.  
    35.      
    36.  
    37.         if (!objectTabs[targetIndex].tabStop || !objectTabs[targetIndex].tabObject.activeSelf)
    38.         {
    39.             SetCurrentTabObject();
    40.             return;
    41.         }
    42.         myEventSystem.SetSelectedGameObject(objectTabs[targetIndex].tabObject);
    43.     }
    44. }
    45.  
    46. [System.Serializable]
    47. public class ObjectTab
    48. {
    49.     public bool tabStop;
    50.     public GameObject tabObject;
    51. }
     
    Cyber-Dog likes this.
  4. Cyber-Dog

    Cyber-Dog

    Joined:
    Sep 12, 2018
    Posts:
    352
  5. ETGgames

    ETGgames

    Joined:
    Jul 10, 2015
    Posts:
    101
    Code (CSharp):
    1. using TMPro;
    2. using UnityEngine.UI;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using UnityEngine;
    6. using UnityEngine.InputSystem;
    7.  
    8. //used so we can easily selcet the next text box when enter or tab is pressed
    9. public class InputFieldGroup : MonoBehaviour
    10. {
    11.     [SerializeField] private List<TMP_InputField> _inputFields;
    12.  
    13.     private int _prevSelectedIdx = 0;
    14.     private bool _preventSelection = false;
    15.  
    16.     private void Start()
    17.     {
    18.         for (int i = 0; i < _inputFields.Count; i++)
    19.         {
    20.             var field = _inputFields[i];
    21.             var tempIdx = i; //needed or all teh lambdas get the same value by reference for some dum S*** reason
    22.             field.onSelect.AddListener((data) =>
    23.            {
    24.                _preventSelection = false;
    25.                _prevSelectedIdx = tempIdx;
    26.            });
    27.  
    28.             field.onDeselect.AddListener((data) =>
    29.                 {
    30.                     if (!AreAnySelected())
    31.                         _preventSelection = true;
    32.                 });
    33.         }
    34.     }
    35.  
    36.  
    37.     void Update()
    38.     {
    39.         if (Keyboard.current.tabKey.wasPressedThisFrame || Keyboard.current.enterKey.wasPressedThisFrame)
    40.         {
    41.             if (!_preventSelection) //so it works with multiple input field groups on screen at once
    42.             {
    43.                 SelectNextInputField();
    44.  
    45.             }
    46.         }
    47.     }
    48.  
    49.     private void SelectNextInputField()
    50.     {
    51.         var next = _inputFields[(_prevSelectedIdx + 1) % _inputFields.Count];
    52.         next.Select();
    53.         next.ActivateInputField();
    54.     }
    55.  
    56.     private bool AreAnySelected()
    57.     {
    58.         for (int i = 0; i < _inputFields.Count; i++)
    59.         {
    60.             if (_inputFields[i].isFocused) return true;
    61.         }
    62.         return false;
    63.     }
    64.  
    65. }
    66.