GUI structuring tools and event handling.
local gui = require("__flib__.gui")
build(parent, structures) | Build a GUI structure. |
dispatch_handlers(event_data) | Dispatch GUI handlers for the given event. |
update_filters(name, player_index, filters, mode) | Add or remove GUI filters to or from a handler or group of handlers. |
remove_player_filters(player_index) | Remove all GUI filters for the given player. |
GuiFilter | An index or string used for filtering GUI handlers on specific elements. |
GuiStructure | A series of nested tables used to build a GUI. |
GuiOutputTable | First output of gui.build, containing saved elements. |
GuiOutputFiltersTable | Second output of gui.build, containing a handler_name –> filters mapping. |
init() | Initial setup. |
build_lookup_tables() | Generate template and handler lookup tables. |
check_filter_validity() | Purge all filters tied to non-existent handlers from global . |
register_handlers() | Register handlers for all GUI events to pass through the module. |
add_templates(input) | Add content to the gui.templates table. |
add_handlers(input) | Add content to the gui.handlers table. |
handlers | GUI handlers, built using gui.add_handlers. |
handler_lookup | One-dimensional handler lookup table, generated using gui.build_lookup_tables. |
handler_groups | Mapping of group names to all handlers belonging to them, generated using gui.build_lookup_tables. |
templates | GUI templates, built using gui.add_templates. |
template_lookup | One-dimensional template lookup table, generating using gui.build_lookup_tables. |
Build a GUI structure.
Parameters:
gui.build(player.gui.screen, {
{type="frame", style="dialog_frame", direction="vertical", handlers="window", save_as="window", children={
{type="flow", children={
{type="label", style="frame_title", caption="Menu"},
{template="drag_handle", style_mods={horizontally_stretchable=true, height=24},
save_as="titlebar.drag_handle"},
{type="condition", condition=(not is_dialog_frame), children={
{template="frame_action_button", sprite="utility/close_white", handlers="titlebar.close_button",
save_as="titlebar.close_button"}
}}
}},
{type="frame", style="inside_shallow_frame_with_padding", children={
{type="table", style="slot_table", column_count=10, save_as="content.slot_table"}
}},
{type="condition", condition=is_dialog_frame, children={
{type="flow", children={
{type="button", style="back_button", caption={"gui.back"}, handlers="footer.back_button"},
{template="drag_handle", style_mods={horizontally_stretchable=true, height=32}, save_as="footer.drag_handle"}
}}
}}
}}
})
Dispatch GUI handlers for the given event.
Only needed if not using gui.register_handlers or if overriding a handler registered using that function.
Parameters:event.on_gui_opened(function(e)
-- dispatch any matching handlers
if not gui.dispatch_handlers(e) then
-- run custom logic if no handlers were dispatched
inventory.on_gui_opened(e)
end
end)
Add or remove GUI filters to or from a handler or group of handlers.
When destroying a GUI element with a filter, be certain to call this function destroying that filter, to avoid memory leaks.
Parameters:nil
to clear all filters when in remove
mode.
-- add a filter to a group of events
gui.update_filters("main.content.inventory_button", player_index, {"demo_inventory_button"}, "add")
-- remove a filter from a group of events
gui.update_filters("main.content.inventory_button", player_index, {"demo_inventory_button"}, "remove")
-- remove all filters tied to a group of events
gui.update_filters("main.content.inventory_button", player_index, nil, "remove")
-- add a filter to a specific event
gui.update_filters("main.titlebar.drag_handle.on_gui_click", player_index, {elems.drag_handle.index}, "add")
Remove all GUI filters for the given player.
Parameters:
An index or string used for filtering GUI handlers on specific elements.
Defined as one of the following:
__
.A series of nested tables used to build a GUI.
This is an extension of LuaGuiElement, providing new features and options.
This inherits all required properties from its base LuaGuiElement, i.e. if the type
field is
sprite-button
, the GuiStructure must contain all the fields that a sprite-button
LuaGuiElement requires.
There are two new types that are exposed, each of which restrict what parameters can be added:
condition
and children
parameters only.tab
and content
parameters only.In addition there are a number of new, fields that can be applied to a GuiStructure depending on the type:
template
A string corresponding to a GuiStructure in the gui.template_lookup table. The contents of this “template” will be used as a base, and any other paramters in this GuiStructure will overwrite / add to this base.
condition
For condition
types only. If true, the children of this GuiStructure will be added to the parent of this
GuiStructure. If false, they will not.
tab
For tab-and-content
types only. A GuiStructure defining a tab to be placed in a tabbed-pane.
content
For tab-and-content
types only. A GuiStructure defining the content that will be shown when the corresponding
tab is active.
style_mods
A key –> value dictionary defining modifications to make to the element’s style. Available properties are listed in LuaStyle.
elem_mods
A key –> value dictionary defining modifications to make to the element. Available properties are listed in LuaGuiElement.
handlers
A string corresponding to an item in the gui.handler_groups table. The element will be registered to the handlers in that group, and when the events are raised by the game relating to this element, the corresponding handlers will be dispatched.
save_as
A string defining a table path to save this element to, in the first return value of gui.build. This is a
dot-deliminated list of nested table names, followed by a key to save the element as. The module will construct the
output table dynamically based on the contents of save_as
throughout the structure.
children
An array of GuiStructure that will be added as children of this element.
Usage:{type="frame", style="dialog_frame", direction="vertical", handlers="window", save_as="window", children={
{type="flow", children={
{type="label", style="frame_title", caption="Menu"},
{template="drag_handle", style_mods={horizontally_stretchable=true, height=24},
save_as="titlebar.drag_handle"},
{type="condition", condition=(not is_dialog_frame), children={
{template="frame_action_button", sprite="utility/close_white", handlers="titlebar.close_button",
save_as="titlebar.close_button"}
}}
}},
{type="frame", style="inside_shallow_frame_with_padding", children={
{type="table", style="slot_table", column_count=10, save_as="content.slot_table"}
}},
{type="condition", condition=is_dialog_frame, children={
{type="flow", children={
{type="button", style="back_button", caption={"gui.back"}, handlers="footer.back_button"},
{template="drag_handle", style_mods={horizontally_stretchable=true, height=32}, save_as="footer.drag_handle"}
}}
}}
}}
First output of gui.build, containing saved elements.
The layout of this table is defined in the GuiStructure using the save_as
parameter. Consists of nested tables
and key –> LuaGuiElement pairs.
Second output of gui.build, containing a handler_name –> filters mapping.
This is useful for knowing which filters are assigned to which handlers for this specific structure, so you can remove them later using gui.update_filters.
Usage:{
["window.on_gui_closed"] = {23},
["content_pane.ingredients_list.ingredient_button.on_gui_click"] = {"demo_ingredient_button", 30},
["titlebar.close_button.on_gui_click"] = {21}
}
Initial setup.
Must be called during on_init
before any structures are built.
If adding the module to an existing mod, this must be called in on_configuration_changed
for that version as well.
Generate template and handler lookup tables.
Must be called during on_init
after gui.init, and during on_init
and on_load
after all templates and
handlers have been added, but before any structures are built.
Purge all filters tied to non-existent handlers from global
.
Must be called during on_configuration_changed
.
This function is necessary in order to prevent crashes when a handler was removed between mod versions, but still has
filters assigned to it stored in global
.
Register handlers for all GUI events to pass through the module.
This is completely optional, but saves you having to create handlers for all GUI events simply to call gui.dispatch_handlers. If custom logic is needed, handlers may be overwritten after calling this.
This function, if used, should be called in the root scope.
Usage:-- register handlers for all GUI events
gui.register_handlers()
-- overwrite a handler to add custom logic
event.on_gui_opened(function(e)
-- pass through the GUI module
if not gui.dispatch_handlers(e) then
-- run our custom logic if no handlers were dispatched
inventory.on_gui_opened(e)
end
end)
Add content to the gui.templates table.
The table you provide will be merged with the current contents of the table.
This table must be complete before gui.build_lookup_tables is run, and must not be changed afterwards.
NOTE: In order to be detected properly, a template must include either a type
key or another template
key.
If both of these are omitted, the template will not be added to the lookup table and will be unusable.
Add content to the gui.handlers table.
The table you provide will be merged with the current contents of the table.
This table must be complete before gui.build_lookup_tables is run, and must not be changed afterwards.
Parameters:GUI handlers, built using gui.add_handlers.
Usage:
-- add handlers
gui.add_handlers{
-- you can organize the handlers however you like
base = {
titlebar = {
-- this is a handler group for a specific GUI element
close_button = {
-- if using a defines.events event, this shortcut syntax is used:
on_gui_click = function(e)
__DebugAdapter.print(e)
end,
-- for direct event IDs, nest the function inside a table:
my_custom_event = {id=constants.my_custom_event, handler=function(e)
__DebugAdapter.print(e)
}
}
}
}
}
-- use the handlers
gui.build(player.gui.screen, {
{type="sprite-button", style="frame_action_button", sprite="utility/close_white", handlers="base.titlebar.close_button"}
})
One-dimensional handler lookup table, generated using gui.build_lookup_tables.
This is what gui.dispatch_handlers uses to retrieve and dispatch handlers tied to certain elements. Each value is a table containing the event ID, handler, groups that handler belongs to, and a mapping of the GUI filters currently assigned to the handler.
Usage:-- given:
gui.add_handlers{
base = {
titlebar = {
close_button = {
on_gui_click = function(e)
__DebugAdapter.print(e)
end
}
}
}
}
gui.build(player.gui.screen, {
{type="sprite-button", style="frame_action_button", sprite="utility/close_white", handlers="base.titlebar.close_button"}
})
-- contents of gui.handler_lookup will be:
{
["base.titlebar.close_button.on_gui_click"] = {
id = defines.events.on_gui_click,
handler = function(e)
__DebugAdapter.print(e)
end,
groups = {
"base",
"base.titlebar",
"base.titlebar.close_button"
},
filters = {
[1] = { -- the player's index
__size = 1,
[5] = 5 -- the element's index, as both the key and the value
}
}
}
}
Mapping of group names to all handlers belonging to them, generated using gui.build_lookup_tables.
This is what gui.build uses to find handler groups when using the handlers
parameter in a GuiStructure.
-- given:
gui.add_handlers{
base = {
titlebar = {
close_button = {
on_gui_click = function(e)
__DebugAdapter.print(e)
end,
my_custom_event = {id=constants.my_custom_event, handler=function(e)
__DebugAdapter.print(e)
}
}
},
content = {
item_button = {
on_gui_click = function(e)
__DebugAdapter.print(e)
end
}
}
}
}
-- contents of gui.handler_groups will be:
{
["base"] = {
"base.titlebar.close_button.on_gui_click",
"base.titlebar.close_button.my_custom_event",
"base.content.item_button.on_gui_click"
},
["base.titlebar"] = {
"base.titlebar.close_button.on_gui_click",
"base.titlebar.close_button.my_custom_event"
},
["base.titlebar.close_button"] = {
"base.titlebar.close_button.on_gui_click",
"base.titlebar.close_button.my_custom_event"
},
["base.content"] = {
"base.content.item_button.on_gui_click"
},
["base.content.item_button"] = {
"base.content.item_button.on_gui_click"
}
}
GUI templates, built using gui.add_templates.
Usage:
-- add content to the table
gui.add_templates{
-- templates are usually defined as GuiStructures
frame_action_button = {type="sprite-button", style="frame_action_button", mouse_button_filter={"left"}},
-- you can divide and organize templates however you wish
pushers = {
horizontal = {type="empty-widget", style_mods={horizontally_stretchable=true}},
vertical = {type="empty-widget", style_mods={vertically_stretchable=true}}
},
-- templates can also be functions
list_box_with_label = function(name)
return
{type="flow", direction="vertical", children={
{type="label", style="bold_label", caption={"my-listbox-labels."..name}, save_as="listboxes."..name..".label"},
{type="list-box", save_as="listboxes."..name..".list_box"}
}}
end
}
-- use the templates
gui.build(player.gui.screen, {
-- use GuiStructure templates using the "template" parameter
{template="pushers.horizontal"},
{template="frame_action_button", sprite="utility/close_white"},
-- use function templates by calling them directly
gui.templates.list_box_with_label("ingredients"),
gui.templates.list_box_with_label("products")
})
One-dimensional template lookup table, generating using gui.build_lookup_tables.
This is what gui.build uses to find templates when using the templates
parameter in a GuiStructure.
-- given:
gui.add_templates{
frame_action_button = {type="sprite-button", style="frame_action_button", mouse_button_filter={"left"}},
pushers = {
horizontal = {type="empty-widget", style_mods={horizontally_stretchable=true}},
vertical = {type="empty-widget", style_mods={vertically_stretchable=true}}
}
}
-- contents of template_lookup will be:
{
["frame_action_button"] = {type="sprite-button", style="frame_action_button", mouse_button_filter={"left"}},
["pushers.horizontal"] = {type="empty-widget", style_mods={horizontally_stretchable=true}},
["pushers.vertical"] = {type="empty-widget", style_mods={vertically_stretchable=true}}
}