How to avoid manual focus grabbing?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Diet Estus

I am building an inventory menu using a TabContainer as base with a bunch of different tab scenes and modular sub-scenes that I add to the tabs. The sub-scenes are basically just GridContainers into which I instance Buttons corresponding to my player’s items. The buttons are the only things with focus enabled, and the player can scroll through them by pressing the arrow keys (no mouse).

Here’s an example of my inventory structure:

> InventoryBase (`TabContainer`)
    >> WeaponsTab (`Container`)
        >>> EquippedWeapons (`GridContainer`)
        >>> AvailableWeapons(`GridContainer`)
        
    >> MagicTab (`Container`)
        >>> EquippedMagic (`GridContainer`)
        >>> AvailableMagic(`GridContainer`)

My question concerns setting focus.

Currently, I watch the the TabContainer's tab_changed signal, and when a tab is changed, I manually set the focus to the first Button in either the AvailableWeapons or AvailableMagic (depending on which tab I’m changing to). For this, I use grab_focus().

The problem is that it’s possible that the player only picked up one weapon and has already equipped it, so there is only one button on the inventory page, in EquippedWeapons rather than AvailableWeapons. So, nothing is focused.

I could write some code which handles this situation, but that doesn’t feel like the right solution to me.

In fact, I dislike how often I find myself needing to manually change focus depending on the situation. I feel like I write a lot of ad hoc code for grabbing focus.

I would like to just auto-focus to the first available Button in my tab (regardless of whether it’s in EquippedWeapons or AvailableWeapons).

So, my question: Is there a way to auto-focus on the first available control with focus enabled?

:bust_in_silhouette: Reply From: dmitriy_shmilo

You could create a function like this:

func focus_first_button(container: Node) -> bool:
	for child in container:
		if child is Button:
			child.grab_focus()
			return true
	return false

Or this, if you want to focus any control:

func focus_first_control(container: Node) -> bool:
	for child in container:
		if child is Control and child.focus_mode != FOCUS_NONE:
			child.grab_focus()
			return true
	return false

And then just call it whenever you switch to a different tab.