Difference between revisions of "Sandbox/GUI/Getting Started"

From The Battle for Wesnoth Wiki
m (A most basic GUI)
m (A most basic GUI)
Line 52: Line 52:
 
Our function creates a table containing the definition of a "dialog", and then passes that to [[LuaAPI/gui#gui.show_dialog|gui.show_dialog()]].  It should be noted that gui.show_dialog does not provide synchronization, so if your GUI make changes to the game state, you'll need to jump through undocumented hoops or you'll break replays and multiplayer, but that's not something we need to care about at this point, so just be aware that you may need to deal with it in the future.
 
Our function creates a table containing the definition of a "dialog", and then passes that to [[LuaAPI/gui#gui.show_dialog|gui.show_dialog()]].  It should be noted that gui.show_dialog does not provide synchronization, so if your GUI make changes to the game state, you'll need to jump through undocumented hoops or you'll break replays and multiplayer, but that's not something we need to care about at this point, so just be aware that you may need to deal with it in the future.
  
Our dialog definition at this point includes three parts.  The first two are a tooltip and a helptip, which are required and probably do something, but nothing we care about here, we just have to have them.  The third part is a grid, which is basically a table, like in HTML or MySQL, with rows and columns.  A grid always contains at least one row.  A row contains at least one column.  Inside a column is a [[https://devdocs.wesnoth.org/group__GUIWidgetWML.html|widget]], in this case we'll use an image.
+
Our dialog definition at this point includes three parts.  The first two are a tooltip and a helptip, which are required and probably do something, but nothing we care about here, we just have to have them.  The third part is a grid, which is basically a table, like in HTML or MySQL, with rows and columns.  A grid always contains at least one row.  A row contains at least one column.  Inside a column is a [https://devdocs.wesnoth.org/group__GUIWidgetWML.html|widget], in this case we'll use an image.
  
 
Note: if you should try out the above, you'll have to hit escape to close the gui.
 
Note: if you should try out the above, you'll have to hit escape to close the gui.

Revision as of 23:22, 29 December 2023

So, it looks like I can't exclude this page/section from the search engine as I had hoped. I wanted to put this in a sandbox so that it wouldn't be published without some proper review. I guess for now

DON'T TRUST ANYTHING YOU READ HERE

This guide is designed to help you get a simple Wesnoth GUI, implemented in lua, up and running while describing the basic building blocks along the way. Some would find creating a GUI in part or in full using WML simpler to follow, and those alternatives are available, but we're using lua.

For example purposes, we'll create a directory in our campaign directory called lua containing a file called gui_tutorial.lua, and add a command in the prestart event for a scenario which will create a right-click menu option to invoke our new GUI. Of course there are other methods, but this one will get us started. After all, we really just want to get something up on the screen ASAP, right?

A most basic GUI

In our prestart event, we create a simple menu item, which calls lua with one command that calls the function most_basic_gui() which is found in gui_tutorial.lua:

[set_menu_item]
   id=most_basic_gui
   description="Our first GUI"
   [command]
       [lua]
           code=<<
                   wesnoth.require("~add-ons/<OUR_CAMPAIGN>/lua/gui_tutorial.lua").most_basic_gui()
                >>
       [/lua]
    [/command]
[/set_menu_item]

And create gui_tutorial.lua:

local function most_basic_gui()
        local dialogDefinition = {
                wml.tag.tooltip { id = "tooltip_large" },  -- required
                wml.tag.helptip { id = "helptip_large" },  -- required
                wml.tag.grid {   -- our most basic gui
                        wml.tag.row {  -- must start with a row
                                wml.tag.column {  -- a row needs a column
                                        wml.tag.image {  -- we can put stuff in a column
                                                label = "units/trolls/grunt.png"
                                        }
                                }
                        }
                }
        }
        gui.show_dialog(dialogDefinition)
end
return { most_basic_gui = most_basic_gui}

In the above file, we have just the single function to create our GUI, and a return command which makes our function most_basic_gui() available to the wesnoth.require command as most_basic_gui(). In this case, we'll just refer to our function using the same name as the function.

Our function creates a table containing the definition of a "dialog", and then passes that to gui.show_dialog(). It should be noted that gui.show_dialog does not provide synchronization, so if your GUI make changes to the game state, you'll need to jump through undocumented hoops or you'll break replays and multiplayer, but that's not something we need to care about at this point, so just be aware that you may need to deal with it in the future.

Our dialog definition at this point includes three parts. The first two are a tooltip and a helptip, which are required and probably do something, but nothing we care about here, we just have to have them. The third part is a grid, which is basically a table, like in HTML or MySQL, with rows and columns. A grid always contains at least one row. A row contains at least one column. Inside a column is a [1], in this case we'll use an image.

Note: if you should try out the above, you'll have to hit escape to close the gui.

Adding a little flavor

You may have noticed our GUI is missing a header and a way to close it when we're done admiring our work. Here we add a new first row to our grid, while demonstrating a couple formatting options: a border to separate our grid cell its neighbor, and the "use_markup = true" option to enable Pango support in our text. Note the use of ".." to concatenate the parts of our label so we can internationalize the actual text.

Our third row adds an OK button at the bottom. You can associate actions with buttons to do all kinds of things, but here we just exploit the default behaviour to close the GUI.

Of course, we'll also have to modify our return to support the new function, and update our menu item(s) accordingly.

local function most_basic_gui2()
        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 {
                                        wml.tag.image { 
                                                label = "units/trolls/grunt.png"
                                        }
                                }
                        },
                        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

And a bit more

And now a minor, but important change. We want to add a new text field to the body of our GUI. Obviously, we want to add a new column, but this is more difficult than when we added new rows in the previous example. The problem is that all rows (at a given level) must contain the same number of columns. We can't have two columns in the row that constitutes the body of our GUI, but only one in the header and OK button. To solve this problem, we put our image tag with a new grid, which can use as many columns as we like (as long as they are the same within each row of our new grid).

local function most_basic_gui3()
        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 {
                                        wml.tag.grid {
                                                wml.tag.row {
                                                        wml.tag.column {
                                                                wml.tag.image {
                                                                        label = "units/trolls/grunt.png"
                                                                }
                                                        },
                                                        wml.tag.column {
                                                                wml.tag.label {
                                                                        label = "A troll"
                                                                }
                                                        }
                                                }
                                        }
                                }
                        },
                        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

Adding a listbox

Now we'd like to add some more monsters. Obviously, we could just add more rows, but what if we won't know until runtime how many and which ones? We could break up the definition of our dialog and add new rows dynamically, it's just a table after all, but fortunately we have a widget which handles this for us, a listbox. A listbox is kind of like an array, a collection of similar objects. We tell the GUI where we want the box, and what each entry in the box will look like, and then at runtime we can add entries to the box.