Convert toolbar minus datastore

This commit is contained in:
Cooldude2606
2025-01-28 02:03:06 +00:00
parent a2b4fd119b
commit e34a320d97
30 changed files with 999 additions and 895 deletions

View File

@@ -103,7 +103,7 @@ end
--- @param player LuaPlayer
--- @return LuaGuiElement
function ExpGui.get_top_element(define, player)
return player_elements[player.index].top[define.name]
return assert(player_elements[player.index].top[define.name], "Element is not on the top flow")
end
--- Register a element define to be drawn to the left flow on join
@@ -111,7 +111,7 @@ end
--- @param player LuaPlayer
--- @return LuaGuiElement
function ExpGui.get_left_element(define, player)
return player_elements[player.index].left[define.name]
return assert(player_elements[player.index].left[define.name], "Element is not on the left flow")
end
--- Register a element define to be drawn to the relative flow on join
@@ -119,14 +119,7 @@ end
--- @param player LuaPlayer
--- @return LuaGuiElement
function ExpGui.get_relative_element(define, player)
return player_elements[player.index].relative[define.name]
end
--- Return all top level elements for a player
---@param player LuaPlayer
---@return ExpGui.player_elements
function ExpGui._get_player_elements(player)
return player_elements[player.index]
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
@@ -177,10 +170,10 @@ function ExpGui._ensure_elements(event)
ensure_elements(player, ExpGui.left_elements, elements.left, ExpGui.get_left_flow(player))
ensure_elements(player, ExpGui.relative_elements, elements.relative, player.gui.relative)
-- Check technically not needed, but makes it easier to use this lib without the core defines
if ExpGui.apply_consistency_checks then
ExpGui.apply_consistency_checks(player, true)
end
--- @diagnostic disable-next-line invisible
ExpGui.toolbar._ensure_elements(player)
--- @diagnostic disable-next-line invisible
ExpGui.toolbar._ensure_consistency(player)
end
--- Rerun the visible check for relative elements

View File

@@ -1,306 +0,0 @@
--- @class ExpGui
local ExpGui = require("modules/exp_gui")
local mod_gui = require("mod-gui")
local toolbar_button_default_style = mod_gui.button_style
local toolbar_button_active_style = "menu_button_continue"
local toolbar_button_size = 36
local elements = {} --- @type table<string, ExpElement>
local buttons_with_left_element = {} --- @type table<string, ExpElement>
local left_elements_with_button = {} --- @type table<string, ExpElement>
ExpGui.on_toolbar_button_toggled = script.generate_event_name()
--- @class EventData.on_toolbar_button_toggled: EventData
--- @field element LuaGuiElement
--- @field state boolean
--- Set the style of a toolbar button
--- @param element LuaGuiElement
--- @param state boolean?
--- @return boolean
function ExpGui.set_toolbar_button_style(element, state)
if state == nil then state = element.style.name == toolbar_button_default_style end
element.style = state and toolbar_button_active_style or toolbar_button_default_style
local style = element.style
style.minimal_width = toolbar_button_size
style.height = toolbar_button_size
if element.type == "sprite-button" then
style.padding = -2
else
style.font = "default-semibold"
style.padding = 0
end
return state
end
--- Get the state of a toolbar button
--- @param element LuaGuiElement
--- @return boolean
function ExpGui.get_toolbar_button_state(element)
return element.style.name == toolbar_button_active_style
end
--- Set the visible state of the top flow for a player
--- @param player LuaPlayer
--- @param state boolean?
function ExpGui.set_top_flow_visible(player, state)
local top_flow = assert(ExpGui.get_top_flow(player).parent, player.name)
local show_top_flow = elements.core_button_flow.data[player].show_top_flow --- @type LuaGuiElement
if state == nil then
state = not top_flow.visible
end
top_flow.visible = state
show_top_flow.visible = not state
end
--- Set the visible state of the left element for a player
--- @param define ExpElement
--- @param player LuaPlayer
--- @param state boolean?
function ExpGui.set_left_element_visible(define, player, state)
local element = assert(ExpGui.get_left_element(define, player), "Define is not added to the left flow")
local clear_left_flow = elements.core_button_flow.data[player].clear_left_flow --- @type LuaGuiElement
-- Update the visible state
if state == nil then state = not element.visible end
element.visible = state
-- Check if there is a toolbar button linked to this element
local toolbar_button = left_elements_with_button[define.name]
if toolbar_button then
ExpGui.set_toolbar_button_style(ExpGui.get_top_element(toolbar_button, player), state)
end
-- If visible state is true, then we don't need to check all elements
if state then
clear_left_flow.visible = true
return
end
-- Check if any left elements are visible
--- @diagnostic disable-next-line invisible
local player_elements = ExpGui._get_player_elements(player)
local flow_name = elements.core_button_flow.name
for name, left_element in pairs(player_elements.left) do
if left_element.visible and name ~= flow_name then
clear_left_flow.visible = true
return
end
end
-- There are no visible left elements, so can hide the button
clear_left_flow.visible = false
end
--- @class ExpGui.create_toolbar_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
--- Create a toolbar button
--- @param options ExpGui.create_toolbar_button__param
--- @return ExpElement
function ExpGui.create_toolbar_button(options)
-- Extract the custom options from the element.add options
local name = assert(options.name, "Name is required for the element")
options.type = options.sprite ~= nil and "sprite-button" or "button"
local visible = options.visible
if visible == nil then visible = true end
options.visible = nil
local auto_toggle = options.auto_toggle
options.auto_toggle = nil
local left_element = options.left_element
options.left_element = nil
if options.style == nil then
options.style = toolbar_button_default_style
end
-- Create the new element define
local toolbar_button = ExpGui.element(name)
:draw(options)
:style{
minimal_width = toolbar_button_size,
height = toolbar_button_size,
padding = options.sprite ~= nil and -2 or nil,
}
-- If a left element was given then link the define
if left_element then
left_elements_with_button[left_element.name] = toolbar_button
buttons_with_left_element[toolbar_button.name] = left_element
end
-- Setup auto toggle, required if there is a left element
if auto_toggle or left_element then
toolbar_button:on_click(function(def, event)
local state = ExpGui.set_toolbar_button_style(event.element)
if left_element then
local player = ExpGui.get_player(event)
ExpGui.set_left_element_visible(left_element, player, state)
end
script.raise_event(ExpGui.on_toolbar_button_toggled, {
element = event.element,
state = state,
})
end)
end
-- Add the define to the top flow and return
ExpGui.add_top_element(toolbar_button, visible)
return toolbar_button
end
--- Update the consistency of the core elements and registered elements
--- @param player LuaPlayer
--- @param skip_ensure boolean?
function ExpGui.apply_consistency_checks(player, skip_ensure)
if not skip_ensure then
--- @diagnostic disable-next-line invisible
ExpGui._ensure_elements{ player_index = player.index }
end
-- Get the core buttons for the player
local core_button_data = elements.core_button_flow.data[player]
local hide_top_flow = ExpGui.get_top_element(elements.hide_top_flow, player)
local show_top_flow = core_button_data.show_top_flow --- @type LuaGuiElement
local clear_left_flow = core_button_data.clear_left_flow --- @type LuaGuiElement
-- Check if any top elements are visible, this includes ones not controlled by this module
local has_top_elements = false
local top_flow = ExpGui.get_top_flow(player)
for _, element in pairs(top_flow.children) do
if element.visible and element ~= hide_top_flow then
has_top_elements = true
break
end
end
-- The show button is only visible when the flow isn't visible but does have visible children
show_top_flow.visible = has_top_elements and not top_flow.visible or false
--- @diagnostic disable-next-line invisible
local player_elements = ExpGui._get_player_elements(player)
local left_elements, top_elements = player_elements.left, player_elements.top
--- Update the styles of toolbar buttons with left elements
for name, top_element in pairs(top_elements) do
local left_element = buttons_with_left_element[name]
if left_element then
local element = assert(left_elements[left_element.name], left_element.name)
ExpGui.set_toolbar_button_style(top_element, element.visible)
end
end
-- Check if any left elements are visible
local flow_name = elements.core_button_flow.name
for name, left_element in pairs(left_elements) do
if left_element.visible and name ~= flow_name then
clear_left_flow.visible = true
return
end
end
-- There are no visible left elements, so can hide the button
clear_left_flow.visible = false
end
--- Hides the top flow when clicked
elements.hide_top_flow = ExpGui.element("hide_top_flow")
:draw{
type = "sprite-button",
sprite = "utility/preset",
style = "tool_button",
tooltip = { "exp-gui.hide-top-flow" },
}
:style{
padding = -2,
width = 18,
height = 36,
}
:on_click(function(def, event)
local player = ExpGui.get_player(event)
ExpGui.set_top_flow_visible(player, false)
end)
--- Shows the top flow when clicked
elements.show_top_flow = ExpGui.element("show_top_flow")
:draw{
type = "sprite-button",
sprite = "utility/preset",
style = "tool_button",
tooltip = { "exp-gui.show-top-flow" },
}
:style{
padding = -2,
width = 18,
height = 20,
}
:on_click(function(def, event)
local player = ExpGui.get_player(event)
ExpGui.set_top_flow_visible(player, true)
end)
--- Hides all left elements when clicked
elements.clear_left_flow = ExpGui.element("clear_left_flow")
:draw{
type = "sprite-button",
sprite = "utility/close_black",
style = "tool_button",
tooltip = { "exp-gui.clear-left-flow" },
}
:style{
padding = -3,
width = 18,
height = 20,
}
:on_click(function(def, event)
local player = ExpGui.get_player(event)
event.element.visible = false
--- @diagnostic disable-next-line invisible
local player_elements = ExpGui._get_player_elements(player)
local flow_name = elements.core_button_flow.name
for name, left_element in pairs(player_elements.left) do
if name ~= flow_name then
left_element.visible = false
local toolbar_button = left_elements_with_button[name]
if toolbar_button then
ExpGui.set_toolbar_button_style(ExpGui.get_top_element(toolbar_button, player), false)
end
end
end
end)
--- Contains the two buttons on the left flow
elements.core_button_flow = ExpGui.element("core_button_flow")
:draw(function(def, parent)
local flow = parent.add{
type = "flow",
direction = "vertical",
}
local player = ExpGui.get_player(parent)
def.data[player] = {
show_top_flow = elements.show_top_flow(flow),
clear_left_flow = elements.clear_left_flow(flow),
}
return flow
end)
ExpGui.add_top_element(elements.hide_top_flow, true)
ExpGui.add_left_element(elements.core_button_flow, true)
return elements

View File

@@ -6,7 +6,7 @@ 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 ExpGui_GuiIter.ReturnType
--- @alias ExpGui_GuiIter.ReturnType fun(): LuaPlayer?, LuaGuiElement?
--- @type table<string, table<uint, table<uint, LuaGuiElement>>>
local registered_scopes = {}
@@ -25,6 +25,7 @@ end)
--- @class ExpGui_GuiIter
local GuiIter = {
_scopes = registered_scopes,
}
local function nop() return nil, nil end
@@ -215,10 +216,10 @@ function GuiIter.add_element(scope, element)
registered_scopes[scope] = scope_elements
end
local player_elements = registered_scopes[element.player_index]
local player_elements = scope_elements[element.player_index]
if not player_elements then
player_elements = {}
registered_scopes[element.player_index] = player_elements
scope_elements[element.player_index] = player_elements
end
player_elements[element.index] = element

View File

@@ -1,5 +1,13 @@
[exp-gui]
hide-top-flow=Hide Toolbar.
show-top-flow=Show Toolbar.
clear-left-flow=Hide all open windows.
clear-left-flow=Hide all open windows.
close-toolbar=__CONTROL_LEFT_CLICK__: Toggle Settings\n__CONTROL_RIGHT_CLICK__: Close Toolbar
open-toolbar=__CONTROL_LEFT_CLICK__: Toggle Settings\n__CONTROL_RIGHT_CLICK__: Open Toolbar
[exp-gui_toolbar-settings]
main-caption=Toolbox
main-tooltip=Toolbox Settings\nUse the checkboxes to select favourites
reset=Reset All
toggle=Toggle Favourites
move-up=Move Up
move-down=Move Down

View File

@@ -7,9 +7,9 @@
"control.lua"
],
"require": [
"core_elements.lua",
"elements.lua",
"styles.lua"
"styles.lua",
"toolbar.lua"
],
"dependencies": {
"clusterio": "*",

View File

@@ -98,7 +98,7 @@ function ExpElement.create(name)
data = GuiData.create(element_name),
_events = {},
_debug = {
defined_at = ExpUtil.safe_file_path(2),
defined_at = ExpUtil.get_current_line(2),
},
}
@@ -106,6 +106,13 @@ function ExpElement.create(name)
return setmetatable(instance, ExpElement._metatable)
end
--- Get an element by its name
--- @param name string
--- @return ExpElement
function ExpElement.get(name)
return assert(ExpElement._elements[name], "ExpElement is not defined: " .. tostring(name))
end
--- Create a new instance of this element definition
--- @param parent LuaGuiElement
--- @param ... any
@@ -179,6 +186,15 @@ function ExpElement._prototype:track_all_elements()
return self
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)
return self:draw{
type = "flow"
}
end
--- @alias ExpElement.add_param LuaGuiElement.add_param | table<string, [function, number|string|nil] | function>
--- Set the draw definition

714
exp_gui/module/toolbar.lua Normal file
View File

@@ -0,0 +1,714 @@
--- @class ExpGui
local ExpGui = require("modules/exp_gui")
local ExpElement = require("modules/exp_gui/prototype")
local mod_gui = require("mod-gui")
local toolbar_button_default_style = mod_gui.button_style
local toolbar_button_active_style = "menu_button_continue"
local toolbar_button_size = 36
local toolbar_button_small = 20
--- @class ExpGui.toolbar
local Toolbar = {}
ExpGui.toolbar = Toolbar
local elements = {}
Toolbar.elements = elements
local left_elements_with_button = {} --- @type table<ExpElement, ExpElement>
local buttons_with_left_element = {} --- @type table<string, ExpElement>
--- 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)
if state == nil then state = not top_flow.visible end
top_flow.visible = state
-- Update the open toolbar button
for _, open_toolbar in elements.open_toolbar:tracked_elements(player) do
open_toolbar.visible = not state
end
-- Update the toggle toolbar button
for _, toggle_toolbar in elements.toggle_toolbar:tracked_elements(player) do
toggle_toolbar.toggled = state
end
return state
end
--- Get the visible state of the toolbar
--- @param player LuaPlayer
--- @return boolean
function Toolbar.get_visible_state(player)
local top_flow = assert(ExpGui.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
--- @param define ExpElement
--- @param player LuaPlayer
--- @param state boolean? toggles if nil
--- @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")
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
-- Make the extra required adjustments
local style = element.style
style.minimal_width = original_width
style.maximal_height = original_height
if element.type == "sprite-button" then
style.padding = -2
else
style.font = "default-semibold"
style.padding = 0
end
end
return state
end
--- Get the toggle state of a toolbar button
--- @param define ExpElement
--- @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")
return element.style.name == toolbar_button_active_style
end
--- Set the visible state of a left element
--- @param define ExpElement
--- @param player LuaPlayer
--- @param state boolean? toggles if nil
--- @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")
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)
end
-- This check is O(n^2) when setting all left elements to hidden, so internals can it
if _skip_consistency then return state end
-- Update the clear left flow button, visible when at least one element is visible
local has_visible = Toolbar.has_visible_left_elements(player)
for _, clear_left_flow in elements.clear_left_flow:tracked_elements(player) do
clear_left_flow.visible = state or has_visible
end
return state
end
--- Get the toggle state of a left element
--- @param define ExpElement
--- @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")
return element.visible
end
--- Check if there are any visible toolbar buttons
--- @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)
for _, element in pairs(top_flow.children) do
if element.visible and element ~= settings_button then
return true
end
end
return false
end
--- Check if there are any visible left elements
--- @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)
for _, element in pairs(left_flow.children) do
if element.visible and element ~= core_button_flow then
return true
end
end
return false
end
--- @class ExpGui.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
--- Create a toolbar button
--- @param options ExpGui.toolbar.create_button__param
--- @return ExpElement
function Toolbar.create_button(options)
-- Extract the custom options from the element.add options
local name = assert(options.name, "Name is required for the element")
options.type = options.sprite ~= nil and "sprite-button" or "button"
local visible = options.visible
if visible == nil then visible = true end
options.visible = nil
local auto_toggle = options.auto_toggle
options.auto_toggle = nil
local left_element = options.left_element
options.left_element = nil
if options.style == nil then
options.style = toolbar_button_default_style
end
-- Create the new element define
local toolbar_button = ExpGui.element(name)
:track_all_elements()
:draw(options)
:style{
minimal_width = toolbar_button_size,
height = toolbar_button_size,
padding = options.sprite ~= nil and -2 or nil,
}
-- If a left element was given then link the define
if left_element then
left_elements_with_button[left_element] = toolbar_button
buttons_with_left_element[toolbar_button.name] = left_element
end
-- Setup auto toggle, required if there is a left element
if auto_toggle or left_element then
toolbar_button:on_click(function(def, event)
local player = ExpGui.get_player(event)
if left_element then
Toolbar.set_left_element_visible_state(left_element, player)
else
Toolbar.set_button_toggled_state(toolbar_button, player)
end
end)
end
-- Add the define to the top flow and return
ExpGui.add_top_element(toolbar_button, visible)
return toolbar_button
end
--- Ensure all the toolbar buttons are in a consistent state
--- @param player LuaPlayer
function Toolbar._ensure_consistency(player)
-- Update clear_left_flow
local has_visible = Toolbar.has_visible_left_elements(player)
for _, clear_left_flow in elements.clear_left_flow:tracked_elements(player) do
clear_left_flow.visible = has_visible
end
-- Update open_toolbar
local top_flow = assert(ExpGui.get_top_flow(player).parent)
for _, open_toolbar in elements.open_toolbar:tracked_elements(player) do
open_toolbar.visible = not top_flow.visible
end
-- Update toggle_toolbar
local has_buttons = Toolbar.has_visible_buttons(player)
for _, toggle_toolbar in elements.toggle_toolbar:tracked_elements(player) do
toggle_toolbar.enabled = has_buttons
end
-- Update the state of buttons with left elements
for left_element, button in pairs(left_elements_with_button) do
local element = ExpGui.get_left_element(left_element, player)
Toolbar.set_button_toggled_state(button, player, element.visible)
end
end
--- Toggles the toolbar settings, RMB will instead hide the toolbar
elements.close_toolbar = ExpGui.element("close_toolbar")
:draw{
type = "sprite-button",
sprite = "utility/preset",
style = "tool_button",
tooltip = { "exp-gui.close-toolbar" },
}
:style{
padding = -2,
width = 18,
height = 36,
}
:on_click(function(def, event, element)
local player = ExpGui.get_player(event)
if event.button == defines.mouse_button_type.left then
Toolbar.set_left_element_visible_state(elements.toolbar_settings, player)
else
Toolbar.set_visible_state(player, false)
end
end)
--- Shows the toolbar, if no buttons are visible then it shows the settings instead
elements.open_toolbar = ExpGui.element("open_toolbar")
:track_all_elements()
:draw{
type = "sprite-button",
sprite = "utility/preset",
style = "tool_button",
tooltip = { "exp-gui.open-toolbar" },
}
:style{
padding = -2,
width = 18,
height = 20,
}
:on_click(function(def, event, element)
local player = ExpGui.get_player(event)
if event.button == defines.mouse_button_type.left then
Toolbar.set_left_element_visible_state(elements.toolbar_settings, player)
else
Toolbar.set_visible_state(player, true)
end
end)
--- Hides all left elements when clicked
elements.clear_left_flow = ExpGui.element("clear_left_flow")
:track_all_elements()
:draw{
type = "sprite-button",
sprite = "utility/close_black",
style = "tool_button",
tooltip = { "exp-gui.clear-left-flow" },
}
:style{
padding = -3,
width = 18,
height = 20,
}
:on_click(function(def, event, element)
element.visible = false
local player = ExpGui.get_player(event)
for define in pairs(ExpGui.left_elements) do
if define ~= elements.core_button_flow then
Toolbar.set_left_element_visible_state(define, player, false, true)
end
end
end)
--- Contains the two buttons on the left flow
elements.core_button_flow = ExpGui.element("core_button_flow")
:draw(function(def, parent)
local flow = parent.add{
type = "flow",
direction = "vertical",
}
elements.open_toolbar(flow)
elements.clear_left_flow(flow)
return flow
end)
--[[
Below here is the toolbar settings GUI and its associated functions
]]
--- Set the style of the fake toolbar element
--- @param src LuaGuiElement
--- @param dst LuaGuiElement
local function copy_style(src, dst)
dst.style = src.style.name
dst.style.height = toolbar_button_small
dst.style.width = toolbar_button_small
dst.style.padding = -2
end
--- Reorder the buttons relative to each other, this will update the datastore
--- @param player LuaPlayer
--- @param item LuaGuiElement
--- @param offset number
local function move_toolbar_button(player, item, offset)
local old_index = item.get_index_in_parent()
local new_index = old_index + offset
-- Swap the position in the list
local list = assert(item.parent)
local other_item = list.children[new_index]
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)
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 left_index = element.get_index_in_parent()
local other_index = other_element.get_index_in_parent()
element.parent.swap_children(left_index, other_index)
end
-- If we are moving in/out of first/last place we need to update the move buttons
local last_index = #list.children
local item_data = elements.toolbar_list_item.data[item]
local other_item_data = elements.toolbar_list_item.data[other_item]
if old_index == 1 then -- Moving out of index 1
item_data.move_item_up.enabled = true
other_item_data.move_item_up.enabled = false
elseif new_index == 1 then -- Moving into index 1
item_data.move_item_up.enabled = false
other_item_data.move_item_up.enabled = true
elseif old_index == last_index then -- Moving out of the last index
item_data.move_item_down.enabled = true
other_item_data.move_item_down.enabled = false
elseif new_index == last_index then -- Moving into the last index
item_data.move_item_down.enabled = false
other_item_data.move_item_down.enabled = true
end
--[[ Update the datastore state
ToolbarState:update(player, function(_, order)
local tmp = order[old_index]
order[old_index] = order[new_index]
order[new_index] = tmp
end)]]
end
--- @alias ExpGui.ToolbarOrder { name: string, favourite: boolean }[]
--- Reorder the toolbar buttons
--- @param player LuaPlayer
--- @param order ExpGui.ToolbarOrder
function Toolbar.set_order(player, order)
local list = elements.toolbar_settings.data[player] --[[ @as LuaGuiElement ]]
local top_flow = ExpGui.get_top_flow(player)
-- Reorder the buttons
local last_index = #order
for index, item_state in ipairs(order) do
-- Switch item order
local item = assert(list[item_state.name], "Missing toolbox item for " .. tostring(item_state.name))
list.swap_children(index, item.get_index_in_parent())
-- Update the item visibility
local element_define = ExpElement.get(item_state.name)
local allowed = ExpGui.top_elements[element_define]
local toolbar_button = ExpGui.get_top_element(element_define, player)
if type(allowed) == "function" then allowed = allowed(player, toolbar_button) end
top_flow.swap_children(index + 1, toolbar_button.get_index_in_parent())
toolbar_button.visible = allowed and item_state.favourite or false
item.visible = toolbar_button.visible
-- Update the children buttons
local data = elements.toolbar_list_item.data[item]
data.set_favourite.state = item_state.favourite
data.move_item_up.enabled = index ~= 1
data.move_item_down.enabled = index ~= last_index
end
-- Update the state of the toggle button
local has_buttons = Toolbar.has_visible_buttons(player)
for _, toggle_toolbar in elements.toggle_toolbar:tracked_elements(player) do
toggle_toolbar.enabled = has_buttons
end
end
--- @class (exact) ExpGui.ToolbarState
--- @field order ExpGui.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
function Toolbar.set_state(player, state)
Toolbar.set_order(player, state.order)
Toolbar.set_visible_state(player, state.visible)
local done = {}
-- Make all open elements visible
for _, name in pairs(state.open) do
local left_element = ExpElement.get(name)
Toolbar.set_left_element_visible_state(left_element, player, true, true)
done[left_element] = true
end
-- Make all other elements hidden
for left_element in pairs(ExpGui.left_elements) do
if not done[left_element] then
Toolbar.set_left_element_visible_state(left_element, player, false, true)
end
end
-- Update clear_left_flow (because we skip above)
local has_visible = Toolbar.has_visible_left_elements(player)
for _, clear_left_flow in elements.clear_left_flow:tracked_elements(player) do
clear_left_flow.visible = has_visible
end
end
--- Ensure the toolbar settings gui has all its elements
--- @param player LuaPlayer
function Toolbar._ensure_elements(player)
-- Add any missing items to the gui
local toolbar_list = elements.toolbar_settings.data[player] --[[ @as LuaGuiElement ]]
for define in pairs(ExpGui.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
end
end
-- Set the state of the move buttons for the first and last element
local children = toolbar_list.children
if #children > 0 then
elements.toolbar_list_item.data[children[1]].move_item_up.enabled = false
elements.toolbar_list_item.data[children[#children]].move_item_down.enabled = false
end
end
do
local default_order --- @type ExpGui.ToolbarOrder
--- Gets the default order for the toolbar
--- @return ExpGui.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
if define ~= elements.close_toolbar then
default_order[index] = { name = define.name, favourite = true }
index = index + 1
end
end
return default_order
end
end
--- Toggle the visibility of the toolbar, does not care if buttons are visible
elements.toggle_toolbar = ExpGui.element("toggle_toolbar")
:track_all_elements()
:draw{
type = "sprite-button",
sprite = "utility/bookmark",
tooltip = { "exp-gui_toolbar-settings.toggle" },
style = "tool_button",
auto_toggle = true,
}
:style(ExpGui.styles.sprite{
size = 22,
})
:on_click(function(def, event, element)
local player = ExpGui.get_player(event)
Toolbar.set_visible_state(player, element.toggled)
end)
--- Reset the toolbar to its default state
elements.reset_toolbar = ExpGui.element("reset_toolbar")
:draw{
type = "sprite-button",
sprite = "utility/reset",
style = "shortcut_bar_button_red",
tooltip = { "exp-gui_toolbar-settings.reset" },
}
:style(ExpGui.styles.sprite{
size = 22,
padding = -1,
})
:on_click(function(def, event, element)
local player = ExpGui.get_player(event)
--ToolbarState:set(player, nil)
Toolbar.set_order(player, Toolbar.get_default_order())
end)
--- Move an item up/left on the toolbar
elements.move_item_up = ExpGui.element("move_item_up")
:draw{
type = "sprite-button",
sprite = "utility/speed_up",
tooltip = { "exp-gui_toolbar-settings.move-up" },
}
:style(ExpGui.styles.sprite{
size = toolbar_button_small,
})
:on_click(function(def, event, element)
local player = ExpGui.get_player(event)
local item = assert(element.parent.parent)
move_toolbar_button(player, item, -1)
end)
--- Move an item down/right on the toolbar
elements.move_item_down = ExpGui.element("move_item_down")
:draw{
type = "sprite-button",
sprite = "utility/speed_down",
tooltip = { "exp-gui_toolbar-settings.move-down" },
}
:style(ExpGui.styles.sprite{
size = toolbar_button_small,
})
:on_click(function(def, event, element)
local player = ExpGui.get_player(event)
local item = assert(element.parent.parent)
move_toolbar_button(player, item, 1)
end)
--- Set an item as a favourite, making it appear on the toolbar
elements.set_favourite = ExpGui.element("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)
return parent.add{
type = "checkbox",
caption = top_element.tooltip or top_element.caption or nil,
state = top_element.visible or false,
tags = {
element_name = item_define.name,
},
}
end)
:style{
width = 180,
}
:on_checked_state_changed(function(def, event, element)
local player = ExpGui.get_player(event)
local define = ExpElement.get(element.tags.element_name --[[ @as string ]])
local top_element = ExpGui.get_top_element(define, player)
local had_visible = Toolbar.has_visible_buttons(player)
top_element.visible = element.state
-- Check if we are on the edge case between 0 and 1 visible elements
if element.state and not had_visible then
Toolbar.set_visible_state(player, true)
for _, toggle_toolbar in elements.toggle_toolbar:tracked_elements(player) do
toggle_toolbar.enabled = true
end
elseif not element.state and not Toolbar.has_visible_buttons(player) then
Toolbar.set_visible_state(player, false)
for _, toggle_toolbar in elements.toggle_toolbar:tracked_elements(player) do
toggle_toolbar.enabled = false
end
end
--[[ Update the datastore state
ToolbarState:update(player, function(_, order)
local index = element.parent.get_index_in_parent()
order[index].favourite = element.state
end)]]
end)
elements.toolbar_list_item = ExpGui.element("toolbar_list_item")
:draw(function(def, parent, item_define)
--- @cast item_define ExpElement
local data = {}
-- Add the flow for the item
local flow = parent.add{
name = item_define.name,
type = "frame",
style = "shortcut_selection_row",
}
flow.style.horizontally_stretchable = true
flow.style.vertical_align = "center"
-- 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)
copy_style(top_element, element)
-- Add the favourite checkbox and label
data.set_favourite = elements.set_favourite(flow, item_define)
-- Add the buttons used to move the flow up and down
local move_flow = flow.add{ type = "flow", name = "move" }
move_flow.style.horizontal_spacing = 0
data.move_item_up = elements.move_item_up(move_flow)
data.move_item_down = elements.move_item_down(move_flow)
def.data[flow] = data
return flow
end)
--- Main list for all toolbar items
elements.toolbar_list = ExpGui.element("toolbar_list")
:draw(function(def, parent)
local scroll = parent.add{
type = "scroll-pane",
direction = "vertical",
horizontal_scroll_policy = "never",
vertical_scroll_policy = "auto",
style = "scroll_pane_under_subheader",
}
scroll.style.horizontally_stretchable = true
scroll.style.maximal_height = 224
scroll.style.padding = 0
-- This is required because vertical_spacing can't be set on a scroll pane
return scroll.add{
type = "flow",
direction = "vertical",
}
end)
:style{
horizontally_stretchable = true,
vertical_spacing = 0,
}
-- The main container for the toolbar settings
elements.toolbar_settings = ExpGui.element("toolbar_settings")
:draw(function(def, parent)
-- Draw the container
local frame = ExpGui.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, {
caption = { "exp-gui_toolbar-settings.main-caption" },
tooltip = { "exp-gui_toolbar-settings.main-tooltip" },
})
-- Draw the toolbar control buttons
local toggle_element = elements.toggle_toolbar(header)
toggle_element.toggled = Toolbar.get_visible_state(player)
elements.reset_toolbar(header)
def.data[player] = elements.toolbar_list(frame)
Toolbar._ensure_elements(player)
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)
return Toolbar