OptionButton text display alignment with index value

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

I have a large series of OptionButtons. I can load in a saved state which sets the displayed text values on the option buttons. However, after the load, if the user presses on the OptionButton to change the selection, the list that displays always shows the default ‘0’ position as the one being selected, regardless of the text value displayed. Is there an easy way to align the selected index value of the OptionButton with the displayed text of that option? Thank you

I haven’t tested it, but I assume simply setting the selected property of each button appropriately (from your saved state) should do what you want. Have you tried that?

jgodfrey | 2022-12-09 18:00

The saved state just saves the text value of the OptionButton, not the index position. The reason for this being the contents of the OptionButtons can shift, so the index numbers can also change. The selected property requires an index number.

item_selected ( int index )

Emitted when the current item has been changed by the user. The index of the item selected is passed as argument.

grymjack | 2022-12-09 19:55

Looking at the OptionButton docs, I see a few things that, when used together, should get you what you need:

  • You can specify an id for each item as it’s added (add_item())
  • You can get an item’s index from its id (get_item_index())
  • You can set the selected item via its index (select())

Armed with that, I’d do something like this:

  • As items are added to the OptionButton, assign (and remember) a unique id.

  • When you save your state, record the id as appropriate

  • When you restore your save state, select the previously selected item via get_item_index() and select() - based on the stored id

jgodfrey | 2022-12-09 20:04

Hmmmmm…I am terrible at explaining these things. There are two steps involved. First, when the screen is first opened. That’s when the OptionButtons are first loaded. The player can then chose (not required) to ‘Load’ a previously saved design rather that work on a new one.

The OptionButtons are fed from a SQLite database. As the player advances in the game, they will have access to more and more entries that will appear in the different OptionButton lists. Below is example code where I use ‘additem’ to populate the OptionButton control (1 of 27). Are you saying that when I do the initial ‘additem’ that populates the OptionButtons, I should track the ‘index’ created at that time and then cross reference if they chose to ‘Load’ a saved design?

# load other vessel components
sql_string = "SELECT * FROM componentsvessel order by componenttitle ASC;"
result = funct.executesqlstring(sql_string)
for row in range(0, result.size()):
	# testing for component that should be displayed here
	if (component_display_test(result[row]['componenttitle']) == true):
		# testing for owner technology for this component to be on the list
		if (funct.test_player_technology(owner_number, result[row]['componentcode']) == true):
			component1_option_button.add_item(result[row]['componenttitle'])

grymjack | 2022-12-09 22:55

The only way I have been able to think of is after loading the ‘Saved’ design to step through all of the OptionButtons and find the entry that corresponds to the text value and then ‘select’ that entry based on its ‘index’. That way the selected entry will correspond with the text value of the control. I appreciate you sticking with me here. :slight_smile:

grymjack | 2022-12-09 23:02

First, I haven’t tried any of this… :slight_smile: My suggestions are only from a quick look at the docs.

  • To select an item programmatically (which is what you ultimately need to do here I think…), you need it’s INDEX (select(index)).
  • You’ve said that the indices can change, so that’s not a stable mechanism to track a given item by.
  • You can specify a unique ID for each item when you add it (add_item("my item", 23)) for example.
  • You can get an item’s Index from its ID (get_item_index(id))

So, if you assign a unique ID to each item, and store that ID in your DB, you can can map it to that item’s INDEX in a given OptionButton, and from there, you can select that item via code.

However, maybe there’s an easier way for you to do this based on the item’s TEXT value - which is apparently what you already have stored… Here’s an example:

func _ready():
	$OptionButton.add_item("Item 1")
	$OptionButton.add_item("Item 2")
	$OptionButton.add_item("Item 3")
	$OptionButton.add_item("Item 4")
	$OptionButton.add_item("Item 5")
	$OptionButton.add_item("Item 6")

	var item_to_select = "Item 3"
	for i in $OptionButton.get_item_count():
		var text = $OptionButton.get_item_text(i)
		if text == item_to_select:
			$OptionButton.select(i)
			break

The above code just iterates through all items in a given OptionButton looking for one with a specific text string. Once that’s found, that item is selected via its index.

jgodfrey | 2022-12-09 23:33

Ah, I missed your above comment, but that’s exactly what the sample code is doing… :slight_smile:

jgodfrey | 2022-12-09 23:47

But, to be clear, my original thought (which still seems valid) is to assign (and store) a unique ID with each item. Then, you can get from that ID to the item’s INDEX, and using that INDEX, select it…

jgodfrey | 2022-12-09 23:49

Further, if you opt to do the text-matching thing, I’d relegate all of that busy work to a dedicated function. Something like select_optionbutton_item_from_text(optionbutton, item_text).

Then, pass in the OptionButton and text of the item of interest and let it do its thing.

jgodfrey | 2022-12-09 23:52

Sounds like a plan. Thanks for the help! If you want t make your code chunk a separate answer I will upvote it!

grymjack | 2022-12-10 01:56

:bust_in_silhouette: Reply From: jgodfrey

Based on the above discussions, here’s a working function:

func select_item_from_text(ob: OptionButton, item_text: String) -> void:
	for i in ob.get_item_count():
		if ob.get_item_text(i) == item_text:
			ob.select(i)
			break

Dang looks like I’m too late to the party.

After dabbling with OptionButtons for a long time learnt really early how to deal with them properly.

Using Signals pressed to populate and disable items and item_selected to relocate and save changes.

for the above you pretty much nailed it however its best to set the item during or after the loop of adding the items as op said before indexes can change.

for row in range(0, result.size()):
    # testing for component that should be displayed here
    if (component_display_test(result[row]['componenttitle']) == true):
        # testing for owner technology for this component to be on the list
        if (funct.test_player_technology(owner_number, result[row]['componentcode']) == true):
            component1_option_button.add_item(result[row]['componenttitle'])
            var index = component1_option_button.get_item_count() -1
            if component1_option_button.get_text() == component1_option_button.get_item_text(index):
                component1_option_button.select(index)

Wakatta | 2022-12-10 14:03

On a somewhat unrelated question: if you have a large list of items in the OptionButton, the popup window is bulky and hard to move around within. Is there a good way to replace the default popup on the OptionButton with one more like the classic smaller one with a scrollbar?

grymjack | 2022-12-10 20:50

If you are interested, here is the function as written in my code, works perfectly. Sorry for all the crappy line wrapping. Thanks for your ideas!

func option_button_reset():
	# gathering list of option buttons
	var option_list = []
	option_list.append("DesignedComponents/Shield/DefensiveShieldOptionButton")
	option_list.append("DesignedComponents/EnergyWeapon/EnergyWeaponOptionButton")
	option_list.append("DesignedComponents/ProjectileWeapon/ProjectileWeaponOptionButton")
	option_list.append("MovementOptions/TacticalEngineOptionButton")
	option_list.append("MovementOptions/InertialDamperOptionButton")
	option_list.append("MovementOptions/TranslightEngineOptionButton")
	option_list.append("MovementOptions/PowerGeneratorOptionButton")
	
	# loop to gather 'other' component option buttons
	for loop in range(1, 20):
		option_list.append("OtherComponentCosts/Component" + str(loop) +
			"Costs/Component" + str(loop) + "OptionButton")

	# loop to set the selected values to text values
	for loop in range(0, option_list.size()):
		var node_name = option_list[loop]
		var temp_node = get_node(node_name)
		var node_text = temp_node.text

		# looping through controls to find value that matches text
		for option_count in range(0, temp_node.get_item_count()):
			if (temp_node.get_item_text(option_count) == node_text):
				temp_node.select(option_count)
				break

grymjack | 2022-12-10 22:33