How do I control which GUI item has input focus?

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?

2 Likes

After you show the new Panel you can call: SetSelectedGameObject on the EventSystem with the desired button.

EventSystemManager.currentSystem.SetSelectedGameObject (theButton);
5 Likes

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.

That works. I just pass null as the second parameter.

The script looks like this

using UnityEngine;
using UnityEngine.EventSystems;

public class OpenScreen : MonoBehaviour
{
     public GameObject defaultButton;

     public void OnOpen()
     {
          if (defaultButton != null)
          {
                EventSystemManager.currentSystem.SetSelectedGameObject(defaultButton, null);
          }
     }

}

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.

1 Like

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

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?

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

1 Like

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.

This should do what you want :slight_smile:

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?

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):

using UnityEngine.EventSystems;

EventSystem.current.SetSelectedGameObject(yourObject);
7 Likes

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!

1 Like

Hey

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

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

public class MainMenuGUIScript : MonoBehaviour {

    public List<GameObject> SelectionArray = new List<GameObject>();
    int currentSelected = 0;

    // Use this for initialization
    void Start () {
        EventSystem.current.SetSelectedGameObject(SelectionArray[0], null);
    }
   
    // Update is called once per frame
    void Update () {
        if (Input.GetKeyDown(KeyCode.Tab)) {
            if(Input.GetKey(KeyCode.LeftShift))
            {
                // Cycle selection Array
                if (currentSelected - 1 < 0)
                {
                    currentSelected = SelectionArray.Count - 1;
                    EventSystem.current.SetSelectedGameObject(SelectionArray[currentSelected], null);
                }
                else
                {
                    EventSystem.current.SetSelectedGameObject(SelectionArray[--currentSelected], null);
                }
            }else{
                // Cycle selection Array
                if (currentSelected + 1 > SelectionArray.Count)
                {
                    currentSelected = 0;
                    EventSystem.current.SetSelectedGameObject(SelectionArray[0], null);
                }
                else
                {
                    EventSystem.current.SetSelectedGameObject(SelectionArray[++currentSelected], null);
                }
        }
        }
    }
}
1 Like

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.

1 Like

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~

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

public class MainMenuGUIScript : MonoBehaviour {

    public GameObject[] SelectionArray;
    int currentSelected = 0;

    void Start () {
        EventSystem.current.SetSelectedGameObject(SelectionArray[currentSelected], null);
    }

    void Update () {
        Debug.Log ("CurrentSelect " + currentSelected + "Array is " + SelectionArray[currentSelected]);

        if (Input.GetKeyDown (KeyCode.UpArrow)) {
            currentSelected--;

            if (currentSelected < 0 )
            {
                currentSelected = 0;
            }
            EventSystem.current.SetSelectedGameObject (SelectionArray [currentSelected], null);
        }

        if (Input.GetKeyDown (KeyCode.DownArrow)) {
            currentSelected++;

            if (currentSelected >= SelectionArray.Length - 1 )
            {
                currentSelected = SelectionArray.Length - 1;
            }
            EventSystem.current.SetSelectedGameObject (SelectionArray [currentSelected], null);
        }
    }
}

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.

2 Likes