GUI: getting started with tree_view

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

Moderator: Forum Moderators

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

GUI: getting started with tree_view

Post by white_haired_uncle »

I'm trying to figure out how tree_view works. The only thing I can find to work from is the MP campaign World Conquest, in particular I'm looking at the help menu because it's easy to find. To keep things simple, I want to strip out the column that has menus and submenus, and just make a very, very simple copy of the Items ('Artifacts') tree view. Then once I get something on the screen to work with I can work up from there. Eventually, I plan to document the process from the perspective of a beginner.
wc-help.png
I'll admit, I haven't figured out yet what a page_definition is, or a node, or... I'm hoping once I get started those things will start to make sense. All I've found so far is that a tree_view must have one or more nodes.

So, copying as little as possible from WC and replacing the identifiers I don't know how to populate yet with some static data (hoping that's okay) I have this:

Code: Select all

function wesnoth.wml_actions.current_test_gui()
        local hardcoded_data = wml.tag.grid {
                wml.tag.row {
                        wml.tag.column {
                                wml.tag.image {
                                        label = "units/monsters/yeti.png"
                                }
                        },
                        wml.tag.column {
                                wml.tag.label {
                                        label = "A yeti"
                                }
                        }
                }
        }

        local tree_view = wml.tag.tree_view {
                id = "monsters_tv",
                wml.tag.node {
                        id = "monsters_node",
                        wml.tag.node_definition {
                                wml.tag.row {
                                        wml.tag.column {
                                                hardcoded_data
                                        }
                                }
                        }
                }
        }

        local multipage = wml.tag.multi_page {
                id = "monsters_mp",
                wml.tag.page_definition {
                        id = "monsters_pdef",
                        wml.tag.row {
                                wml.tag.column {
                                        wml.tag.scrollbar_panel {
                                                wml.tag.definition {
                                                        wml.tag.row {
                                                                wml.tag.column {
                                                                        tree_view
                                                                }
                                                        }
                                                }
                                        }
                                }
                        }
                }
        }

        
       local dialogDefinition = {
                wml.tag.tooltip { id = "tooltip_large" },
                wml.tag.helptip { id = "helptip_large" },
                wml.tag.grid {
                        wml.tag.row {  -- A header 
                                wml.tag.column {
                                        border = "bottom",
                                        border_size = 10,
                                        wml.tag.label {
                                                use_markup = true,
                                                label = "<span size='large'>" .. _"Here there be " ..
                                                        "<span color='yellow'>" ..
                                                        _"MONSTERS!" .. "</span></span>"
                                        }
                                }
                        },
                        wml.tag.row {  -- The body of our GUI
                                wml.tag.column {
                                        --multipage     
                                        tree_view
                                        --hardcoded_data
                                }
                        },
                        wml.tag.row {
                                wml.tag.column {  -- An "OK" button, with no action assigned for now, 
                                                  -- but it will close the gui
                                        wml.tag.button {
                                                id = "ok",
                                                label = _"OK"
                                        },
                                }
                        }
                }
        }

        gui.show_dialog(dialogDefinition)
end
In the cell of "The body of our GUI", if I include just the variable hardcoded_data, I get what I'm hoping for.

Then I tried replacing that with tree_view. I got a header and an okay button, and nothing in between.

In WC, the items tv is inside a scroll_panel/page_definition/multi_page. So I tried to replicate what I saw there and plug that into my GUI cell. Again, the body is empty.

At this point, as far as I can tell what I have should be equivalent to what WC has, just with some unrelated(?) columns and nodes omitted for clarity. I suppose the next step would be to replicate everything and then start pulling out pieces one by one, but that's a lot of typing and I hope someone point me in the right direction towards something very small I can start working UP from instead.

TIA
Speak softly, and carry Doombringer.
gfgtdf
Developer
Posts: 1432
Joined: February 10th, 2013, 2:25 pm

Re: GUI: getting started with tree_view

Post by gfgtdf »

Just like a lixbox a treeview is a dynamic container that is usually filled in preshow.

Unlike a listbox, a treeview is a heterogenous container, meaning that it can contain different "types" of rows/nodes, so while for listbox you can implicity add rows by accessing them (dialog.lisbox_id[item_nr].widget_id.label = "something" created the row number item_nr if needed) this is not possible for a treeview, in a treeview you have to add rows/nodes explicitly via dialog.monsters_tv:add_item_of_type("monsters_node") after that you can access the nodes childwidgets as usual
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.
User avatar
Celtic_Minstrel
Developer
Posts: 2235
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: GUI: getting started with tree_view

Post by Celtic_Minstrel »

You mentioned page_definition, which is part of the multi_page widget, rather than tree_view. A multi_page is the same sort of heterogenous container as a tree_view from the programming side, but it only shows one of its pages at a time; the page_definition defines the possible types of pages. The same functionality in tree_view is handled with node_definition.
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
white_haired_uncle
Posts: 1206
Joined: August 26th, 2018, 11:46 pm
Location: A country place, far outside the Wire

Re: GUI: getting started with tree_view

Post by white_haired_uncle »

Thank you both, that helps a lot. I can see now how my attempt at hardcoding the values made no sense.

I'll post the fixed code in case anyone finds this thread some day. It's a rather silly example, but it shows how tree_views can be populated with different data types.

P.S. For the life of me, I can't seem to get the text for the monster labels to center within the column (or align with the top of the image, or move at all really). Can't be that hard?

Code: Select all

function wesnoth.wml_actions.current_test_gui()
        local monsters = {
                { image = "units/trolls/grunt.png", label = "A troll", name = _"Bob", type = "Trolls" },
                { image = "units/trolls/whelp.png", label = "A troll whelp", name = _"Junior", type = "Trolls" },
                { image = "units/trolls/shaman.png", label = "A troll shaman", name = _"Alice", type = "Trolls" },
                { image = "units/monsters/cuttlefish.png", label = "A cuttlefish", type = "Seamonsters" },
                { image = "units/monsters/yeti.png", label = "A yeti", type = "Coolers" }
        }

        local tree_view = wml.tag.tree_view {
                id = "monsters_tv",
                wml.tag.node {
                        id = "trolls_node",
                        wml.tag.node_definition {
                                wml.tag.row {
                                        wml.tag.column {
                                                wml.tag.label {
                                                        id = "monster_name",
                                                        linked_group = "monster_name"
                                                }
                                        },
                                        wml.tag.column {
                                                wml.tag.image {
                                                        id = "monster_image",
                                                        linked_group = "monster_image"
                                                }
                                        },
                                        wml.tag.column {
                                                wml.tag.label {
                                                        id = "monster_label",
                                                        linked_group = "monster_label"
                                                }
                                        }
                                }
                        }
                },
                wml.tag.node {
                        id = "nottrolls_node",
                        wml.tag.node_definition {
                                wml.tag.row {
                                        wml.tag.column {
                                                wml.tag.image {
                                                        id = "monster_image",
                                                        linked_group = "monster_image"
                                                }
                                        },
                                        wml.tag.column {
                                                wml.tag.label {
                                                        id = "monster_label",
                                                        linked_group = "monster_label"
                                                }
                                        }
                                }
                        }
                }
        }
        local dialogDefinition = {
                wml.tag.tooltip { id = "tooltip_large" },
                wml.tag.helptip { id = "helptip_large" },
                wml.tag.linked_group {
                        id = "monster_name",
                        fixed_width = true
                },
                wml.tag.linked_group {
                        id = "monster_image",
                        fixed_width = true
                },
                wml.tag.linked_group {
                        id = "monster_label",
                        fixed_width = true
                },
                wml.tag.grid {
                        wml.tag.row {  -- A header 
                                wml.tag.column {
                                        border = "bottom",
                                        border_size = 10,
                                        wml.tag.label {
                                                use_markup = true,
                                                label = "<span size='large'>" .. _"Here there be " ..
                                                        "<span color='yellow'>" ..
                                                        _"MONSTERS!" .. "</span></span>"
                                        }
                                }
                        },
                        wml.tag.row {  -- The body of our GUI
                                wml.tag.column {
                                        tree_view
                                }
                        },
                        wml.tag.row {
                                wml.tag.column {  -- An "OK" button, with no action assigned for now, 
                                                  -- but it will close the gui
                                        wml.tag.button {
                                                id = "ok",
                                                label = _"OK"
                                        },
                                }
                        }
                }
        }
        local function preshow(dialog)  -- Prepare the GUI before display
                for i, monster in ipairs(monsters) do
                        if monster.type == "Trolls" then
                                dialog.monsters_tv:add_item_of_type("trolls_node")
                                dialog.monsters_tv[i].monster_name.label = monster.name
                                dialog.monsters_tv[i].monster_image.label = monster.image
                                dialog.monsters_tv[i].monster_label.label = monster.label
                        else
                                dialog.monsters_tv:add_item_of_type("nottrolls_node")
                                dialog.monsters_tv[i].monster_image.label = monster.image
                                dialog.monsters_tv[i].monster_label.label = monster.label
                        end
                end
        end
        gui.show_dialog(dialogDefinition,preshow)
end



Speak softly, and carry Doombringer.
gfgtdf
Developer
Posts: 1432
Joined: February 10th, 2013, 2:25 pm

Re: GUI: getting started with tree_view

Post by gfgtdf »

white_haired_uncle wrote: December 31st, 2023, 4:32 am
P.S. For the life of me, I can't seem to get the text for the monster labels to center within the column (or align with the top of the image, or move at all really). Can't be that hard?
You have to tell the code which part it has to grow (with horizontal_grow=true) because there is more space than needed, maybe evn add a dummy spacer that fills the gap.
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.
white_haired_uncle
Posts: 1206
Joined: August 26th, 2018, 11:46 pm
Location: A country place, far outside the Wire

Re: GUI: getting started with tree_view

Post by white_haired_uncle »

gfgtdf wrote: December 31st, 2023, 11:34 pm
white_haired_uncle wrote: December 31st, 2023, 4:32 am
P.S. For the life of me, I can't seem to get the text for the monster labels to center within the column (or align with the top of the image, or move at all really). Can't be that hard?
You have to tell the code which part it has to grow (with horizontal_grow=true) because there is more space than needed, maybe evn add a dummy spacer that fills the gap.
I have been really struggling with this. I can envision some scenarios where a row/column/cell would need to grow based on the data around it, and I can usually get the data layed out properly for a specific case, but I can't come up with a general set of rules to accurately describe to someone else how to get their ducks in a row for some arbitrary grid.

Before I can understand how the grow/align/etc stuff works, I think I need to understand how the gui lays out a grid (before I start adding grow/alignmt rules). If there is a way to query a grid and get the height and width of each row/column/cell/widget that would help me a lot. I tried using set_canvas and drawing rectangles with borders, but that only seems to work on widgets (it did seem to confirm my suspicion of about widget size at least).

So far, I think these things are true:

1) The size of a widget is determined by the size of the data it contains (at least for image/label).
2) The size of a cell is determined by the height of the row and the width of the column it resides in.
3) The height of a row is determined by the tallest widget in the row.
4) In a grid with multiple rows, the rows will be equal in width. Columns will be widened as necessary.
5) The only "dead space" is within a cell if the widget it contains is smaller than the cell.

6?) At least in a 2x2 grid, the columns may be widened more than what is required by rule 4 so that the columns in
each row line up with the columns in the other row (nice to look at, annoying to describe).
Speak softly, and carry Doombringer.
gfgtdf
Developer
Posts: 1432
Joined: February 10th, 2013, 2:25 pm

Re: GUI: getting started with tree_view

Post by gfgtdf »

I don't remember the details, but iirc it goes like this:

You have vertical/horizontal grow, this is a property of the cells, a cell can either grow or have an alignment. It determines how a widget behaves inside its cell.

You have grow_factor, this is a property of rows/columns, it specifies how the columns/rows behave if the grid as a whole grows (usually becasue the grid is inside a cell with some grow=yes attribute)

You have linked_groups which can somehow force certain widgets to have the same size,

I'm actualyl not 100% sure how all of this works inside a treeview,

So my attempt would be to add grow_factors as following.

Code: Select all

        local tree_view = wml.tag.tree_view {
                id = "monsters_tv",
                wml.tag.node {
                        id = "trolls_node",
                        wml.tag.node_definition {
                                wml.tag.row {
                                        wml.tag.column {
                                                --don't grow this
                                                grow_factor = 0,
                                                wml.tag.label {
                                                        id = "monster_name",
                                                        linked_group = "monster_name"
                                                }
                                        },
                                        wml.tag.column {
                                                --grow this instead
                                                grow_factor = 1,
                                                wml.tag.image {
                                                        id = "monster_image",
                                                        linked_group = "monster_image"
                                                }
                                        },
                                        wml.tag.column {
                                                -- don't grow this
                                                grow_factor = 1,
                                                wml.tag.label {
                                                        id = "monster_label",
                                                        linked_group = "monster_label"
                                                }
                                        }
                                }
                        }
                },
                wml.tag.node {
                        id = "nottrolls_node",
                        wml.tag.node_definition {
                                wml.tag.row {
                                        wml.tag.column {
                                                grow_factor = 0,
                                                wml.tag.image {
                                                        id = "monster_image",
                                                        linked_group = "monster_image"
                                                }
                                        },
                                        wml.tag.column {
                                                --put all remaining space here
                                                grow_factor = 1,
                                                wml.tag.spacer {
                                                }
                                        },
                                        wml.tag.column {
                                                grow_factor = 0,
                                                wml.tag.label {
                                                        id = "monster_label",
                                                        linked_group = "monster_label"
                                                }
                                        }
                                }
                        }
                }
        }

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.
Post Reply