[solved] GUI: Embedded listboxes
Moderator: Forum Moderators
-
- Posts: 1256
- Joined: August 26th, 2018, 11:46 pm
- Location: A country place, far outside the Wire
[solved] GUI: Embedded listboxes
In this campaign, units can pick up custom items. I want to initially show a list of units which have said items, hopefully eventually adding support for acting on them (dropping an item for example). I have a table geared_units which contains units and for each unit a table of objects representing the gear they have collected.
The body of my dialog (hopefully I get these terms right) contains a listbox with one entry for each geared unit. The listbox is populated in preshow based on the unit contents of the geared_units table (if it makes a difference, it's done that way because that's how the example I had did it when I "learned" this stuff by copying the LotI guis).
Now my dilemna. I want those right hand columns to contain a list of items (discovered at runtime based on what that unit has collected, with each item potentially selectable in the future) instead of "List of items goes here".
My gut says that's another listbox, but I'm not sure if that's right, where I should define/populate it, or if it's even possible to put a listbox in a listbox. For starters, I tried just building a grid with one row for each item when I built the object table for each unit, which I think worked out fine, but I don't know how to insert them into each listbox entry.
I know I'm probably not wording my question well. I would if I could. I'm hoping someone will know much better than I do what I'm trying to accomplish and can kick me in the right direction. I've omitted my code at this point, as I worry that until I understand what I'm trying to do displaying what I've tried may just muddy the issue.
TIA
P.S. Yes, the right way to do this would be to use the LotI items menu. That's what I would do if it were up to me and I planned to go any further with this. At this point, I'm pretty much done messing with it, but I have a problem I wasn't able to solve and I've yet to mature to the point of being able to walk away defeated by a stupid gui.
P.P.S. I tried to force the sizing of the left hand column of each listbox entry so that they'd all be the same and things would line up nice. I failed. I don't really care enough to worry about layout details, but if it's an obvious fix I'd take it.
The body of my dialog (hopefully I get these terms right) contains a listbox with one entry for each geared unit. The listbox is populated in preshow based on the unit contents of the geared_units table (if it makes a difference, it's done that way because that's how the example I had did it when I "learned" this stuff by copying the LotI guis).
Now my dilemna. I want those right hand columns to contain a list of items (discovered at runtime based on what that unit has collected, with each item potentially selectable in the future) instead of "List of items goes here".
My gut says that's another listbox, but I'm not sure if that's right, where I should define/populate it, or if it's even possible to put a listbox in a listbox. For starters, I tried just building a grid with one row for each item when I built the object table for each unit, which I think worked out fine, but I don't know how to insert them into each listbox entry.
I know I'm probably not wording my question well. I would if I could. I'm hoping someone will know much better than I do what I'm trying to accomplish and can kick me in the right direction. I've omitted my code at this point, as I worry that until I understand what I'm trying to do displaying what I've tried may just muddy the issue.
TIA
P.S. Yes, the right way to do this would be to use the LotI items menu. That's what I would do if it were up to me and I planned to go any further with this. At this point, I'm pretty much done messing with it, but I have a problem I wasn't able to solve and I've yet to mature to the point of being able to walk away defeated by a stupid gui.
P.P.S. I tried to force the sizing of the left hand column of each listbox entry so that they'd all be the same and things would line up nice. I failed. I don't really care enough to worry about layout details, but if it's an obvious fix I'd take it.
Last edited by white_haired_uncle on December 28th, 2023, 5:56 am, edited 1 time in total.
Speak softly, and carry Doombringer.
- Celtic_Minstrel
- Developer
- Posts: 2260
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: GUI: Embedded listboxes (maybe?)
I don't think there's any reason why a listbox in a listbox won't work. I do think the game won't be able to lay it out properly, though, so you'd probably need some kind of layout trick to force the inner listbox to a fixed size. Have you tried using a nested listbox yet?
Another option might be to use a treeview, so the "nested listbox" becomes indented entries in the tree.
Another option might be to use a treeview, so the "nested listbox" becomes indented entries in the tree.
-
- Posts: 1256
- Joined: August 26th, 2018, 11:46 pm
- Location: A country place, far outside the Wire
Re: GUI: Embedded listboxes (maybe?)
I have not tried either. First I've heard of them. I'm not finding much of anything on the wiki, do you have any links/examples?Celtic_Minstrel wrote: ↑December 27th, 2023, 4:33 pm I don't think there's any reason why a listbox in a listbox won't work. I do think the game won't be able to lay it out properly, though, so you'd probably need some kind of layout trick to force the inner listbox to a fixed size. Have you tried using a nested listbox yet?
Another option might be to use a treeview, so the "nested listbox" becomes indented entries in the tree.
Maybe a GUI guide? Pretty much everything on wiki/devdocs seems to be reference aimed at an audience who has already read "the guide".
Thanks
P.S.
Assuming I wanted to try listbox in listbox...I create a reference to an image in my listbox item definition with something like
Code: Select all
wml.tag.row {
wml.tag.column {
wml.tag.image { id = "unitimage" }
}
}
Code: Select all
listbox[index].unitimage.label = gunit.unit.__cfg.image
Speak softly, and carry Doombringer.
- Celtic_Minstrel
- Developer
- Posts: 2260
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: GUI: Embedded listboxes (maybe?)
Everything is valid in theory. Of course, certain tags only fit in certain places.white_haired_uncle wrote: ↑December 27th, 2023, 6:10 pm (I can't even find a list of what "wml.tag.*" are valid)?
I don't understand what's confusing about this? Just replace the "image" with a "listbox"? Your path to assign something would have two listboxes in it, something like:white_haired_uncle wrote: ↑December 27th, 2023, 6:10 pm Assuming I wanted to try listbox in listbox...I create a reference to an image in my listbox item definition with something like
and then populate it withCode: Select all
wml.tag.row { wml.tag.column { wml.tag.image { id = "unitimage" } } }
But I haven't figured out what that would look like if I wanted to insert another listbox (or grid, etc).Code: Select all
listbox[index].unitimage.label = gunit.unit.__cfg.image
Code: Select all
listbox[index].sub_listbox[index2].label
It's true that the GUI2 documentation situation is pretty bad. I'm not sure what's a good way to deal with this. The devdocs is the authoritative reference, certainly, but I think it's not very discoverable, and there's not much in the way of a general overview or a tutorial.white_haired_uncle wrote: ↑December 27th, 2023, 6:10 pm Maybe a GUI guide? Pretty much everything on wiki/devdocs seems to be reference aimed at an audience who has already read "the guide".
Encoding your dialog as WML instead of Lua does mean you'd be able to run it through the schema validator, but that's only helpful if you already roughly know the structure of a dialog.
-
- Posts: 1256
- Joined: August 26th, 2018, 11:46 pm
- Location: A country place, far outside the Wire
Re: GUI: Embedded listboxes (maybe?)
I tried replacing image with listbox, but got "bad argument #2 to 'newindex' (invalid modifiable property of widget)" when I first tried. Then I did actually have the idea that I needed to use that "array in array" syntax and made a note to try and investigate that, but I had reached my limit of learning by making things up and hoping and stopped to ask for help at that point. Sounds like I may have been onto something, and now that you've shown me this I'll continue on with a bit of confidence (while searching for more on the treeview/nested concepts).Celtic_Minstrel wrote: ↑December 27th, 2023, 6:16 pm I don't understand what's confusing about this? Just replace the "image" with a "listbox"? Your path to assign something would have two listboxes in it, something like:Code: Select all
listbox[index].sub_listbox[index2].label
Thanks
Speak softly, and carry Doombringer.
-
- Posts: 1256
- Joined: August 26th, 2018, 11:46 pm
- Location: A country place, far outside the Wire
Re: GUI: Embedded listboxes (maybe?)
So I took what was working, a dialog with a populated list box, and drew it out on paper to show how the pieces fit together to create a listbox inside a dialog. Then I drew in another listbox ("items_listbox") as a member of the first and tried to implement it as just another layer. Of course, it didn't actually work (bad argument #2 to 'index' (invalid property of widget), coming from the line "local items_listbox = " in preshow() ).
I think (well, hope) my issue stems from the fact that I really don't understand what the first line in preshow ( local listbox = dialog[listbox_id] ) is doing, that's something I just copied along with most of the rest of the code but never needed to understand. I tried to mimic it based on where my new listbox resides within the rest of the GUI, but it seems I guessed wrong. Pretty sure it's never going to work if I can't initiate that items_listbox, even if the rest is okay.
I think (well, hope) my issue stems from the fact that I really don't understand what the first line in preshow ( local listbox = dialog[listbox_id] ) is doing, that's something I just copied along with most of the rest of the code but never needed to understand. I tried to mimic it based on where my new listbox resides within the rest of the GUI, but it seems I guessed wrong. Pretty sure it's never going to work if I can't initiate that items_listbox, even if the rest is okay.
Code: Select all
function wesnoth.wml_actions.show_geared_units()
-- GUI code (except mistakes) pretty much lifted from LotI
-- for now, we'll just look at units on map (not recall), and only for side 1
local units=wesnoth.units.find_on_map{side=1}
if #units<1 then wml.error("[show_geared_units]: No units found on map for side 1!") end
local geared_units = {} -- a geared_unit is a unit, and a list of that unit's gear (TDM objects)
-- for example geared_units[3].unit.name = "Adon" (the 3rd unit in the table has the name Adon)
-- geared_units[3].gear[1].name = "The Holy Grail" (the 1st object held by the 3rd unit is The Holy Grail)
for _, unit in pairs(units) do
-- wesnoth.interface.add_chat_message(string.format("Looking at %s, a.k.a. %s", unit.id, unit.name))
local gear = {} -- a list of TDM objects held by this unit
local modifications = wml.get_child(unit.__cfg, "modifications")
for object in wml.child_range(modifications, "object") do
if object.can_be_dropped_by ~= nil then -- can_be_dropped_by is an attribute of objects specific to TDM
table.insert(gear,object)
end
end
if next(gear) ~= nil then -- gear is not empty
table.insert(geared_units, { unit = unit, gear = gear })
end
end
-- the inner listbox filled with a unit's goodies
local items_listbox_id = "items"
-- an entry in the items listbox
local items_listbox_template = wml.tag.grid {
wml.tag.row {
wml.tag.column {
wml.tag.image { id = "itemimage" }
},
wml.tag.column {
wml.tag.label { id = "itemname" }
}
}
}
-- the box to put the list of items (per unit) into
local items_listboxDefinition = wml.tag.listbox { id = items_listbox_id,
wml.tag.list_definition {
wml.tag.row {
wml.tag.column {
wml.tag.toggle_panel { items_listbox_template }
}
}
}
}
-- Our GUI will show a list of units that have gear
local listbox_id = "geared_units"
-- a listbox_template defines the information about a single geared unit
-- it's one entry in a list
local listbox_template = wml.tag.grid {
wml.tag.row {
wml.tag.column {
wml.tag.grid {
wml.tag.row {
wml.tag.column {
wml.tag.label {
use_markup = true,
id = "unitname" -- we'll use this to identify this field when we add the actual data
}
}
},
wml.tag.row {
wml.tag.column {
wml.tag.image { id = "unitimage" }
}
},
wml.tag.row {
wml.tag.column {
wml.tag.label { id = "unittype" }
}
}
}
},
--wml.tag.column { wml.tag.listbox { id = "unititems"} }
wml.tag.column { items_listboxDefinition }
}
}
-- listboxDefinition defines a box which will contain the list of our geared units (a list of listbox_template's)
local listboxDefinition = wml.tag.listbox { id = listbox_id,
wml.tag.list_definition {
wml.tag.row {
wml.tag.column {
wml.tag.toggle_panel { listbox_template }
}
}
}
}
-- Top level GUI grid, a header, a box containing geared units, and an Ok button
local dialogDefinition = {
wml.tag.tooltip { id = "tooltip_large" },
wml.tag.helptip { id = "tooltip_large" },
wml.tag.grid {
wml.tag.row { -- Header
wml.tag.column {
border = "bottom",
border_size = 10,
wml.tag.label {
use_markup = true,
label = "<span size='large' weight='bold'>" .. _"Geared Units" .. "</span>"
}
}
},
wml.tag.row { -- Our listbox, not yet populated, just described here
wml.tag.column { listboxDefinition }
},
wml.tag.row { -- Ok button
wml.tag.column {
wml.tag.button { id = "ok", label = _"OK" },
}
}
}
}
-- A function to populate our listbox with our listbox_templates
local function preshow(dialog)
local listbox = dialog[listbox_id]
for index, gunit in ipairs(geared_units) do
listbox[index].unitname.label = "<span color='yellow' weight='bold'>" .. gunit.unit.name .. "</span>"
listbox[index].unitimage.label = gunit.unit.__cfg.image
listbox[index].unittype.label = gunit.unit.__cfg.language_name
-- Something must go here to initiate items_listbox, BUT WHAT???
-- local items_listbox = listbox[items_listbox_id] -- Seems (to me!) analogous to creation of listbox, only wrong
for itemindex, object in ipairs(gunit.gear) do
listbox[index].items_listbox[itemindex].itemimage.label=object.image
listbox[index].items_listbox[itemindex].itemname.label=object.name
end
-- listbox[index].unititems.label = "List of items goes here"
end
end
-- Draw the GUI
gui.show_dialog(dialogDefinition,preshow)
end
Speak softly, and carry Doombringer.
Re: GUI: Embedded listboxes (maybe?)
In lua
a.b
is always the same as a["b"]
so the following codes are equivalent:
Code: Select all
a.b.c = 5
Code: Select all
a["b"].c = 5
Code: Select all
local var = "b"
a[var].c = 5
Code: Select all
local var = "b"
a[var]["c"] = 5
Code: Select all
local var = "b"
local b = a[var]
b["c"] = 5
Scenario with Robots SP scenario (1.11/1.12), allows you to build your units with components, PYR No preperation turn 1.12 mp-mod that allows you to select your units immideately after the game begins.
-
- Posts: 1256
- Joined: August 26th, 2018, 11:46 pm
- Location: A country place, far outside the Wire
Re: GUI: Embedded listboxes (maybe?)
Progress!
It struck me that items_listbox should be a "child" of each listbox "entry" (not sure of the right terms), not of the (outer) listbox itself.
The formatting is terrible, but I'm getting the info I want in the area I want it.
It struck me that items_listbox should be a "child" of each listbox "entry" (not sure of the right terms), not of the (outer) listbox itself.
Code: Select all
-- A function to populate our listbox with our listbox_templates
local function preshow(dialog)
local listbox = dialog[listbox_id]
for index, gunit in ipairs(geared_units) do
listbox[index].unitname.label = "<span color='yellow' weight='bold'>" .. gunit.unit.name .. "</span>"
listbox[index].unitimage.label = gunit.unit.__cfg.image
listbox[index].unittype.label = gunit.unit.__cfg.language_name
for itemindex, object in ipairs(gunit.gear) do
local items_listbox = listbox[index][items_listbox_id]
items_listbox[itemindex].itemimage.label=object.image
items_listbox[itemindex].itemname.label=object.name
end
end
end
Speak softly, and carry Doombringer.
- Celtic_Minstrel
- Developer
- Posts: 2260
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: GUI: Embedded listboxes (maybe?)
You should use linked groups to keep the list box entries all the same size. It probably makes sense to use two linked groups, one for the inner listbox and one for the outer one.
-
- Posts: 1256
- Joined: August 26th, 2018, 11:46 pm
- Location: A country place, far outside the Wire
Re: GUI: Embedded listboxes (maybe?)
Nice. I saw linked_group in LuaAPI/gui/example, but wasn't sure what that did. Couldn't find any docs, but a bit of searching led me to data/campaigns/World_Conquest/gui/help_dialog.cfg which served as enough of an example.
Thanks for all your help with this.
Thanks for all your help with this.
Speak softly, and carry Doombringer.
- Celtic_Minstrel
- Developer
- Posts: 2260
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: [solved] GUI: Embedded listboxes
You can probably make it a bit better by setting
border
in some of your grid cells. Despite the name, "border" in GUI2 is actually like margins or padding, so you would be able to add a bit of space between the inner and outer listboxes on the right edge.-
- Posts: 1256
- Joined: August 26th, 2018, 11:46 pm
- Location: A country place, far outside the Wire
Re: GUI: Embedded listboxes (maybe?)
That thought did cross my mind. I really don't care for that notation (square brackets are for array indices, periods for structure members, IMO), so I tried changing dialog[listbox_id] to dialog.listbox_id which seems (seemed) sensible. That I could accept.
It didn't work. Probably because it's wrong. dialog["listbox_id"] == dialog.listbox_id, I see that now, but I have no idea what if anything is equivalent to dialog[listbox_id] using "dot-notation".
I did set the border/border_size in at least one place, but it looks like there's more to do which I'll leave for the actual campaign author if he chooses to use this (this whole exercise was really about helping someone else get up to speed with a basic lua gui, I just ran into a problem I couldn't solve with the embedded listboxes and couldn't leave it alone). I wanted to get rid of those highlighted border box things completely. In the process, I ran into border_thickness and border_color, but I couldn't get them to do anything. "has_minimum = false" helped initially, though I still get a box around my data if I click on it. Looking for a way I could make the items non-selectable, I tried ".enabled = false", and something using "on_click" to try and turn it back off if it got turned on, no dice. Curiously, when I tried using panel instead of toggle_panel I got an error that only buttons and panels are allowed in a listbox grid (or something close to that).Celtic_Minstrel wrote: ↑December 28th, 2023, 7:49 am You can probably make it a bit better by settingborder
in some of your grid cells. Despite the name, "border" in GUI2 is actually like margins or padding, so you would be able to add a bit of space between the inner and outer listboxes on the right edge.
Speak softly, and carry Doombringer.
Re: GUI: Embedded listboxes (maybe?)
In general, there is no way to writewhite_haired_uncle wrote: ↑December 30th, 2023, 11:13 am It didn't work. Probably because it's wrong. dialog["listbox_id"] == dialog.listbox_id, I see that now, but I have no idea what if anything is equivalent to dialog[listbox_id] using "dot-notation".
dialog[listbox_id]
using dot-notation. But in your code, you could just write it as dialog.geared_units
. (Because listbox_id
is always "geared_units"
.)- Celtic_Minstrel
- Developer
- Posts: 2260
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: [solved] GUI: Embedded listboxes
There is no way to make listbox items unselectable. Setting
has_minimum=false
just means that it supports having no item selected, but the items themselves are still selectable. If making them unselectable is that important, you could try using a tree view instead. In practice I think the code using a tree view is very similar to a listbox.