Refactor some of the Guis from the legacy plugin (#399)

* Fix bugs in core and add default args to Gui defs

* Refactor production Gui

* Refactor landfill blueprint button

* Fix more bugs in core

* Consistent naming of new guis

* Refactor module inserter gui

* Refactor surveillance gui

* Add shorthand for data from arguments

* Make element names consistent

* Add types

* Change how table rows work

* Refactor player stats gui

* Refactor quick actions gui

* Refactor research milestones gui

* Refactor player bonus gui

* Refactor science production gui

* Refactor autofill gui

* Cleanup use of aligned flow

* Rename "Gui.element" to "Gui.define"

* Rename Gui types

* Rename property_from_arg

* Add guide for making guis

* Add full reference document

* Add condensed reference

* Apply style guide to refactored guis

* Bug fixes
This commit is contained in:
Cooldude2606
2025-08-29 14:30:30 +01:00
committed by GitHub
parent e2a7ab7b8b
commit 7ab721b4b6
72 changed files with 6736 additions and 4105 deletions

View File

@@ -3,37 +3,48 @@ local Storage = require("modules/exp_util/storage")
local ExpElement = require("modules/exp_gui/prototype")
--- @alias ExpGui.VisibleCallback fun(player: LuaPlayer, element: LuaGuiElement): boolean
--- @alias Gui.VisibleCallback fun(player: LuaPlayer, element: LuaGuiElement): boolean
--- @class ExpGui.player_elements
--- @class Gui.player_elements
--- @field top table<string, LuaGuiElement?>
--- @field left table<string, LuaGuiElement?>
--- @field relative table<string, LuaGuiElement?>
--- @type table<uint, ExpGui.player_elements>
--- @type table<uint, Gui.player_elements> | table<"_debug", boolean>
local player_elements = {}
Storage.register(player_elements, function(tbl)
player_elements = tbl
end)
--- @class ExpGui
local ExpGui = {
element = ExpElement.create,
property_from_arg = ExpElement.property_from_arg,
property_from_name = ExpElement.property_from_name,
top_elements = {}, --- @type table<ExpElement, ExpGui.VisibleCallback | boolean>
left_elements = {}, --- @type table<ExpElement, ExpGui.VisibleCallback | boolean>
relative_elements = {}, --- @type table<ExpElement, ExpGui.VisibleCallback | boolean>
--- @class Gui
local Gui = {
define = ExpElement.new,
from_argument = ExpElement.from_argument,
from_name = ExpElement.from_name,
no_return = ExpElement.no_return,
top_elements = {}, --- @type table<ExpElement, Gui.VisibleCallback | boolean>
left_elements = {}, --- @type table<ExpElement, Gui.VisibleCallback | boolean>
relative_elements = {}, --- @type table<ExpElement, Gui.VisibleCallback | boolean>
}
local mod_gui = require("mod-gui")
ExpGui.get_top_flow = mod_gui.get_button_flow
ExpGui.get_left_flow = mod_gui.get_frame_flow
Gui.get_top_flow = mod_gui.get_button_flow
Gui.get_left_flow = mod_gui.get_frame_flow
--- Set the gui to redraw all elements on join, helps with style debugging
--- @param state boolean
function Gui._debug(state)
if state == nil then
player_elements._debug = true
else
player_elements._debug = state
end
end
--- Get a player from an element or gui event
--- @param input LuaGuiElement | { player_index: uint } | { element: LuaGuiElement }
--- @return LuaPlayer
function ExpGui.get_player(input)
function Gui.get_player(input)
if type(input) == "table" and not input.player_index then
return assert(game.get_player(input.element.player_index))
end
@@ -44,7 +55,7 @@ end
--- @param element LuaGuiElement
--- @param state boolean?
--- @return boolean
function ExpGui.toggle_enabled_state(element, state)
function Gui.toggle_enabled_state(element, state)
if not element or not element.valid then return false end
if state == nil then
state = not element.enabled
@@ -57,7 +68,7 @@ end
--- @param element LuaGuiElement
--- @param state boolean?
--- @return boolean
function ExpGui.toggle_visible_state(element, state)
function Gui.toggle_visible_state(element, state)
if not element or not element.valid then return false end
if state == nil then
state = not element.visible
@@ -68,41 +79,41 @@ end
--- Destroy an element if it exists and is valid
--- @param element LuaGuiElement?
function ExpGui.destroy_if_valid(element)
function Gui.destroy_if_valid(element)
if not element or not element.valid then return end
element.destroy()
end
--- Register a element define to be drawn to the top flow on join
--- @param define ExpElement
--- @param visible ExpGui.VisibleCallback | boolean | nil
function ExpGui.add_top_element(define, visible)
assert(ExpGui.top_elements[define.name] == nil, "Element is already added to the top flow")
ExpGui.top_elements[define] = visible or false
--- @param visible Gui.VisibleCallback | boolean | nil
function Gui.add_top_element(define, visible)
assert(Gui.top_elements[define.name] == nil, "Element is already added to the top flow")
Gui.top_elements[define] = visible or false
end
--- Register a element define to be drawn to the left flow on join
--- @param define ExpElement
--- @param visible ExpGui.VisibleCallback | boolean | nil
function ExpGui.add_left_element(define, visible)
assert(ExpGui.left_elements[define.name] == nil, "Element is already added to the left flow")
ExpGui.left_elements[define] = visible or false
--- @param visible Gui.VisibleCallback | boolean | nil
function Gui.add_left_element(define, visible)
assert(Gui.left_elements[define.name] == nil, "Element is already added to the left flow")
Gui.left_elements[define] = visible or false
end
--- Register a element define to be drawn to the relative flow on join
--- @param define ExpElement
--- @param visible ExpGui.VisibleCallback | boolean | nil
function ExpGui.add_relative_element(define, visible)
assert(ExpGui.relative_elements[define.name] == nil, "Element is already added to the relative flow")
ExpGui.relative_elements[define] = visible or false
--- @param visible Gui.VisibleCallback | boolean | nil
function Gui.add_relative_element(define, visible)
assert(Gui.relative_elements[define.name] == nil, "Element is already added to the relative flow")
Gui.relative_elements[define] = visible or false
end
--- Register a element define to be drawn to the top flow on join
--- @param define ExpElement
--- @param player LuaPlayer
--- @return LuaGuiElement
function ExpGui.get_top_element(define, player)
function Gui.get_top_element(define, player)
return assert(player_elements[player.index].top[define.name], "Element is not on the top flow")
end
@@ -110,7 +121,7 @@ end
--- @param define ExpElement
--- @param player LuaPlayer
--- @return LuaGuiElement
function ExpGui.get_left_element(define, player)
function Gui.get_left_element(define, player)
return assert(player_elements[player.index].left[define.name], "Element is not on the left flow")
end
@@ -118,13 +129,13 @@ end
--- @param define ExpElement
--- @param player LuaPlayer
--- @return LuaGuiElement
function ExpGui.get_relative_element(define, player)
function Gui.get_relative_element(define, player)
return assert(player_elements[player.index].relative[define.name], "Element is not on the relative flow")
end
--- Ensure all the correct elements are visible and exist
--- @param player LuaPlayer
--- @param element_defines table<ExpElement, ExpGui.VisibleCallback | boolean>
--- @param element_defines table<ExpElement, Gui.VisibleCallback | boolean>
--- @param elements table<string, LuaGuiElement?>
--- @param parent LuaGuiElement
local function ensure_elements(player, element_defines, elements, parent)
@@ -135,7 +146,7 @@ local function ensure_elements(player, element_defines, elements, parent)
if not element or not element.valid then
element = assert(define(parent), "Element define did not return an element: " .. define.name)
elements[define.name] = element
if type(visible) == "function" then
visible = visible(player, element)
end
@@ -153,39 +164,45 @@ end
--- Ensure all elements have been created
--- @param event EventData.on_player_created | EventData.on_player_joined_game
function ExpGui._ensure_consistency(event)
function Gui._ensure_consistency(event)
local player = assert(game.get_player(event.player_index))
local elements = player_elements[event.player_index]
if not elements then
elements = {
top = {},
left = {},
relative = {},
}
player_elements[event.player_index] = elements
local elements = player_elements[event.player_index] or {
top = {},
left = {},
relative = {},
}
player_elements[event.player_index] = elements
if player_elements._debug and event.name == defines.events.on_player_joined_game then
log("Gui debug active, clearing gui for: " .. player.name)
player.gui.relative.clear()
player.gui.screen.clear()
player.gui.center.clear()
player.gui.left.clear()
player.gui.top.clear()
end
ensure_elements(player, ExpGui.top_elements, elements.top, ExpGui.get_top_flow(player))
ensure_elements(player, ExpGui.left_elements, elements.left, ExpGui.get_left_flow(player))
ensure_elements(player, ExpGui.relative_elements, elements.relative, player.gui.relative)
ensure_elements(player, Gui.top_elements, elements.top, Gui.get_top_flow(player))
ensure_elements(player, Gui.left_elements, elements.left, Gui.get_left_flow(player))
ensure_elements(player, Gui.relative_elements, elements.relative, player.gui.relative)
-- This check isn't needed, but allows the toolbar file to be deleted without modifying any lib code
if ExpGui.toolbar then
if Gui.toolbar then
--- @diagnostic disable-next-line invisible
ExpGui.toolbar._create_elements(player)
Gui.toolbar._create_elements(player)
--- @diagnostic disable-next-line invisible
ExpGui.toolbar._ensure_consistency(player)
Gui.toolbar._ensure_consistency(player)
end
end
--- Rerun the visible check for relative elements
--- @param event EventData.on_gui_opened
local function on_gui_opened(event)
local player = ExpGui.get_player(event)
local player = Gui.get_player(event)
local original_element = event.element
for define, visible in pairs(ExpGui.relative_elements) do
local element = ExpGui.get_relative_element(define, player)
for define, visible in pairs(Gui.relative_elements) do
local element = Gui.get_relative_element(define, player)
if type(visible) == "function" then
visible = visible(player, element)
@@ -204,10 +221,10 @@ end
local e = defines.events
local events = {
[e.on_player_created] = ExpGui._ensure_consistency,
[e.on_player_joined_game] = ExpGui._ensure_consistency,
[e.on_player_created] = Gui._ensure_consistency,
[e.on_player_joined_game] = Gui._ensure_consistency,
[e.on_gui_opened] = on_gui_opened,
}
ExpGui.events = events
return ExpGui
Gui.events = events
return Gui

View File

@@ -1,4 +1,4 @@
--[[-- ExpGui - GuiData
--[[-- Gui - GuiData
Provides a method of storing data for elements, players, and forces under a given scope.
This is not limited to GUI element definitions but this is the most common use case.
]]
@@ -6,10 +6,10 @@ This is not limited to GUI element definitions but this is the most common use c
local ExpUtil = require("modules/exp_util")
local Storage = require("modules/exp_util/storage")
--- @type table<string, ExpGui.GuiData_Raw>
--- @type table<string, GuiData_Raw>
local scope_data = {}
--- @type table<string, ExpGui.GuiData_Internal>
--- @type table<string, GuiData_Internal>
local registered_scopes = {}
--- @type table<uint, uint> Reg -> Player Index
@@ -38,12 +38,12 @@ Storage.register({
end
end)
--- @class ExpGui_GuiData
--- @class _GuiData
local GuiData = {
_scopes = registered_scopes,
}
--- @class ExpGui.GuiData_Raw
--- @class GuiData_Raw
--- @field element_data table<uint, table<uint, any>?>?
--- @field player_data table<uint, any>?
--- @field force_data table<uint, any>?
@@ -51,13 +51,13 @@ local GuiData = {
-- This class has no prototype methods
-- Keep this in sync with DataKeys to block arbitrary strings
--- @class ExpGui.GuiData_Internal
--- @class GuiData_Internal
--- @field _scope string
--- @field _raw ExpGui.GuiData_Raw
--- @field _raw GuiData_Raw
-- This class has no prototype methods
-- Do add keys to _raw without also referencing scope_data
--- @class ExpGui.GuiData: ExpGui.GuiData_Internal
--- @class GuiData: GuiData_Internal
--- @field element_data table<uint, table<uint, any>>
--- @field player_data table<uint, any>
--- @field force_data table<uint, any>
@@ -70,7 +70,7 @@ GuiData._metatable = {
}
--- Return the index for a given key
--- @param self ExpGui.GuiData_Internal
--- @param self GuiData_Internal
--- @param key DataKeys | DataKey
--- @return any
function GuiData._metatable.__index(self, key)
@@ -105,7 +105,7 @@ end
--- Set the value index of a given key
-- Internal type is not used here to allow for creation of storage
--- @param self ExpGui.GuiData
--- @param self GuiData
--- @param key DataKey
--- @param value unknown
function GuiData._metatable.__newindex(self, key, value)
@@ -134,7 +134,7 @@ end
--- Create the data object for a given scope
--- @param scope string
--- @return ExpGui.GuiData
--- @return GuiData
function GuiData.create(scope)
ExpUtil.assert_not_runtime()
assert(GuiData._scopes[scope] == nil, "Scope already exists with name: " .. scope)
@@ -145,15 +145,15 @@ function GuiData.create(scope)
}
GuiData._scopes[scope] = instance
--- @cast instance ExpGui.GuiData
--- @cast instance GuiData
return setmetatable(instance, GuiData._metatable)
end
--- Get the link to an existing data scope
--- @param scope string
--- @return ExpGui.GuiData
--- @return GuiData
function GuiData.get(scope)
return GuiData._scopes[scope] --[[ @as ExpGui.GuiData ]]
return GuiData._scopes[scope] --[[ @as GuiData ]]
end
--- Used to clean up data from destroyed elements

View File

@@ -1,18 +1,24 @@
--- @class ExpGui
local ExpGui = require("modules/exp_gui")
--- @class Gui
local Gui = require("modules/exp_gui")
--- @class ExpGui.elements
local elements = {}
ExpGui.elements = elements
--- @class Gui.elements
local Elements = {}
Gui.elements = Elements
--- @class Gui.elements.aligned_flow.opts
--- @field horizontal_align ("left" | "center" | "right")?
--- @field vertical_align ("top" | "center" | "bottom")?
--- A flow which aligns its content as specified
elements.aligned_flow = ExpGui.element("aligned_flow")
--- @class Gui.elements.aligned_flow: ExpElement
--- @overload fun(parent: LuaGuiElement, opts: Gui.elements.aligned_flow.opts?): LuaGuiElement
Elements.aligned_flow = Gui.define("aligned_flow")
:draw{
type = "flow",
name = ExpGui.property_from_arg("name"),
name = Gui.from_argument("name"),
}
:style(function(def, element, parent, opts)
opts = opts or {}
opts = opts or {} --- @cast opts Gui.elements.aligned_flow.opts
local vertical_align = opts.vertical_align or "center"
local horizontal_align = opts.horizontal_align or "right"
return {
@@ -22,15 +28,18 @@ elements.aligned_flow = ExpGui.element("aligned_flow")
vertically_stretchable = vertical_align ~= "center",
horizontally_stretchable = horizontal_align ~= "center",
}
end)
end) --[[ @as any ]]
--- A solid horizontal white bar element
elements.bar = ExpGui.element("bar")
--- @class Gui.elements.bar: ExpElement
--- @overload fun(parent: LuaGuiElement, width: number?): LuaGuiElement
Elements.bar = Gui.define("bar")
:draw{
type = "progressbar",
value = 1,
}
:style(function(def, element, parent, width)
--- @cast width number?
local style = element.style
style.color = { r = 255, g = 255, b = 255 }
style.height = 4
@@ -39,50 +48,58 @@ elements.bar = ExpGui.element("bar")
else
style.horizontally_stretchable = true
end
end)
end) --[[ @as any ]]
--- A label which is centered
elements.centered_label = ExpGui.element("centered_label")
--- @class Gui.elements.centered_label: ExpElement
--- @overload fun(parent: LuaGuiElement, width: number, caption: LocalisedString, tooltip: LocalisedString?): LuaGuiElement
Elements.centered_label = Gui.define("centered_label")
:draw{
type = "label",
caption = ExpGui.property_from_arg(2),
tooltip = ExpGui.property_from_arg(3),
caption = Gui.from_argument(2),
tooltip = Gui.from_argument(3),
}
:style{
horizontal_align = "center",
single_line = false,
width = ExpGui.property_from_arg(1),
}
width = Gui.from_argument(1),
} --[[ @as any ]]
--- A label which has two white bars on either side of it
elements.title_label = ExpGui.element("title_label")
--- @class Gui.elements.title_label: ExpElement
--- @overload fun(parent: LuaGuiElement, width: number, caption: LocalisedString, tooltip: LocalisedString?): LuaGuiElement
Elements.title_label = Gui.define("title_label")
:draw(function(def, parent, width, caption, tooltip)
local flow =
parent.add{
type = "flow"
}
--- @cast width number
--- @cast caption LocalisedString
--- @cast tooltip LocalisedString?
local flow = parent.add{
type = "flow"
}
flow.style.vertical_align = "center"
elements.bar(flow, width)
local label =
flow.add{
type = "label",
style = "frame_title",
caption = caption,
tooltip = tooltip,
}
Elements.bar(flow, width)
elements.bar(flow)
local label = flow.add{
type = "label",
style = "frame_title",
caption = caption,
tooltip = tooltip,
}
Elements.bar(flow)
return label
end)
end) --[[ @as any ]]
--- A fixed size vertical scroll pane
elements.scroll_pane = ExpGui.element("scroll_pane")
--- @class Gui.elements.scroll_pane: ExpElement
--- @overload fun(parent: LuaGuiElement, maximal_height: number, name: string?): LuaGuiElement
Elements.scroll_pane = Gui.define("scroll_pane")
:draw{
type = "scroll-pane",
name = ExpGui.property_from_arg(2),
name = Gui.from_argument(2),
direction = "vertical",
horizontal_scroll_policy = "never",
vertical_scroll_policy = "auto",
@@ -90,14 +107,19 @@ elements.scroll_pane = ExpGui.element("scroll_pane")
}
:style{
padding = { 1, 3 },
maximal_height = ExpGui.property_from_arg(1),
maximal_height = Gui.from_argument(1),
horizontally_stretchable = true,
}
} --[[ @as any ]]
--- A fixed size vertical scroll pane containing a table
elements.scroll_table = ExpGui.element("scroll_table")
:draw(function(def, parent, height, column_count, scroll_name)
local scroll_pane = elements.scroll_pane(parent, height, scroll_name)
--- @class Gui.elements.scroll_table: ExpElement
--- @overload fun(parent: LuaGuiElement, maximal_height: number, column_count: number, scroll_name: string?): LuaGuiElement
Elements.scroll_table = Gui.define("scroll_table")
:draw(function(def, parent, maximal_height, column_count, scroll_name)
--- @cast maximal_height number
--- @cast column_count number
--- @cast scroll_name string?
local scroll_pane = Elements.scroll_pane(parent, maximal_height, scroll_name)
return scroll_pane.add{
type = "table",
@@ -106,25 +128,25 @@ elements.scroll_table = ExpGui.element("scroll_table")
}
end)
:style{
padding = 0,
cell_padding = 0,
padding = { 3, 2 },
cell_padding = 1,
vertical_align = "center",
horizontally_stretchable = true,
}
} --[[ @as any ]]
--- A container frame
elements.container = ExpGui.element("container")
:draw(function(def, parent, width, container_name)
local container =
parent.add{
type = "frame",
name = container_name,
}
--- @class Gui.elements.container: ExpElement
--- @overload fun(parent: LuaGuiElement, minimal_width: number?, name: string?): LuaGuiElement
Elements.container = Gui.define("container")
:draw(function(def, parent, minimal_width, name)
--- @cast minimal_width number?
--- @cast name string?
local container = parent.add{
type = "frame",
name = name,
}
local style = container.style
style.horizontally_stretchable = false
style.minimal_width = width
style.padding = 2
container.style.padding = 2
return container.add{
type = "frame",
@@ -135,59 +157,176 @@ elements.container = ExpGui.element("container")
end)
:style{
vertically_stretchable = false,
}
horizontally_stretchable = false,
minimal_width = Gui.from_argument(1),
} --[[ @as any ]]
--- Get the root element of a container
--- @param container LuaGuiElement
--- @return LuaGuiElement
function Elements.container.get_root_element(container)
return container.parent
end
--- A frame within a container
elements.subframe_base = ExpGui.element("container_subframe")
--- @class Gui.elements.subframe_base: ExpElement
--- @overload fun(parent: LuaGuiElement, style: string, name: string?): LuaGuiElement
Elements.subframe_base = Gui.define("container_subframe")
:draw{
type = "frame",
name = ExpGui.property_from_arg(2),
style = ExpGui.property_from_arg(1),
name = Gui.from_argument(2),
style = Gui.from_argument(1),
}
:style{
height = 0,
minimal_height = 36,
padding = { 3, 4 },
padding = { 3, 6, 0, 6 },
use_header_filler = false,
horizontally_stretchable = true,
}
} --[[ @as any ]]
--- @class Gui.elements.header.opts
--- @field name string?
--- @field caption LocalisedString?
--- @field tooltip LocalisedString?
--- A header frame within a container
elements.header = ExpGui.element("container_header")
--- @class Gui.elements.header: ExpElement
--- @field label LuaGuiElement
--- @overload fun(parent: LuaGuiElement, opts: Gui.elements.header.opts?): LuaGuiElement
Elements.header = Gui.define("container_header")
:draw(function(def, parent, opts)
opts = opts or {}
local subframe = elements.subframe_base(parent, "subheader_frame", opts.name)
opts = opts or {} --- @cast opts Gui.elements.header.opts
local subframe = Elements.subframe_base(parent, "subheader_frame", opts.name)
if opts.caption then
subframe.add{
type = "label",
name = opts.label_name,
name = "label",
caption = opts.caption,
tooltip = opts.tooltip,
style = "frame_title",
}
end
return opts.no_flow and subframe or elements.aligned_flow(subframe, { name = "flow" })
end)
subframe.add{ type = "empty-widget" }.style.horizontally_stretchable = true
return subframe
end) --[[ @as any ]]
--- @class Gui.elements.footer.opts
--- @field name string?
--- @field caption LocalisedString?
--- @field tooltip LocalisedString?
--- A footer frame within a container
elements.footer = ExpGui.element("container_footer")
--- @class Gui.elements.footer: ExpElement
--- @field label LuaGuiElement
--- @overload fun(parent: LuaGuiElement, opts: Gui.elements.footer.opts?): LuaGuiElement
Elements.footer = Gui.define("container_footer")
:draw(function(def, parent, opts)
opts = opts or {}
local subframe = elements.subframe_base(parent, "subfooter_frame", opts.name)
opts = opts or {} --- @cast opts Gui.elements.footer.opts
local subframe = Elements.subframe_base(parent, "subfooter_frame", opts.name)
if opts.caption then
subframe.add{
type = "label",
name = opts.label_name,
name = "label",
caption = opts.caption,
tooltip = opts.tooltip,
style = "frame_title",
}
end
return opts.no_flow and subframe or elements.aligned_flow(subframe, { name = "flow" })
end)
subframe.add{ type = "empty-widget" }.style.horizontally_stretchable = true
return elements
return subframe
end) --[[ @as any ]]
--- A button used to destroy its target when clicked, intended for screen frames
--- @class Gui.elements.screen_frame_close: ExpElement
--- @field data table<LuaGuiElement, LuaGuiElement>
--- @overload fun(parent: LuaGuiElement, target: LuaGuiElement): LuaGuiElement
Elements.screen_frame_close = Gui.define("screen_frame_close")
:draw{
type = "sprite-button",
style = "frame_action_button",
sprite = "utility/close",
}
:element_data(
Gui.from_argument(1)
)
:on_click(function(def, player, element, event)
--- @cast def Gui.elements.screen_frame_close
Gui.destroy_if_valid(def.data[element])
end) --[[ @as any ]]
--- A draggable frame with close button and button flow
--- @class Gui.elements.screen_frame: ExpElement
--- @field data table<LuaGuiElement, LuaGuiElement?>
--- @overload fun(parent: LuaGuiElement, caption: LocalisedString?, button_flow: boolean?): LuaGuiElement
Elements.screen_frame = Gui.define("screen_frame")
:draw(function(def, parent, caption, button_flow)
local container = parent.add{
type = "frame",
direction = "vertical",
}
container.style.padding = 2
local header = container.add{
type = "flow",
}
if caption then
local label = header.add{
type = "label",
caption = caption,
style = "frame_title"
}
label.style.top_margin = -3
label.style.bottom_padding = 3
end
local filler = header.add{
type = "empty-widget",
style = "draggable_space_header",
}
filler.drag_target = container
local filler_style = filler.style
filler_style.horizontally_stretchable = true
filler_style.vertically_stretchable = true
filler_style.left_margin = caption and 4 or 0
filler_style.natural_height = 24
filler_style.height = 24
if button_flow then
local _button_flow = header.add{ type = "flow" }
def.data[container] = _button_flow
_button_flow.style.padding = 0
end
Elements.screen_frame_close(header, container)
return container.add{
type = "frame",
direction = "vertical",
style = "inside_shallow_frame_packed",
}
end) --[[ @as any ]]
--- Get the button flow for a screen frame
--- @param screen_frame LuaGuiElement
--- @return LuaGuiElement
function Elements.screen_frame.get_button_flow(screen_frame)
return assert(Elements.screen_frame.data[screen_frame.parent], "Screen frame has no button flow")
end
--- Get the root element of a screen frame
--- @param screen_frame LuaGuiElement
--- @return LuaGuiElement
function Elements.screen_frame.get_root_element(screen_frame)
return screen_frame.parent
end
return Elements

View File

@@ -1,12 +1,12 @@
--[[-- ExpGui - GuiData
--[[-- Gui - GuiData
Provides a method of storing elements created for a player and provide a global iterator for them.
]]
local ExpUtil = require("modules/exp_util")
local Storage = require("modules/exp_util/storage")
--- @alias ExpGui_GuiIter.FilterType LuaPlayer | LuaForce | LuaPlayer[] | nil
--- @alias ExpGui_GuiIter.ReturnType fun(): LuaPlayer?, LuaGuiElement?
--- @alias GuiIter.FilterType LuaPlayer | LuaForce | LuaPlayer[] | nil
--- @alias GuiIter.ReturnType fun(): LuaPlayer?, LuaGuiElement?
--- @type table<string, table<uint, table<uint, LuaGuiElement>>>
local registered_scopes = {}
@@ -23,7 +23,7 @@ Storage.register({
registration_numbers = tbl.registration_numbers
end)
--- @class ExpGui_GuiIter
--- @class GuiIter
local GuiIter = {
_scopes = registered_scopes,
}
@@ -76,7 +76,7 @@ end
--- Iterate over all valid elements for a player
--- @param scope string
--- @param player LuaPlayer
--- @return ExpGui_GuiIter.ReturnType
--- @return GuiIter.ReturnType
function GuiIter.player_elements(scope, player)
if not player.valid then return no_loop end
@@ -98,7 +98,7 @@ end
--- @param scope string
--- @param players LuaPlayer[]
--- @param online boolean?
--- @return ExpGui_GuiIter.ReturnType
--- @return GuiIter.ReturnType
function GuiIter.filtered_elements(scope, players, online)
local scope_elements = registered_scopes[scope]
if not scope_elements then return no_loop end
@@ -125,7 +125,7 @@ end
--- Iterate over all valid elements
--- @param scope string
--- @return ExpGui_GuiIter.ReturnType
--- @return GuiIter.ReturnType
function GuiIter.all_elements(scope)
local scope_elements = registered_scopes[scope]
if not scope_elements then return no_loop end
@@ -161,8 +161,8 @@ end
--- Iterate over all valid gui elements for all players
--- @param scope string
--- @param filter ExpGui_GuiIter.FilterType
--- @return ExpGui_GuiIter.ReturnType
--- @param filter GuiIter.FilterType
--- @return GuiIter.ReturnType
function GuiIter.get_tracked_elements(scope, filter)
local class_name = ExpUtil.get_class_name(filter)
if class_name == "nil" then
@@ -184,8 +184,8 @@ end
--- Iterate over all valid gui elements for all online players
--- @param scope string
--- @param filter ExpGui_GuiIter.FilterType
--- @return ExpGui_GuiIter.ReturnType
--- @param filter GuiIter.FilterType
--- @return GuiIter.ReturnType
function GuiIter.get_online_elements(scope, filter)
local class_name = ExpUtil.get_class_name(filter)
if class_name == "nil" then

View File

@@ -4,7 +4,7 @@ local ExpUtil = require("modules/exp_util")
local GuiData = require("modules/exp_gui/data")
local GuiIter = require("modules/exp_gui/iter")
--- @class ExpGui_ExpElement
--- @class _ExpElement
local ExpElement = {
_elements = {}, --- @type table<string, ExpElement>
}
@@ -13,14 +13,14 @@ ExpElement.events = {}
--- @alias ExpElement.DrawCallback fun(def: ExpElement, parent: LuaGuiElement, ...): LuaGuiElement?, function?
--- @alias ExpElement.PostDrawCallback fun(def: ExpElement, element: LuaGuiElement?, parent: LuaGuiElement, ...): table?
--- @alias ExpElement.PostDrawCallbackAdder fun(self: ExpElement, definition: table | ExpElement.PostDrawCallback): ExpElement
--- @alias ExpElement.PostDrawCallbackAdder fun(self: ExpElement, definition: table | number | string | boolean | ExpElement.PostDrawCallback): ExpElement
--- @alias ExpElement.EventHandler<E> fun(def: ExpElement, player: LuaPlayer, element: LuaGuiElement, event: E)
--- @alias ExpElement.OnEventAdder<E> fun(self: ExpElement, handler: fun(def: ExpElement, player: LuaPlayer, element: LuaGuiElement, event: E)): ExpElement
--- @class ExpElement._debug
--- @field defined_at string
--- @field draw_definition table?
--- @field draw_from_args table?
--- @field draw_signals table?
--- @field style_definition table?
--- @field style_from_args table?
--- @field element_data_definition table?
@@ -34,7 +34,7 @@ ExpElement.events = {}
--- @class ExpElement
--- @field name string
--- @field data ExpGui.GuiData
--- @field data GuiData
--- @field _debug ExpElement._debug
--- @field _draw ExpElement.DrawCallback?
--- @field _style ExpElement.PostDrawCallback?
@@ -55,40 +55,95 @@ ExpElement._metatable = {
__class = "ExpElement",
}
--- Used to signal the intentional lack of a return value from draw
--- @return nil, function
function ExpElement.no_return()
return nil, ExpElement.no_return
end
--- Used to signal that the property should be the same as the define name
--- @return function
function ExpElement.property_from_name()
return ExpElement.property_from_name
function ExpElement.from_name()
return ExpElement.from_name
end
--- Used to signal that a property should be taken from the arguments, a string means key of last arg
--- @param arg number|string|nil
--- @return [function, number|string|nil]
function ExpElement.property_from_arg(arg)
return { ExpElement.property_from_arg, arg }
--- @generic A: number|string, D: any
--- @param arg A
--- @param default D?
--- @return [function, A, D]
function ExpElement.from_argument(arg, default)
return { ExpElement.from_argument, assert(arg), default }
end
--- @alias ExpElement._signals table<string|number, [string, any]> | [function, string|number, any|nil]
--- Extract the from args properties from a definition
--- @param definition table
--- @return table<string|number, string>
--- @return ExpElement._signals
function ExpElement._prototype:_extract_signals(definition)
local from_args = {}
for k, v in pairs(definition) do
if v == ExpElement.property_from_arg then
from_args[#from_args + 1] = k
elseif v == ExpElement.property_from_name then
definition[k] = self.name
elseif type(v) == "table" and rawget(v, 1) == ExpElement.property_from_arg then
from_args[v[2] or (#from_args + 1)] = k
-- Check if the definition is from_argument
if definition[1] == ExpElement.from_argument then
return definition
end
-- Otherwise check if any of the values are from_argument or from_name
local signals = {}
for prop, value in pairs(definition) do
if value == ExpElement.from_argument then
error("ExpElement.from_argument must be called with an argument index / key")
elseif value == ExpElement.from_name then
definition[prop] = self.name
elseif type(value) == "table" and rawget(value, 1) == ExpElement.from_argument then
local key = value[2] or (#signals + 1)
signals[key] = { prop, value[3] }
end
end
return from_args
return signals
end
--- Apply the previously extracted signals to a definition using the create args
--- @param definition table
--- @param signals ExpElement._signals
--- @param args table
--- @return any
function ExpElement._prototype:_apply_signals(definition, signals, args)
local last = args[#args] or args -- 'or args' used instead of empty table
-- Check if the root is from_argument
if signals[1] == ExpElement.from_argument then
local key, rtn = signals[2], nil
if type(key) == "string" then
rtn = last[key]
else
rtn = args[key]
end
if rtn == nil then
return signals[3]
end
return rtn
end
-- Otherwise set the properties of the definition
for i, pair in pairs(signals) do
local key = pair[1]
if type(i) == "string" then
definition[key] = last[i]
else
definition[key] = args[i]
end
if definition[key] == nil then
definition[key] = pair[2]
end
end
return definition
end
--- Register a new instance of a prototype
--- @param name string
--- @return ExpElement
function ExpElement.create(name)
function ExpElement.new(name)
ExpUtil.assert_not_runtime()
local module_name = ExpUtil.get_module_name(2)
local element_name = module_name .. "/" .. name
@@ -103,7 +158,7 @@ function ExpElement.create(name)
},
}
ExpElement._elements[element_name] = instance
ExpElement._elements[element_name] = instance --[[ @as ExpElement ]]
return setmetatable(instance, ExpElement._metatable)
end
@@ -122,11 +177,14 @@ function ExpElement._prototype:create(parent, ...)
assert(self._draw, "Element does not have a draw definition")
local element, status = self:_draw(parent, ...)
local player = assert(game.get_player(parent.player_index))
if status ~= ExpElement.no_return then
assert(element and element.object_name == "LuaGuiElement", "Draw did not return a LuaGuiElement")
end
if self._style then
assert(element, "Cannot set style when no element was returned by draw definition")
local style = self:_style(element, parent, ...)
if style then
assert(element, "Cannot set style when no element was returned by draw definition")
local element_style = element.style
for k, v in pairs(style) do
element_style[k] = v
@@ -135,23 +193,23 @@ function ExpElement._prototype:create(parent, ...)
end
if self._element_data then
assert(element, "Cannot set element data when no element was returned by draw definition")
local data = self:_element_data(element, parent, ...)
if data then
assert(element, "Cannot set element data when no element was returned by draw definition")
if data ~= nil and self.data[element] == nil then
self.data[element] = data
end
end
if self._player_data then
local data = self:_player_data(element, parent, ...)
if data then
if data ~= nil and self.data[player] == nil then
self.data[player] = data
end
end
if self._force_data then
local data = self:_force_data(element, parent, ...)
if data then
if data ~= nil and self.data[player.force] == nil then
self.data[player.force] = data
end
end
@@ -161,7 +219,9 @@ function ExpElement._prototype:create(parent, ...)
if data then
local global_data = self.data.global_data
for k, v in pairs(data) do
global_data[k] = v
if global_data[k] == nil then
global_data[k] = v
end
end
end
end
@@ -189,8 +249,8 @@ end
--- Add a temp draw definition, useful for when working top down
--- @return ExpElement
function ExpElement._prototype:dev()
log("Dev draw used for " .. self.name)
function ExpElement._prototype:empty()
log("empty draw used for " .. self.name)
return self:draw{
type = "flow"
}
@@ -203,71 +263,67 @@ end
--- @return ExpElement
function ExpElement._prototype:draw(definition)
ExpUtil.assert_not_runtime()
if type(definition) == "function" then
if type(definition) == "function" and definition ~= ExpElement.from_argument then
self._draw = definition
return self
end
assert(type(definition) == "table", "Definition is not a table or function")
local from_args = self:_extract_signals(definition)
local signals = self:_extract_signals(definition)
self._debug.draw_definition = definition
if not next(from_args) then
if not next(signals) then
-- If no signals then skip var arg
self._draw = function(_, parent)
return parent.add(definition)
end
return self
end
self._debug.draw_from_args = from_args
self._debug.draw_signals = signals
self._draw = function(_, parent, ...)
local args = { ... }
local last = args[#args] or args
-- 'or args' used instead of empty table
for i, k in pairs(from_args) do
if type(i) == "string" then
definition[k] = last[i]
else
definition[k] = args[i]
end
end
return parent.add(definition)
return parent.add(self:_apply_signals(definition, signals, { ... }))
end
return self
end
--- Create a definition adder for anything other than draaw
--- Create a definition adder for anything other than draw
--- @param prop_name string
--- @param debug_def string
--- @param debug_args string
--- @param debug_signals string
--- @return ExpElement.PostDrawCallbackAdder
local function definition_factory(prop_name, debug_def, debug_args)
local function definition_factory(prop_name, debug_def, debug_signals)
return function(self, definition)
ExpUtil.assert_not_runtime()
if type(definition) == "function" then
if type(definition) == "function" and definition ~= ExpElement.from_argument then
self[prop_name] = definition
return self
end
assert(type(definition) == "table", "Definition is not a table or function")
local from_args = self:_extract_signals(definition)
self._debug[debug_def] = definition
if #from_args == 0 then
if type(definition) ~= "table" and definition ~= ExpElement.from_argument then
-- Primitive value so we can just return it
self[prop_name] = function(_, _, _)
return definition
end
return self
end
self._debug[debug_args] = from_args
self[prop_name] = function(_, _, _, ...)
local args = { ... }
for i, k in pairs(from_args) do
definition[k] = args[i]
assert(type(definition) == "table", "Definition is not a table or function")
local signals = self:_extract_signals(definition)
self._debug[debug_def] = definition
if not next(signals) then
-- If no signals then skip var arg
self[prop_name] = function(_, _, _)
return table.deep_copy(definition)
end
return definition
return self
end
self._debug[debug_signals] = signals
self[prop_name] = function(_, _, _, ...)
return self:_apply_signals(table.deep_copy(definition), signals, { ... })
end
return self
@@ -276,34 +332,34 @@ end
--- Set the style definition
--- @type ExpElement.PostDrawCallbackAdder
ExpElement._prototype.style = definition_factory("_style", "style_definition", "style_from_args")
ExpElement._prototype.style = definition_factory("_style", "style_definition", "style_signals")
--- Set the default element data
--- @type ExpElement.PostDrawCallbackAdder
ExpElement._prototype.element_data = definition_factory("_element_data", "element_data_definition", "element_data_from_args")
ExpElement._prototype.element_data = definition_factory("_element_data", "element_data_definition", "element_data_signals")
--- Set the default player data
--- @type ExpElement.PostDrawCallbackAdder
ExpElement._prototype.player_data = definition_factory("_player_data", "player_data_definition", "player_data_from_args")
ExpElement._prototype.player_data = definition_factory("_player_data", "player_data_definition", "player_data_signals")
--- Set the default force data
--- @type ExpElement.PostDrawCallbackAdder
ExpElement._prototype.force_data = definition_factory("_force_data", "force_data_definition", "force_data_from_args")
ExpElement._prototype.force_data = definition_factory("_force_data", "force_data_definition", "force_data_signals")
--- Set the default global data
--- @type ExpElement.PostDrawCallbackAdder
ExpElement._prototype.global_data = definition_factory("_global_data", "global_data_definition", "global_data_from_args")
ExpElement._prototype.global_data = definition_factory("_global_data", "global_data_definition", "global_data_signals")
--- Iterate the tracked elements of all players
--- @param filter ExpGui_GuiIter.FilterType
--- @return ExpGui_GuiIter.ReturnType
--- @param filter GuiIter.FilterType
--- @return GuiIter.ReturnType
function ExpElement._prototype:tracked_elements(filter)
return GuiIter.get_tracked_elements(self.name, filter)
end
--- Iterate the tracked elements of all online players
--- @param filter ExpGui_GuiIter.FilterType
--- @return ExpGui_GuiIter.ReturnType
--- @param filter GuiIter.FilterType
--- @return GuiIter.ReturnType
function ExpElement._prototype:online_elements(filter)
return GuiIter.get_online_elements(self.name, filter)
end
@@ -337,10 +393,10 @@ function ExpElement._prototype:link_element(element)
element_tags = {}
end
local event_tags = element_tags["ExpGui"]
local event_tags = element_tags["Gui"]
if not event_tags then
event_tags = {}
element_tags["ExpGui"] = event_tags
element_tags["Gui"] = event_tags
end
--- @cast event_tags string[]
@@ -363,10 +419,10 @@ function ExpElement._prototype:unlink_element(element)
return element, ExpElement._prototype.unlink_element
end
local event_tags = element_tags["ExpGui"]
local event_tags = element_tags["Gui"]
if not event_tags then
event_tags = {}
element_tags["ExpGui"] = event_tags
element_tags["Gui"] = event_tags
end
--- @cast event_tags string[]
@@ -381,7 +437,7 @@ local function event_handler(event)
local element = event.element
if not element or not element.valid then return end
local event_tags = element.tags and element.tags["ExpGui"]
local event_tags = element.tags and element.tags["Gui"]
if not event_tags then return end
--- @cast event_tags string[]

View File

@@ -1,9 +1,9 @@
--- @class ExpGui
local ExpGui = require("modules/exp_gui")
--- @class Gui
local Gui = require("modules/exp_gui")
--- @class ExpGui.styles
--- @class Gui.styles
local styles = {}
ExpGui.styles = styles
Gui.styles = styles
function styles.sprite(style)
style = style or {}

View File

@@ -1,6 +1,6 @@
--- @class ExpGui
local ExpGui = require("modules/exp_gui")
--- @class Gui
local Gui = require("modules/exp_gui")
local ExpElement = require("modules/exp_gui/prototype")
local mod_gui = require("mod-gui")
@@ -9,9 +9,9 @@ local toolbar_button_active_style = "menu_button_continue"
local toolbar_button_size = 36
local toolbar_button_small = 20
--- @class ExpGui.toolbar
--- @class Gui.toolbar
local Toolbar = {}
ExpGui.toolbar = Toolbar
Gui.toolbar = Toolbar
local elements = {}
Toolbar.elements = elements
@@ -20,13 +20,27 @@ local toolbar_buttons = {} --- @type ExpElement[]
local left_elements_with_button = {} --- @type table<ExpElement, ExpElement>
local buttons_with_left_element = {} --- @type table<string, ExpElement>
--- Called when toolbar button toggle state is changed.
Toolbar.on_gui_button_toggled = script.generate_event_name()
--- @class (exact) EventData.on_gui_button_toggled: EventData
--- @field element LuaGuiElement
--- @field state boolean
--- @class _ExpElement._prototype
--- @field on_button_toggled ExpElement.OnEventAdder<EventData.on_gui_button_toggled>
--- @diagnostic disable-next-line: invisible, inject-field
function ExpElement._prototype.on_button_toggled(self, handler)
return self:on_event(Toolbar.on_gui_button_toggled, handler)
end
--- Set the visible state of the toolbar
--- @param player LuaPlayer
--- @param state boolean? toggles if nil
--- @return boolean
function Toolbar.set_visible_state(player, state)
-- Update the top flow
local top_flow = assert(ExpGui.get_top_flow(player).parent)
local top_flow = assert(Gui.get_top_flow(player).parent)
if state == nil then state = not top_flow.visible end
top_flow.visible = state
@@ -47,33 +61,46 @@ end
--- @param player LuaPlayer
--- @return boolean
function Toolbar.get_visible_state(player)
local top_flow = assert(ExpGui.get_top_flow(player).parent)
local top_flow = assert(Gui.get_top_flow(player).parent)
return top_flow.visible
end
--- Set the toggle state of a toolbar button, does not check for a linked left element
--- Set the toggle state of a toolbar button
--- @param define ExpElement
--- @param player LuaPlayer
--- @param state boolean? toggles if nil
--- @param _from_left boolean?
--- @return boolean
function Toolbar.set_button_toggled_state(define, player, state)
local top_element = assert(ExpGui.get_top_element(define, player), "Element is not on the top flow")
function Toolbar.set_button_toggled_state(define, player, state, _from_left)
local top_element = assert(Gui.get_top_element(define, player), "Element is not on the top flow")
if state == nil then state = top_element.style.name == toolbar_button_default_style end
for _, element in define:tracked_elements(player) do
local original_width, original_height = element.style.minimal_width, element.style.maximal_height
element.style = state and toolbar_button_active_style or toolbar_button_default_style
local left_element = buttons_with_left_element[define.name]
if left_element and not _from_left then
return Toolbar.set_left_element_visible_state(left_element, player, state)
end
for _, button in define:tracked_elements(player) do
local original_width, original_height = button.style.minimal_width, button.style.maximal_height
button.style = state and toolbar_button_active_style or toolbar_button_default_style
-- Make the extra required adjustments
local style = element.style
local style = button.style
style.minimal_width = original_width
style.maximal_height = original_height
if element.type == "sprite-button" then
if button.type == "sprite-button" then
style.padding = -2
else
style.font = "default-semibold"
style.padding = 0
end
script.raise_event(Toolbar.on_gui_button_toggled, {
name = Toolbar.on_gui_button_toggled,
tick = game.tick,
element = button,
state = state,
})
end
return state
@@ -84,7 +111,7 @@ end
--- @param player LuaPlayer
--- @return boolean
function Toolbar.get_button_toggled_state(define, player)
local element = assert(ExpGui.get_top_element(define, player), "Element is not on the top flow")
local element = assert(Gui.get_top_element(define, player), "Element is not on the top flow")
return element.style.name == toolbar_button_active_style
end
@@ -95,14 +122,14 @@ end
--- @param _skip_consistency boolean?
--- @return boolean
function Toolbar.set_left_element_visible_state(define, player, state, _skip_consistency)
local element = assert(ExpGui.get_left_element(define, player), "Element is not on the left flow")
local element = assert(Gui.get_left_element(define, player), "Element is not on the left flow")
if state == nil then state = not element.visible end
element.visible = state
-- Check if there is a linked toolbar button and update it
local button = left_elements_with_button[define]
if button then
Toolbar.set_button_toggled_state(button, player, state)
Toolbar.set_button_toggled_state(button, player, state, true)
end
-- This check is O(n^2) when setting all left elements to hidden, so internals can it
@@ -122,7 +149,7 @@ end
--- @param player LuaPlayer
--- @return boolean
function Toolbar.get_left_element_visible_state(define, player)
local element = assert(ExpGui.get_left_element(define, player), "Element is not on the left flow")
local element = assert(Gui.get_left_element(define, player), "Element is not on the left flow")
return element.visible
end
@@ -130,11 +157,11 @@ end
--- @param player any
--- @return boolean
function Toolbar.has_visible_buttons(player)
local top_flow = ExpGui.get_top_flow(player)
local settings_button = ExpGui.get_top_element(elements.close_toolbar, player)
local top_flow = Gui.get_top_flow(player)
local settings_button = Gui.get_top_element(elements.close_toolbar, player)
for _, element in pairs(top_flow.children) do
if element.visible and element ~= settings_button then
for _, top_element in pairs(top_flow.children) do
if top_element.visible and top_element ~= settings_button then
return true
end
end
@@ -146,11 +173,11 @@ end
--- @param player any
--- @return boolean
function Toolbar.has_visible_left_elements(player)
local left_flow = ExpGui.get_left_flow(player)
local core_button_flow = ExpGui.get_left_element(elements.core_button_flow, player)
local left_flow = Gui.get_left_flow(player)
local core_button_flow = Gui.get_left_element(elements.core_button_flow, player)
for _, element in pairs(left_flow.children) do
if element.visible and element ~= core_button_flow then
for _, left_element in pairs(left_flow.children) do
if left_element.visible and left_element ~= core_button_flow then
return true
end
end
@@ -158,14 +185,14 @@ function Toolbar.has_visible_left_elements(player)
return false
end
--- @class ExpGui.toolbar.create_button__param: LuaGuiElement.add_param.sprite_button, LuaGuiElement.add_param.button
--- @class Gui.toolbar.create_button__param: LuaGuiElement.add_param.sprite_button, LuaGuiElement.add_param.button
--- @field name string
--- @field type nil
--- @field left_element ExpElement| nil
--- @field visible ExpGui.VisibleCallback | boolean | nil
--- @field visible Gui.VisibleCallback | boolean | nil
--- Create a toolbar button
--- @param options ExpGui.toolbar.create_button__param
--- @param options Gui.toolbar.create_button__param
--- @return ExpElement
function Toolbar.create_button(options)
-- Extract the custom options from the element.add options
@@ -187,7 +214,7 @@ function Toolbar.create_button(options)
end
-- Create the new element define
local toolbar_button = ExpGui.element(name)
local toolbar_button = Gui.define(name)
:track_all_elements()
:draw(options)
:style{
@@ -215,12 +242,12 @@ function Toolbar.create_button(options)
-- Add the define to the top flow and return
toolbar_buttons[#toolbar_buttons + 1] = toolbar_button
ExpGui.add_top_element(toolbar_button, visible)
Gui.add_top_element(toolbar_button, visible)
return toolbar_button
end
--- Toggles the toolbar settings, RMB will instead hide the toolbar
elements.close_toolbar = ExpGui.element("close_toolbar")
elements.close_toolbar = Gui.define("close_toolbar")
:draw{
type = "sprite-button",
sprite = "utility/preset",
@@ -241,7 +268,7 @@ elements.close_toolbar = ExpGui.element("close_toolbar")
end)
--- Shows the toolbar, if no buttons are visible then it shows the settings instead
elements.open_toolbar = ExpGui.element("open_toolbar")
elements.open_toolbar = Gui.define("open_toolbar")
:track_all_elements()
:draw{
type = "sprite-button",
@@ -263,7 +290,7 @@ elements.open_toolbar = ExpGui.element("open_toolbar")
end)
--- Hides all left elements when clicked
elements.clear_left_flow = ExpGui.element("clear_left_flow")
elements.clear_left_flow = Gui.define("clear_left_flow")
:track_all_elements()
:draw{
type = "sprite-button",
@@ -278,7 +305,7 @@ elements.clear_left_flow = ExpGui.element("clear_left_flow")
}
:on_click(function(def, player, element)
element.visible = false
for define in pairs(ExpGui.left_elements) do
for define in pairs(Gui.left_elements) do
if define ~= elements.core_button_flow then
Toolbar.set_left_element_visible_state(define, player, false, true)
end
@@ -286,7 +313,7 @@ elements.clear_left_flow = ExpGui.element("clear_left_flow")
end)
--- Contains the two buttons on the left flow
elements.core_button_flow = ExpGui.element("core_button_flow")
elements.core_button_flow = Gui.define("core_button_flow")
:draw(function(def, parent)
local flow = parent.add{
type = "flow",
@@ -327,15 +354,15 @@ local function move_toolbar_button(player, item, offset)
list.swap_children(old_index, new_index)
-- Swap the position in the top flow, offset by 1 because of settings button
local top_flow = ExpGui.get_top_flow(player)
local top_flow = Gui.get_top_flow(player)
top_flow.swap_children(old_index + 1, new_index + 1)
-- Check if the element has a left element to move
local left_element = buttons_with_left_element[item.name]
local other_left_element = buttons_with_left_element[other_item.name]
if left_element and other_left_element then
local element = ExpGui.get_left_element(left_element, player)
local other_element = ExpGui.get_left_element(other_left_element, player)
local element = Gui.get_left_element(left_element, player)
local other_element = Gui.get_left_element(other_left_element, player)
local left_index = element.get_index_in_parent()
local other_index = other_element.get_index_in_parent()
element.parent.swap_children(left_index, other_index)
@@ -360,15 +387,15 @@ local function move_toolbar_button(player, item, offset)
end
end
--- @alias ExpGui.ToolbarOrder { name: string, favourite: boolean }[]
--- @alias Gui.ToolbarOrder { name: string, favourite: boolean }[]
--- Reorder the toolbar buttons
--- @param player LuaPlayer
--- @param order ExpGui.ToolbarOrder
--- @param order Gui.ToolbarOrder
function Toolbar.set_order(player, order)
local list = elements.toolbar_settings.data[player] --[[ @as LuaGuiElement ]]
local left_flow = ExpGui.get_left_flow(player)
local top_flow = ExpGui.get_top_flow(player)
local left_flow = Gui.get_left_flow(player)
local top_flow = Gui.get_top_flow(player)
-- Reorder the buttons
local left_index = 1
@@ -380,7 +407,7 @@ function Toolbar.set_order(player, order)
-- Switch the toolbar button order
local element_define = ExpElement.get(item_state.name)
local toolbar_button = ExpGui.get_top_element(element_define, player)
local toolbar_button = Gui.get_top_element(element_define, player)
top_flow.swap_children(index + 1, toolbar_button.get_index_in_parent())
-- Update the children buttons
@@ -392,21 +419,21 @@ function Toolbar.set_order(player, order)
-- Switch the left element order
local left_define = buttons_with_left_element[item_state.name]
if left_define then
local left_element = ExpGui.get_left_element(left_define, player)
local left_element = Gui.get_left_element(left_define, player)
left_flow.swap_children(left_index, left_element.get_index_in_parent())
left_index = left_index + 1
end
end
end
--- @class (exact) ExpGui.ToolbarState
--- @field order ExpGui.ToolbarOrder
--- @class (exact) Gui.ToolbarState
--- @field order Gui.ToolbarOrder
--- @field open string[]
--- @field visible boolean
--- Reorder the toolbar buttons and set the open state of the left flows
--- @param player LuaPlayer
--- @param state ExpGui.ToolbarState
--- @param state Gui.ToolbarState
function Toolbar.set_state(player, state)
Toolbar.set_order(player, state.order)
Toolbar.set_visible_state(player, state.visible)
@@ -420,7 +447,7 @@ function Toolbar.set_state(player, state)
end
-- Make all other elements hidden
for left_element in pairs(ExpGui.left_elements) do
for left_element in pairs(Gui.left_elements) do
if not done[left_element] then
Toolbar.set_left_element_visible_state(left_element, player, false, true)
end
@@ -435,7 +462,7 @@ end
--- Get the full toolbar state for a player
--- @param player LuaPlayer
--- @return ExpGui.ToolbarState
--- @return Gui.ToolbarState
function Toolbar.get_state(player)
-- Get the order of toolbar buttons
local order = {}
@@ -446,7 +473,7 @@ function Toolbar.get_state(player)
-- Get the names of all open left elements
local open, open_index = {}, 1
for left_element in pairs(ExpGui.left_elements) do
for left_element in pairs(Gui.left_elements) do
if Toolbar.get_left_element_visible_state(left_element, player) then
open[open_index] = left_element.name
open_index = open_index + 1
@@ -462,10 +489,10 @@ function Toolbar._create_elements(player)
-- Add any missing items to the gui
local toolbar_list = elements.toolbar_settings.data[player] --[[ @as LuaGuiElement ]]
local previous_last_index = #toolbar_list.children_names
for define in pairs(ExpGui.top_elements) do
for define in pairs(Gui.top_elements) do
if define ~= elements.close_toolbar and toolbar_list[define.name] == nil then
local element = elements.toolbar_list_item(toolbar_list, define)
element.visible = ExpGui.get_top_element(define, player).visible
element.visible = Gui.get_top_element(define, player).visible
end
end
@@ -489,8 +516,8 @@ function Toolbar._ensure_consistency(player)
local list = elements.toolbar_settings.data[player] --[[ @as LuaGuiElement ]]
for _, button in ipairs(toolbar_buttons) do
-- Update the visible state based on if the player is allowed the button
local element = ExpGui.get_top_element(button, player)
local allowed = ExpGui.top_elements[button]
local element = Gui.get_top_element(button, player)
local allowed = Gui.top_elements[button]
if type(allowed) == "function" then
allowed = allowed(player, element)
end
@@ -500,9 +527,10 @@ function Toolbar._ensure_consistency(player)
-- Update the toggle state and hide the linked left element if the button is not allowed
local left_define = buttons_with_left_element[button.name]
if left_define then
local left_element = ExpGui.get_left_element(left_define, player)
Toolbar.set_button_toggled_state(button, player, left_element.visible)
if not allowed then
if allowed then
local left_element = Gui.get_left_element(left_define, player)
Toolbar.set_button_toggled_state(button, player, left_element.visible, true)
else
Toolbar.set_left_element_visible_state(left_define, player, false)
end
end
@@ -515,7 +543,7 @@ function Toolbar._ensure_consistency(player)
end
-- Update open_toolbar
local top_flow = assert(ExpGui.get_top_flow(player).parent)
local top_flow = assert(Gui.get_top_flow(player).parent)
for _, open_toolbar in elements.open_toolbar:tracked_elements(player) do
open_toolbar.visible = not top_flow.visible
end
@@ -528,15 +556,15 @@ function Toolbar._ensure_consistency(player)
end
do
local default_order --- @type ExpGui.ToolbarOrder
local default_order --- @type Gui.ToolbarOrder
--- Gets the default order for the toolbar
--- @return ExpGui.ToolbarOrder
--- @return Gui.ToolbarOrder
function Toolbar.get_default_order()
if default_order then return default_order end
local index = 1
default_order = {}
for define in pairs(ExpGui.top_elements) do
for define in pairs(Gui.top_elements) do
if define ~= elements.close_toolbar then
default_order[index] = { name = define.name, favourite = true }
index = index + 1
@@ -548,7 +576,7 @@ do
end
--- Toggle the visibility of the toolbar, does not care if buttons are visible
elements.toggle_toolbar = ExpGui.element("toggle_toolbar")
elements.toggle_toolbar = Gui.define("toggle_toolbar")
:track_all_elements()
:draw{
type = "sprite-button",
@@ -557,7 +585,7 @@ elements.toggle_toolbar = ExpGui.element("toggle_toolbar")
style = "tool_button",
auto_toggle = true,
}
:style(ExpGui.styles.sprite{
:style(Gui.styles.sprite{
size = 22,
})
:on_click(function(def, player, element)
@@ -565,14 +593,14 @@ elements.toggle_toolbar = ExpGui.element("toggle_toolbar")
end)
--- Reset the toolbar to its default state
elements.reset_toolbar = ExpGui.element("reset_toolbar")
elements.reset_toolbar = Gui.define("reset_toolbar")
:draw{
type = "sprite-button",
sprite = "utility/reset",
style = "shortcut_bar_button_red",
tooltip = { "exp-gui_toolbar-settings.reset" },
}
:style(ExpGui.styles.sprite{
:style(Gui.styles.sprite{
size = 22,
padding = -1,
})
@@ -581,13 +609,13 @@ elements.reset_toolbar = ExpGui.element("reset_toolbar")
end)
--- Move an item up/left on the toolbar
elements.move_item_up = ExpGui.element("move_item_up")
elements.move_item_up = Gui.define("move_item_up")
:draw{
type = "sprite-button",
sprite = "utility/speed_up",
tooltip = { "exp-gui_toolbar-settings.move-up" },
}
:style(ExpGui.styles.sprite{
:style(Gui.styles.sprite{
size = toolbar_button_small,
})
:on_click(function(def, player, element)
@@ -596,13 +624,13 @@ elements.move_item_up = ExpGui.element("move_item_up")
end)
--- Move an item down/right on the toolbar
elements.move_item_down = ExpGui.element("move_item_down")
elements.move_item_down = Gui.define("move_item_down")
:draw{
type = "sprite-button",
sprite = "utility/speed_down",
tooltip = { "exp-gui_toolbar-settings.move-down" },
}
:style(ExpGui.styles.sprite{
:style(Gui.styles.sprite{
size = toolbar_button_small,
})
:on_click(function(def, player, element)
@@ -611,11 +639,11 @@ elements.move_item_down = ExpGui.element("move_item_down")
end)
--- Set an item as a favourite, making it appear on the toolbar
elements.set_favourite = ExpGui.element("set_favourite")
elements.set_favourite = Gui.define("set_favourite")
:draw(function(def, parent, item_define)
--- @cast item_define ExpElement
local player = ExpGui.get_player(parent)
local top_element = ExpGui.get_top_element(item_define, player)
local player = Gui.get_player(parent)
local top_element = Gui.get_top_element(item_define, player)
return parent.add{
type = "checkbox",
@@ -631,7 +659,7 @@ elements.set_favourite = ExpGui.element("set_favourite")
}
:on_checked_state_changed(function(def, player, element)
local define = ExpElement.get(element.tags.element_name --[[ @as string ]])
local top_element = ExpGui.get_top_element(define, player)
local top_element = Gui.get_top_element(define, player)
local had_visible = Toolbar.has_visible_buttons(player)
top_element.visible = element.state
@@ -649,7 +677,7 @@ elements.set_favourite = ExpGui.element("set_favourite")
end
end)
elements.toolbar_list_item = ExpGui.element("toolbar_list_item")
elements.toolbar_list_item = Gui.define("toolbar_list_item")
:draw(function(def, parent, item_define)
--- @cast item_define ExpElement
local data = {}
@@ -665,8 +693,8 @@ elements.toolbar_list_item = ExpGui.element("toolbar_list_item")
-- Add the button and the icon edit button
local element = item_define(flow)
local player = ExpGui.get_player(parent)
local top_element = ExpGui.get_top_element(item_define, player)
local player = Gui.get_player(parent)
local top_element = Gui.get_top_element(item_define, player)
copy_style(top_element, element)
-- Add the favourite checkbox and label
@@ -683,7 +711,7 @@ elements.toolbar_list_item = ExpGui.element("toolbar_list_item")
end)
--- Main list for all toolbar items
elements.toolbar_list = ExpGui.element("toolbar_list")
elements.toolbar_list = Gui.define("toolbar_list")
:draw(function(def, parent)
local scroll = parent.add{
type = "scroll-pane",
@@ -708,16 +736,16 @@ elements.toolbar_list = ExpGui.element("toolbar_list")
}
-- The main container for the toolbar settings
elements.toolbar_settings = ExpGui.element("toolbar_settings")
elements.toolbar_settings = Gui.define("toolbar_settings")
:draw(function(def, parent)
-- Draw the container
local frame = ExpGui.elements.container(parent, 268)
local frame = Gui.elements.container(parent, 268)
frame.style.maximal_width = 268
frame.style.minimal_width = 268
-- Draw the header
local player = ExpGui.get_player(parent)
local header = ExpGui.elements.header(frame, {
local player = Gui.get_player(parent)
local header = Gui.elements.header(frame, {
caption = { "exp-gui_toolbar-settings.main-caption" },
tooltip = { "exp-gui_toolbar-settings.main-tooltip" },
})
@@ -732,8 +760,8 @@ elements.toolbar_settings = ExpGui.element("toolbar_settings")
return frame.parent
end)
ExpGui.add_left_element(elements.core_button_flow, true)
ExpGui.add_left_element(elements.toolbar_settings, false)
ExpGui.add_top_element(elements.close_toolbar, true)
Gui.add_left_element(elements.core_button_flow, true)
Gui.add_left_element(elements.toolbar_settings, false)
Gui.add_top_element(elements.close_toolbar, true)
return Toolbar