pygame_gui icon indicating copy to clipboard operation
pygame_gui copied to clipboard

A question on UIDropDownMenu/separating entry strings from entry IDs

Open Toma400 opened this issue 9 months ago • 4 comments

I'm coming with issue/question heavily inspired by this SO question I made a while ago for another lib but for which I don't see as straightforward solution as PySimpleGUI author suggested.

In short, I want to showcase, let's say for this example, 5 options in UIDropDownMenu. For brighter example, let's make it fantasy race picker:

  • Human
  • Elven
  • Dwarven
  • Orcish
  • Hobbitean

The issue is, I can pass list of strings being those, but I can't pass objects. Why does that matter?
Because, if I would like to use translateable strings, I can't use anything that give me flexibility - for example, class like this:

class Gender:

    def __init__(self, key: str, mod_id: str):
        self.key    : str = key     # translation key
        self.mod_id : str = mod_id  # mod ID

    def __repr__(self) -> str:
        return langjstring(key=self.key, modtype="stats", modid=self.mod_id)

Where I can use language key and object to represent unique entry, but its string representation returns currently selected language translation.
This way I can compare if currently selected entry is exactly matching, without "reverse translating" (which can get buggy and verbose) or showing only key (which is not really what anyone wants).

I hope it's only me missing some reads in documentation, but if it's a lack of feature in PyGame_GUI, it'd be lovely to get this introduced soon (probably to more than only UIDropDownMenu, anything working on entries), as for now it's the only issue I've found in this awesome library.

Toma400 avatar Sep 17 '23 21:09 Toma400

Additionally I'd say that even if OOP-friendly approach isn't planned, it'd be nice to at least have an option to get index of list[str] passed for .selected_option. Because retrieving str as .selected_option return is very bug-prone, in case we have list with repeated entries.

Having index would allow us to make workarounds like this (pseudocode):

oop_list: list[Gender] = [Male, Female, Else]
str_list: list[str] = ["m", "f", "m"]

ddmenu = UIDropDownMenu(options_list=str_list, relative_rect=pygame.Rect((400, 100), (400, 50)), starting_option=str_list[2], manager=manager)

# let's imagine we want to get Else which has `"m"` string representation on UIDropDownMenu
# getting str (current, bug-prone solution)
print ddmenu.selected_option == "m" # returns string
print str_list.index("m") # returns 0 or 2, either way not specific position
# getting index (workaround)
print ddmenu.selected_option_index == 2 # returns index int
print str_list[2] # gets specifically "m" on position we need
print oop_list[2] # allows for getting object on the same position, if our lists are exactly same

# ideally, objects list could be used and UIDropDownMenu would retrieve __repr__ method
# and you could compare objects instead of representation strings, but index would also allow for at least some flexibility

Toma400 avatar Sep 18 '23 10:09 Toma400

We actually have support for IDs separate from the visible strings in the underlying UISelectionList type but then that is not used in the UIDropDown. So that is my bad.

These elements developed in a backwards way in that I built the drop down before the UISelectionList and then realised that one was really a sub set of the other.

If you look at line 202 of ui_drop_down.py where we pass in the item_list parameter:

https://github.com/MyreMylar/pygame_gui/blob/7f26d3b44aee4c655869e232d86677889b3b7ab7/pygame_gui/elements/ui_drop_down_menu.py#L202-L211

That item_list needs to optionally be a list of tuples of two strings at that point instead of always just a list of strings. There are a few other things that'd need to change following that, like adjusting the parameters on the event to give the selected items object ID as well as just the overall drop down's id (probably a new param). But the main thing would just be passing in and keeping the IDs in the selection list and the drop down in the first place.

Basically each option in a drop down should be able to be ("Visible string", "#object_id_for_events_and_or_theming") as is possible with the UISelectionList but the code needs a bit of tweaking to make that possible.

MyreMylar avatar Sep 18 '23 17:09 MyreMylar

I see, unfortunately UISelectionList .get_single_selection is still returning this Visible string element instead of ID.. is there a way to get the ID instead? Because this is literally the only thing keeping me away from using it right now (I'm trying to keep PyGameGUI dependency as minimal as possible, so I will try to not use events at all).

I know it's asking for much, but if there was a way for me to get the latter part in any way, I'd be so damn thankful. I lost over a year trying to make my own GUI for the game, and from what I've saw other PyGame GUI libs, they were much more intrusive towards OG code.. so this lib is literally my only saviour for game development right now.

Toma400 avatar Sep 19 '23 11:09 Toma400

Okay, so for anyone crossing this issue by, I made small workaround:

def getCurrentID(slist: UISelectionList) -> str or None:
    """Retrieves ID from UISelectionList selected element"""
    for item in slist.item_list:
        if item['selected']:
            return item["object_id"]
    return None

Feel free to use it, and thanks @MyreMylar for showing UISelectionList as an alternative <3

Toma400 avatar Sep 20 '23 12:09 Toma400