Search Unity

How do I control which GUI item has input focus?

Discussion in 'Unity UI & TextMesh Pro' started by Mortom, Aug 21, 2014.

  1. Mortom

    Mortom

    Joined:
    Aug 21, 2014
    Posts:
    20
    I've been through the UI tutorials and everything looks great. However they didn't seem to be anything covering how to navigate between screens. I've looked at the demo project but that doesn't show how to navigate between screens.

    Looking closely at the 2014 Keynote, it was obvious that each screen was a separate GameObject that was inactive.

    I've created a number of GameObjects, as a child of a single Canvas, and each GameObject has a Rect Transform on it. I've name each of these game objects as my screens, e.g. Main Menu etc. On each of these I've added a series of buttons. Each button then has two OnClick actions. The first one sets the owning screen to be inactive and the second action is to activate the screen I want to switch two.

    So far so good. I can click on each button and the relevant screen appears and the old one disappears. Each button plays an scale animation when it gains focus when the mouse pointer enters it.

    However I'm having problems controlling which button has focus. The EventSystem allows me to set a First Selected object, which I've done for my main menu. I can then use the keyboard to navigate around. However when I select the button, using the spacebar, the new screen opens but there doesn't seem to be a way to select which button has focus by default. In fact the selection is still on the hidden button. I proved this by writing a script that outputted the currently selected item from the EventSystem.

    I tried dragging the EventSystem into an OnClick slot in the button but it didn't have any methods where I could select the focused item. I also attempted to write a script but again I could see anything on the EventSystem interface that would allow me to control item focus.

    This is kind of critical for non-pointer interface such as those used on consoles with a controller. I have also had many cases in the past with TCR issues. The classic problem is when a button is focused and then becomes disabled for some reason. For example a Split Screen button becomes greyed out if a second controlled is unplugged from the console. If the button was highlighted then the system needs to un-highlight it and move the focus to the next available button.

    So how do I control focus?
     
    Xonatron likes this.
  2. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,087
    After you show the new Panel you can call: SetSelectedGameObject on the EventSystem with the desired button.

    Code (csharp):
    1.  
    2. EventSystemManager.currentSystem.SetSelectedGameObject (theButton);
    3.  
     
  3. MuNgLo

    MuNgLo

    Joined:
    Jan 27, 2014
    Posts:
    31
    SetSelectedGameObject needs two arguments. Second being a BaseEventData.
    I can't find anything that helps me anywhere. I still haven't wrapped my head around how the eventsystem really works. It would really help if I could see a working example.
     
  4. Mortom

    Mortom

    Joined:
    Aug 21, 2014
    Posts:
    20
    That works. I just pass null as the second parameter.

    The script looks like this

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.EventSystems;
    4.  
    5. public class OpenScreen : MonoBehaviour
    6. {
    7.      public GameObject defaultButton;
    8.  
    9.      public void OnOpen()
    10.      {
    11.           if (defaultButton != null)
    12.           {
    13.                 EventSystemManager.currentSystem.SetSelectedGameObject(defaultButton, null);
    14.           }
    15.      }
    16.  
    17. }
    18.  
    I attached this to the top level screen GameObject and get the button OnClick event to call it.

    From inspecting the code I found two classes AxisEventData and PointerEventData which inherit from BaseEventData. I guess I could create our own custom event data class but I don't really have enough information on how the event system works to decide if that is even necessary.

    Thanks for the help.
     
    Last edited: Aug 22, 2014
    twobob likes this.
  5. MuNgLo

    MuNgLo

    Joined:
    Jan 27, 2014
    Posts:
    31
    Is it a bug? I mean as far as I can see there is no reason why you would have to give an eventdata just to tell the eventsystem to shift focus to a different object. The fact it works with passing null into the second argument kinda agrees with that idea. Or, which is more likely, I am missing something.

    --EDIT--
    Ok so now I use
    Code (CSharp):
    1. EventSystemManager.currentSystem.SetSelectedGameObject( input.gameObject, null );
    and it kinda works.
    It's on an InputField so it has focus on it but you need to press enter to trigger the listening state of the InputField again. I'm guessing that if I feed the correct baseEvent as second argument I can trigger the InputField to go directly into listening state. But how?
     
    Last edited: Aug 22, 2014
  6. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,087
    With input field selection and activation are two states, you can select it, but we don't enter the editable state until enter is pressed.

    We have some API to do the selection, but it is currently private. I've now made it public for the future: ActivateInputField
     
    twobob likes this.
  7. MuNgLo

    MuNgLo

    Joined:
    Jan 27, 2014
    Posts:
    31
    So with that you can, in code, select an InputField and activate its editing state?
    For clarity, this is what I need and I suspect needs to be possible for many others. By code, activate and present player with a dialog of any sort which demand textinput. At that time also put focus on and prepare the InputField so all the player have to do is start typing. If needed, as in my case, also let focus and editstate still be active after submission so the player can input more without selecting and triggering editstate.
    It gets a bit hard to be precise since it involves several different states and so on but I hope I made myself clear.
     
  8. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,087
    This should do what you want :)
     
  9. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    284
    Code (CSharp):
    1. EventSystemManager.currentSystem.SetSelectedGameObject(defaultButton, null);
    I have used this myself to set the selected button on my sub-menus, but there is a problem with mouse input (Beta 20). The newly selected object will remain selected, even after selecting something else (with the mouse) and will only be deselected after the mouse has entered and exited it. However, this problem does not exist for the "First Selected" button set in the editor. In that case the button is deselected as soon as the mouse pointer is moved. Can I do something in the code to get the same behavior?
     
  10. huulong

    huulong

    Joined:
    Jul 1, 2013
    Posts:
    41
    I know the post is old (considering the development of Unity UI) but to those still in doubt:

    SetSelectedGameObject also works with one argument (at least in Unity 4.6 released): the game object to select.
    The overload SetSelectedGameObject(GameObject) will in turn call SetSelectedGameObject(selected, baseEventDataCache) where baseEventDataCache is the BaseEventData you are looking for. To make things simple, it is like a pointer you can move around in the menu. However, baseEventDataCache is a private attribute of EventSystem which is lazily initialized (actually it is a property and when you try to get it, it creates one if needed), so you cannot call SetSelectedGameObject(GameObject, BaseEventData) with this pointer.

    If you try to break through and use new BaseEventData as a second argument, you will end up with a second pointer, then a third, etc. It could be used for other purpose such as letting multiple players choose their options simultaneously in a menu, but this is not what you want here.

    I give the code for the new syntax (Unity 4.6 released):

    Code (CSharp):
    1. using UnityEngine.EventSystems;
    2.  
    3. EventSystem.current.SetSelectedGameObject(yourObject);
     
  11. antoniog9430

    antoniog9430

    Joined:
    Mar 15, 2014
    Posts:
    13
    The best way to go about it is so simple ! I can't believe I didn't realize this at first
    Let's say you have a pause menu
    In the pause menu you have 3 buttons
    Resume game
    Inventory
    Player stats
    On those you would have 2 on click functions
    One closes the pause menu
    One opens the other menu
    Now add a third on click function
    Drag the first button from that menu onto
    the on click functions game object box
    Instead of selecting the game object select button in the drop down menu
    Then click select and bobs your uncle
    You can now select the first button
    Rinse and repeat both ways and you have a fully functioning menu without any scripting ! Hope it answers your question pal!
     
    Navid likes this.
  12. passeride

    passeride

    Joined:
    Oct 9, 2013
    Posts:
    1
    Hey

    This is a simple way i made for a simple menu, going to leave it here in case it helps anyone

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using UnityEngine.EventSystems;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6.  
    7. public class MainMenuGUIScript : MonoBehaviour {
    8.  
    9.     public List<GameObject> SelectionArray = new List<GameObject>();
    10.     int currentSelected = 0;
    11.  
    12.     // Use this for initialization
    13.     void Start () {
    14.         EventSystem.current.SetSelectedGameObject(SelectionArray[0], null);
    15.     }
    16.    
    17.     // Update is called once per frame
    18.     void Update () {
    19.         if (Input.GetKeyDown(KeyCode.Tab)) {
    20.             if(Input.GetKey(KeyCode.LeftShift))
    21.             {
    22.                 // Cycle selection Array
    23.                 if (currentSelected - 1 < 0)
    24.                 {
    25.                     currentSelected = SelectionArray.Count - 1;
    26.                     EventSystem.current.SetSelectedGameObject(SelectionArray[currentSelected], null);
    27.                 }
    28.                 else
    29.                 {
    30.                     EventSystem.current.SetSelectedGameObject(SelectionArray[--currentSelected], null);
    31.                 }
    32.             }else{
    33.                 // Cycle selection Array
    34.                 if (currentSelected + 1 > SelectionArray.Count)
    35.                 {
    36.                     currentSelected = 0;
    37.                     EventSystem.current.SetSelectedGameObject(SelectionArray[0], null);
    38.                 }
    39.                 else
    40.                 {
    41.                     EventSystem.current.SetSelectedGameObject(SelectionArray[++currentSelected], null);
    42.                 }
    43.         }
    44.         }
    45.     }
    46. }
    47.  
     
  13. tapticc

    tapticc

    Joined:
    Jan 16, 2014
    Posts:
    352
    cheers for sharing, slight issue where the tabbing over the last control gets an array out of bounds error but easily fixable, also if you manually click on say the middle of 5 items and then tab, the index of the selected item would still be the first item so the tabbing is out of sync but should be able to sync that by setting the value when changing active control.
     
  14. indieDoroid

    indieDoroid

    Joined:
    Jan 25, 2016
    Posts:
    130
    Much thanks to @passeride for the head start.

    Took his code and did an artist's hack on it. (Meaning coders will throw up if they see this code). It works for me and I don't get the error that @tapticc mentions above.

    For anyone that wants to use this. You'll need to create at least 2 buttons(UI) inside your Canvas. Have them lined up vertically. Then drop this script into the parent that holds these buttons. After that you can press up and down arrows to select the buttons.

    Hope it helps others. Sharing's caring~

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using UnityEngine.EventSystems;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6.  
    7. public class MainMenuGUIScript : MonoBehaviour {
    8.  
    9.     public GameObject[] SelectionArray;
    10.     int currentSelected = 0;
    11.  
    12.     void Start () {
    13.         EventSystem.current.SetSelectedGameObject(SelectionArray[currentSelected], null);
    14.     }
    15.  
    16.     void Update () {
    17.         Debug.Log ("CurrentSelect " + currentSelected + "Array is " + SelectionArray[currentSelected]);
    18.  
    19.         if (Input.GetKeyDown (KeyCode.UpArrow)) {
    20.             currentSelected--;
    21.  
    22.             if (currentSelected < 0 )
    23.             {
    24.                 currentSelected = 0;
    25.             }
    26.             EventSystem.current.SetSelectedGameObject (SelectionArray [currentSelected], null);
    27.         }
    28.  
    29.         if (Input.GetKeyDown (KeyCode.DownArrow)) {
    30.             currentSelected++;
    31.  
    32.             if (currentSelected >= SelectionArray.Length - 1 )
    33.             {
    34.                 currentSelected = SelectionArray.Length - 1;
    35.             }
    36.             EventSystem.current.SetSelectedGameObject (SelectionArray [currentSelected], null);
    37.         }
    38.     }
    39. }
    40.  
     
    Last edited: May 28, 2016
  15. Xonatron

    Xonatron

    Joined:
    Jan 14, 2013
    Posts:
    23
    Heads up you do not need a script to do this. Just do it directly in the OnClick() event of the button that loads the new menu. In the fields, choose EventSystem to access its methods. Choose the SetSelectedGameObject() method. Pass in the menu button in question. This is a more elegant solution.
     
    mgstauff likes this.