[GUI] dynamic widget definition

Discussion of Lua and LuaWML support, development, and ideas.

Moderator: Forum Moderators

Post Reply
white_haired_uncle
Posts: 1231
Joined: August 26th, 2018, 11:46 pm
Location: A country place, far outside the Wire

[GUI] dynamic widget definition

Post by white_haired_uncle »

I'm building a preferences menu. Preferences can take multiple forms, such as the simple bool for which I am using a checkbox/button. I use a table to hold preferences, along with things like their default value, range, etc. These will be displayed in a tree_view, with the node type depending on new_prefs.type (e.g. bool == button).

Code: Select all

        table.insert(new_prefs,{ id = "northfrost_animation", default = true , type = "bool", value = true,
                description = _"Display animation for northfrost aura"})
        table.insert(new_prefs,{ id = "ai_aggression", default = 1, menu = "ai", type = "float", value = 1, min=-10, max = 2, step = 0.1,
                        description = _"aggression"})


Now for the float, I'd like to use a slider, and that slider's minimum and maximum values and step size aren't known when I create the node definition, and it appears they cannot be modified on the fly. My guess is that they are fixed, and I have to define them when I define(/instantiate/initialize/?) the widget.

If I am correct, then my question is how do I create/define/whatever a widget in a tree_view "at runtime"?

My first thought was gui.add_widget_definition(), then something using add_item_of_type, but these ideas aren't really going anywhere for me. If you're wondering what in the world made me think those might be the answer, they're the only things I know that create stuff dynamically.

I hope this make some sort of sense. At least, what I'm trying to do.

I could just use a text_box of course, but I've built myself a puzzle I don't know how to solve with the slider idea (and it just seems like a nice way of constraining input to a "valid" range).


This was my original attempt:

Code: Select all

       local prefs_tv = T.tree_view { id = "prefs_tv",
                T.node { id = "bool_node",
                        T.node_definition {
                                T.row {
                                        T.column {
                                                grow_factor = 0,
                                                horizontal_alignment = "left",
                                                T.toggle_button { id = "prefs_tv_button",
                                                        definition = "default"
                                                }
                                        },
                                        T.column {
                                                grow_factor = 0,
                                                horizontal_alignment = "left",
                                                T.label { id = "prefs_tv_label",
                                                }
                                        }, 
                                        T.column { grow_factor = 1, T.spacer {} }
                                }
                        }
                },
                T.node { id = "float_node",
                        T.node_definition {
                                T.row {
                                        -- probably need a spacer/blank image here
                                        T.column {
                                                grow_factor = 0,
                                                horizontal_alignment = "left",
                                                T.label { id = "prefs_tv_label",
                                                }
                                        }, 
                                        T.column {
                                                grow_factor = 0,
                                                horizontal_alignment = "left",
                                                T.slider { id = "prefs_tv_slider",
                                                       definition = "default"
                                                }
                                        },
                                        T.column { grow_factor = 1, T.spacer {} }
                                }
                        }
                }
        }
 ...
                                elseif pref.type == "float" then
                                local item = dialog.prefs_tv:add_item_of_type("float_node")
                                item.prefs_tv_label.label = pref.description
                                item.prefs_tv_slider.minimum_value = pref.min     -- Line 134
                                --item.prefs_tv_slider.maximum_value = pref.max
                                item.prefs_tv_slider.value = pref.value
                                --item.prefs_tv_slider.step_size = pref.step
                        end

       prefs.lua:134: bad argument #2 to 'newindex' (invalid modifiable property of widget) 
        
Speak softly, and carry Doombringer.
User avatar
Celtic_Minstrel
Developer
Posts: 2251
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: [GUI] dynamic widget definition

Post by Celtic_Minstrel »

white_haired_uncle wrote: April 25th, 2024, 1:58 am Now for the float, I'd like to use a slider, and that slider's minimum and maximum values and step size aren't known when I create the node definition, and it appears they cannot be modified on the fly. My guess is that they are fixed, and I have to define them when I define(/instantiate/initialize/?) the widget.
Looks like it's just missing from the documentation – you can assign min_value and max_value to change a slider's bounds. Unfortunately, it looks like step size is missing not only from the documentation but from the implementation itself – it's not that it's immutable, but we forgot to add a way to change it from Lua.
white_haired_uncle wrote: April 25th, 2024, 1:58 am My first thought was gui.add_widget_definition(), then something using add_item_of_type, but these ideas aren't really going anywhere for me. If you're wondering what in the world made me think those might be the answer, they're the only things I know that create stuff dynamically.
Widget definition works for purely cosmetic changes or for auxiliary behaviour changes. By "auxiliary" I mean aside from the primary purpose of the widget, so for example if you wanted your slider to have a button attached that when clicked sets the slider to the default value (and you needed enough slides like that that it'd be a pain to manually add a button beside each one), that could (I think) be done with a custom definition.

Add item of type doesn't dynamically generate entirely new content, as it must use a template defined in the WML to generate the element from (that's the "type" of the item you're adding). So it would work if you can break the sliders down into a small number categories, for example "slider with a step size of 1", and "slider with a step size of 5", and "slider with a step size of 10" – each of those would be defined as a different type in the WML. But if each slider needs its own unique step size, then I don't think this approach will help you.
white_haired_uncle wrote: April 25th, 2024, 1:58 am I could just use a text_box of course, but I've built myself a puzzle I don't know how to solve with the slider idea (and it just seems like a nice way of constraining input to a "valid" range).
If you do like the idea of a text box, you could try out the new [spinner] widget. It's not documented yet, but it's basically a text box for numeric input. Though it doesn't currently support any kind of limited range (that's probably worth a feature request). And, glancing at the code, it seems it (unfortunately) probably doesn't even support read/write of value… I hope I'm wrong but I guess it's useless in Lua if I'm right.
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
white_haired_uncle
Posts: 1231
Joined: August 26th, 2018, 11:46 pm
Location: A country place, far outside the Wire

Re: [GUI] dynamic widget definition

Post by white_haired_uncle »

Thank you. I'll try the min_value approach. Having to configure multiple TV nodes for each step size is unfortunate, but not that big a deal, I was already considering having a few pre-defined sliders if necessary.

As far as the spinner, I did try it out a while back. I ended up getting it to work, but I had to do something like this:

dialog.gold_sp._text.text = tostring(dialog.gold_sl.value)

but I don't even remember how I came up with that. I'm pretty sure it's not the way it's supposed to work, so I just set it aside to revisit later.
Speak softly, and carry Doombringer.
User avatar
Celtic_Minstrel
Developer
Posts: 2251
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: [GUI] dynamic widget definition

Post by Celtic_Minstrel »

Sounds like what you're doing is digging into the spinner's internals (it's a compound widget, which means it's composed of several smaller widges). That's definitely not the way it's supposed to work, but it's good to know that there's at least a workaround for the fact that it's not properly supported yet.
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
white_haired_uncle
Posts: 1231
Joined: August 26th, 2018, 11:46 pm
Location: A country place, far outside the Wire

Re: [GUI] dynamic widget definition

Post by white_haired_uncle »

Yes, now that you mention it I remember that. I looked in data/gui/widget/spinner_default.cfg, saw that the text_box had id = "_text", and figured I could use that to get to the attribute I wanted. But it just looked wrong, like something that might "work" now but only by luck and decided to avoid documenting it.
Speak softly, and carry Doombringer.
Post Reply